11

I have two Git repos, foo/master and bar/master:

In foo:

code_root
->dirA
  ->dirB
    -> *some files*

In bar:

code_root
  -> *same files as above*

Now someone has made changes to *some files*... how do I get those changes merged into *same files as above*?

When I say "merged" I mean I need the history (commit messages, log hashes etc.) of the deltas to come over as well.

RavenHursT
  • 2,336
  • 1
  • 25
  • 46
  • Why are the directory structures of the two repos different? Are the repos from the same project, or are they two completely unrelated projects? Are you trying to update a 3rd-party library in your repo? –  Mar 03 '14 at 22:28
  • project was erroneously created as a subtree of the first repo. Time to make it into it's own project/repo. So no need for all the tree cruft of the first repo. – RavenHursT Mar 03 '14 at 23:12

3 Answers3

4

You can split the changes out to the subtree level, and then merge with your other branch:

# from your foo project
git subtree split --prefix=dirA/dirB/ --branch=temp-branch [--onto=<onto-sub-note1>] [<commit-sub-note2>]

onto sub-note 1: It seems like since bar project exists at all, you must have copied it at some point in time, and started this as the new library, in which case, if you want all changes made after that, you'd specify this bar commit id when the changes were brought in.

commit sub-note 2: You'd then want to specify the commit id that was used when you copied the sub-project in the first place, so that you only get the changes since then to merge into the copy of bar you already have (this will maintain the history of what you missed). Use syntax like this to include the commit id itself up to the latest: 0abc210^..

You can also use --rejoin to make a commit back into your foo project, which will make it easier to push changes later if you would like to continue development on bar from withing your foo project. The commit back into foo is kind of pointless other than to help the subtree command base it's split changes more easily in the future.

After running the split command, you'll be in a branch of foo, which only has those files. From there you can do a normal merge with your bar project or start a new project and merge bar into that (since it probably doesn't have a proper history). You may want to rebase to the diverge point or something before trying to do that merge though.

Edit: Also here's the reference for git subtree commands

johnb003
  • 1,821
  • 16
  • 30
  • But don't both projects have to have the same tree structures (more or less) in order for this to work? I just want /some/path/to/project in "foo", to live in /project in "bar". This won't work for that will it? wouldn't I still need /some/path/to/project in "bar"? – RavenHursT Mar 06 '14 at 22:42
  • 1
    If you're in some /project, and you want to split ./subFolderA/subFolderB/*Stuff*, you use `subtree split --prefix=subFolderA/subFolderB`, and you'll get a new branch with: ./*Stuff*. All of the rest of the project files in that branch will not be in that branch. So, this will bring the directory structures into alignment, and from there you can merge. – johnb003 Mar 06 '14 at 22:50
  • Ok.. so I tried this: https://gist.github.com/RavenHursT/9421802 What am I doing wrong here? – RavenHursT Mar 07 '14 at 22:49
  • I think you need to fetch first, `git fetch /path/to/projectA split-branch`, and don't do `split-branch master`. Then do the merge with the same path? Or better yet just try `git pull /path/to/projectA split-branch` – johnb003 Mar 08 '14 at 00:29
  • Why did you remove the accepted answer? I have tested this and know it works. Are you still having problems? – johnb003 Mar 08 '14 at 18:19
  • This was most of the correct answer. Turns out, in order to push the repo to github after merging in the split-branch... -I had to go back and create the "new" repo as a BARE repo FIRST... -Split the subtree branch.. -Then add the "empty" github repo as the origin remote -At which point, I was finally able to push the local repo to github. Not exactly sure why it all worked this way, but it worked. Sorry for the long delay in response. I'll check this as accepted answer again.. – RavenHursT Mar 10 '14 at 16:28
  • I'll gladly add whatever you think is missing. Did you already have _bar_ on GitHub from the copy you had already made and just wanted to merge in the changes from the subtree, or did you want to merge the two and then add the result to github as a new project? – johnb003 Mar 10 '14 at 19:16
  • Sorry, yeah.. I should've been more clear.. basically, bar is a NEW repo based on dirB in foo. I couldn't find a simple way to create a new github repo from the command line, so yes, bar did infact exist on the remote already, but it was just empty. So I had to merge in the split from foo, to "local-bar", init'ed as "bare".. THEN do a git init on local-bar so the actual files were there.. THEN I was able to set the github bar as the remote, and finally push "lobal-bar" to "github-bar".. Maybe that makes more sense? If so, I'll update the question and hopefully make it clearer. – RavenHursT Mar 10 '14 at 23:22
1

So, you are saying the files are essentially the same in both in foo & bar, just that the ones in foo are newer than those in bar:

If yes, then you can just take a diff:

diff -b /path_to/foo/dirA/dirB/ /path_to/bar/ > diff.patch

and then apply the patch onto bar:

cd bar

patch -p1 < diff.patch

Update: As per OPs updated, he's looking to maintain commit history, above won't work in that case.

brokenfoot
  • 11,083
  • 10
  • 59
  • 80
  • patch would work.. but change history (commit messages etc.) would be lost. Thanks for the answer, I'll update my question to make clear that I need history as well. – RavenHursT Mar 03 '14 at 23:59
1

Check out the git book and git magic. Depending on the exact changes, perhaps a round of history rewriting (git filter-branch and friends; or brute-force application of git rebase --interactive, straightening out each commit) can restore sanity to the mangled clone, and then allow a clean merge.

Other alternative is to export each commit as a patch (essentially run git format-patch), recover the commit data from there and reverse engineer the patches to apply. You could even use the oportunity to rewrite a cleaner/simpler history when doing this.

Clearly the viability depends on the extent of the divergence, and how many "wrongly applied" commits are at stake.

vonbrand
  • 11,412
  • 8
  • 32
  • 52