threads / discuss / 30238

Merging changes from branches where file paths have changed

Subject: Merging changes from branches where file paths have changed

## tl;dr

4 messages between Apr 14, 2012 and Apr 14, 2012.

replies: 3people: 2as markdown or json

Thomas Adam· Apr 14, 2012, 13:15 UTC · lore
Hi all,

I've got two git repositories -- for the purposes of illustration, I'll call them RepoA, and RepoB. RepoA is a large repository originally from a CVS conversion. Repo B is a repository which has been formed from git-filter-branch (should it matter) from RepoA.

In doing the conversion -- RepoB contains the same history as in RepoA, except that RepoB is now a limited subset in terms of its files that RepoA has (repoB contains commits out of repoA for a specific subdirectory). However, the problem I'm wanting to solve now, which is causing me to scratch my head and go "hmm", is how I go about merging commits from RepoA and have them in RepoB -- where the commits from repoA are in the same *directory* as the one which formed RepoB.

In RepoA, for the directory (and all its sub-directories) which is contained in RepoB, I've got a list of commits -- roughly like the following:

% cd ./distrib/build/sources/coretree/tree % git log --oneline master..my/topic/branch -- .

Now in RepoB, I've got all of the directories and files from ./distrib/build/sources/coretree/tree/ in the root, having obtained this from git-filter-branch from RepoA. Despite the change of file locations, the history of the files between the two repositories are the same.

The commits though coming from "git log" in RepoA touch files in distrib/build/sources/coretree/tree, yet the files to which I want the changes for in RepoB are not in "distrib/build/sources/coretree/tree" -- there all in "./" at the top-level directory for RepoB. The files in both once shared the same history -- it's just that now the locations of those files in RepoB which have changed.

I want to cherry-pick the commits I obtained from the "git log" command in RepoA, and have them contained in RepoB. So I thought something like this would work:

[ In RepoB.... ]: % git remote add repoA file:///path/to/repoA/ % git fetch repoA % git checkout specific/branch % git cherry-pick $SOME_COMMIT_FROM_REPOA

However, this won't work, and doesn't. Not only do the two repositories not have any common ancestry, but even if they did, the file paths from RepoA and RepoB do not match for the commit(s) I would want to cherry-pick. As far as I can tell, were I able to convince Git the file paths did work, then I could cherry-pick them.

Can anyone suggest a good way round this? I hope I've managed to explain this adequately; just ask if not.

Thanks in Advance.
-- Thomas Adam
Johannes Sixt· Apr 14, 2012, 19:21 UTC · re: Thomas Adam · lore

Re: Merging changes from branches where file paths have changed

Am 14.04.2012 15:15, schrieb Thomas Adam:
Show 16 quoted lines
> The commits though coming from "git log" in RepoA touch files in
> distrib/build/sources/coretree/tree, yet the files to which I want the
> changes for in RepoB are not in "distrib/build/sources/coretree/tree"
> -- there all in "./" at the top-level directory for RepoB.  The files
> in both once shared the same history -- it's just that now the
> locations of those files in RepoB which have changed.
> 
> I want to cherry-pick the commits I obtained from the "git log"
> command in RepoA, and have them contained in RepoB.  So I thought
> something like this would work:
> 
> [ In RepoB.... ]:
> % git remote add repoA file:///path/to/repoA/
> % git fetch repoA
> % git checkout specific/branch
> % git cherry-pick $SOME_COMMIT_FROM_REPOA
You have these histories
  A--o--B--C--o--o--D--o--E   <- RepoA
  A'----B'-C'                 <- RepoB
and you want to cherry-pick D and E to RepoB. Right?
I suggest that you install a graft in RepoB, then cherry-pick D and E:
  echo $(git rev-parse C' C) >> .git/info/grafts
  git cherry-pick D
  git cherry-pick E

Then it will look as if everything outside the directory was removed, and everything else was moved from the directory to the root with 100% similarity, which makes the merge process easy. (This works only if there are no files with the same names in C's and C''s root; a Makefile in each would pose a problem.)

(Remove the graft when you're done.)
-- Hannes
Thomas Adam· Apr 14, 2012, 19:36 UTC · re: Johannes Sixt · lore

Re: Merging changes from branches where file paths have changed

Hello,
On 14 April 2012 20:21, Johannes Sixt <j6t@kdbg.org> wrote:
Show 8 quoted lines
> You have these histories
>
>
>  A--o--B--C--o--o--D--o--E   <- RepoA
>
>  A'----B'-C'                 <- RepoB
>
> and you want to cherry-pick D and E to RepoB. Right?
Yes, that's about right.
> I suggest that you install a graft in RepoB, then cherry-pick D and E:
>
>  echo $(git rev-parse C' C) >> .git/info/grafts

Can you elaborate on this a little more please? Or point me towards some documentation. Note that due to the way git-filter-branch works, although the history between RepoA and RepoB are common, the commit IDs between the two are completely different; presumably git-filter-branch uses patch-id at some level to reform commits. Nevertheless, if I'm understanding superficially what this graft is supposed to do, I do not see how getting a list of commits from C' to C is going to help in repoB when neither RepoA or RepoB have any ancestry in common?

Kindly,
-- Thomas Adam
Johannes Sixt· Apr 14, 2012, 19:49 UTC · re: Thomas Adam · lore

Re: Merging changes from branches where file paths have changed

Am 14.04.2012 21:36, schrieb Thomas Adam:
Show 27 quoted lines
> Hello,
> 
> On 14 April 2012 20:21, Johannes Sixt <j6t@kdbg.org> wrote:
>> You have these histories
>>
>>
>>  A--o--B--C--o--o--D--o--E   <- RepoA
>>
>>  A'----B'-C'                 <- RepoB
>>
>> and you want to cherry-pick D and E to RepoB. Right?
> 
> Yes, that's about right.
> 
>> I suggest that you install a graft in RepoB, then cherry-pick D and E:
>>
>>  echo $(git rev-parse C' C) >> .git/info/grafts
> 
> Can you elaborate on this a little more please?  Or point me towards
> some documentation.  Note that due to the way git-filter-branch works,
> although the history between RepoA and RepoB are common, the commit
> IDs between the two are completely different; presumably
> git-filter-branch uses patch-id at some level to reform commits.
> Nevertheless, if I'm understanding superficially what this graft is
> supposed to do, I do not see how getting a list of commits from C' to
> C is going to help in repoB when neither RepoA or RepoB have any
> ancestry in common?

There is no "list of commits from C' to C". Look carefully: it's rev-parse, not rev-list. The command above only states that the (only) parent of C' is C, i.e., it fakes this history:

  A--o--B--C--o--o--D--o--E   <- RepoA
            \
             C'               <- RepoB
and then the two repositories have history in common.
And BTW:
1. C' must be the top-most commit that was derived from RepoA.
2. If D or E add new paths, they will not be moved to the root by the
cherry-pick. Be sure to check the result.
3. If you have additional commits in RepoB that were not derived from
RepoA, it may not work out as smoothly, if the changes are so large that
rename detection does not kick in anymore.
-- Hannes

← back to recent threads