bmargulies bmargulies - 1 year ago 108
Git Question

git read-tree versus subsequent merge

I followed the procedure in this blog post to copy the contents of one git repo into another. (It involves creating a remote, fetching, and then using read-tree with a pathname map.) The problem comes in when I want to merge in subsequent changes. The merge does not know about the pathname mapping. Is there some way to tell it?

Here's the procedure I followed to add the contents of the second repo to the bigger repo.

git remote add projB <github-remote-location>
git merge -s ours --no-commit projB/master
git read-tree --prefix=subdirB/ -u projB/master
git ci -m "merging projB into subdirB"

And then later, the fun results from using git add to add something new in the old 'projB' repo, and then, in the merged repo, running:

git fetch projB
git pull projB SomeTag

Which results have in the new files appearing in the location that isn't relocated.

Answer Source

The short answer is:

git pull -s subtree projB SomeTag

git doesn't track moves or renames by default. It happens to appear to catch some by similarity, but deep down in the repository it doesn't keep that information. Also, it has no way to guess that sort of metadata with new files.

The commands you typed don't do anything magical about those metadata. All they end up doing, in the end, is create a new HEAD commit that happens to:

  • have projB/master as an additional ancestor (line 2). The base ancestor is your current HEAD. In the usual case of a linear baseline, all commits have a single ancestor, except for the initial one that doesn't have any. A commit with more than one ancestor is known as a "merge" commit.

  • contain all that projB/master contains, shifted down to subdirectory subdirB (line 3)

But after the merge commit is created (line 4), nothing more is remembered. Especially not which files come from where in the resulting tree.

Apparently that use case showed up often enough that someone wrote a subtree merge strategy, that happens to seek if the ref being merged wouldn't fit better somewhere else than at the root. But that behavior has to be explicitly requested.

The blog post you link to happens to mention it, but it's hidden at the very bottom; the author failed to remind it in his TL;DR. You can find a more condensed version of the story in the git docs, and it's a section in the Pro Git book as well.