threads / discuss / 11488

how to use git merge -s subtree?

Subject: how to use git merge -s subtree?

## tl;dr

12 messages between Jan 5, 2008 and Jan 8, 2008.

replies: 11people: 4as markdown or json

Miklos Vajna· Jan 5, 2008, 23:00 UTC · lore
hi,

i recently noticed that the subtree merge strategy is missing from merge-strategies.txt an i tried to first figure out how it works. i got it to work, but i'm not 100% sure about i'm using it the way i'm supposed to.

here is what i do:
1) git remote add B /path/to/B.git
2) git fetch
3) mkdir B
4) touch B/.gitignore
5) git add B/.gitignore
6) git commit -m "add empty B directory"
7) git merge -s subtree B/master

and yes, it works pretty well, but is this the right way? or is it possible to somehow avoid steps 3..6?

thanks,
- VMiklos
Sean· Jan 6, 2008, 01:17 UTC · re: Miklos Vajna · lore

Re: how to use git merge -s subtree?

On Sun, 6 Jan 2008 00:00:04 +0100 Miklos Vajna <vmiklos@frugalware.org> wrote:

Show 18 quoted lines
> i recently noticed that the subtree merge strategy is missing from
> merge-strategies.txt an i tried to first figure out how it works. i got
> it to work, but i'm not 100% sure about i'm using it the way i'm
> supposed to.
> 
> here is what i do:
> 
> 1) git remote add B /path/to/B.git
> 2) git fetch
> 3) mkdir B
> 4) touch B/.gitignore
> 5) git add B/.gitignore
> 6) git commit -m "add empty B directory"
> 7) git merge -s subtree B/master
> 
> and yes, it works pretty well, but is this the right way? or is it
> possible to somehow avoid steps 3..6?
> 
Hi Miklos,
Here's another way that is perhaps a little cleaner:

$ git remote add -f B /path/to/B $ git merge -s ours --no-commit B/master $ git read-tree --prefix=sub/ -u B/master $ git commit -m "subtree merged B"

The first line creates and fetches the remote. The second line initiates a merge, but stops before committing it. The third line reads B/master into the subdirectory "sub", at which point all that remains is committing the completed merge.

HTH,
Sean
 
David Soria Parra· Jan 6, 2008, 01:22 UTC · re: Sean · lore

Re: how to use git merge -s subtree?

Show 9 quoted lines
> Hi Miklos,
> 
> Here's another way that is perhaps a little cleaner:
> 
> $ git remote add -f B /path/to/B
> $ git merge -s ours --no-commit B/master
> $ git read-tree --prefix=sub/ -u B/master 
> $ git commit -m "subtree merged B"
> 
hi sean,
that works perfectly but it doesn't preserve the history, does it?
                    dsp
Sean· Jan 6, 2008, 02:13 UTC · re: David Soria Parra · lore

Re: how to use git merge -s subtree?

On Sun, 06 Jan 2008 02:22:47 +0100 David Soria Parra <sn_@gmx.net> wrote:

Show 8 quoted lines
> > Here's another way that is perhaps a little cleaner:
> > 
> > $ git remote add -f B /path/to/B
> > $ git merge -s ours --no-commit B/master
> > $ git read-tree --prefix=sub/ -u B/master 
> > $ git commit -m "subtree merged B"
> 
> that works perfectly but it doesn't preserve the history, does it?
David,

Yes, the reason to start with the "--no-commit" merge is so that the history is properly connected once you do the final commit step. However, I should have noted in my original message that none of the steps actually use the subtree merge. Instead they simply prepare a repository such that future merging can be done with:

   $ git merge -s subtree B/master.

Cheers, Sean

David Soria Parra· Jan 6, 2008, 02:28 UTC · re: Sean · lore

Re: how to use git merge -s subtree?

Sean wrote:
Show 13 quoted lines
>
> David,
>
> Yes, the reason to start with the "--no-commit" merge is so that the history
> is properly connected once you do the final commit step.  However, I should
> have noted in my original message that none of the steps actually use the
> subtree merge.  Instead they simply prepare a repository such that
> future merging can be done with:
>
>    $ git merge -s subtree B/master.
>
> Cheers,
> Sean

Well yes the history is preserved, but it's not connected to the subdirectory. So you cannot do git-log B/foo.c as git doesnot know where to search it as it thinks it is in /foo.c not in B/foo.c

Cheers
Junio C Hamano· Jan 6, 2008, 02:42 UTC · re: David Soria Parra · lore

Re: how to use git merge -s subtree?

David Soria Parra <sn_@gmx.net> writes:
> Well yes the history is preserved, but it's not connected to the
> subdirectory. So you cannot do git-log B/foo.c as git doesnot know where
> to search it as it thinks
> it is in /foo.c not in B/foo.c

The thing is, what you are talking is not about the subtree merge strategy, but the fundamental philosophy of git. Asking for "the history of file B/foo.c" does not make any sense, as git never tracks history of individual files.

I'll point you to one of the most important messages in the whole archive of the git mailing list.

 http://thread.gmane.org/gmane.comp.version-control.git/27/focus=217
Sean· Jan 6, 2008, 08:06 UTC · re: Junio C Hamano · lore

Re: how to use git merge -s subtree?

On Sat, 05 Jan 2008 18:42:37 -0800 Junio C Hamano <gitster@pobox.com> wrote:

Show 11 quoted lines
> David Soria Parra <sn_@gmx.net> writes:
> 
> > Well yes the history is preserved, but it's not connected to the
> > subdirectory. So you cannot do git-log B/foo.c as git doesnot know where
> > to search it as it thinks
> > it is in /foo.c not in B/foo.c
> 
> The thing is, what you are talking is not about the subtree
> merge strategy, but the fundamental philosophy of git.  Asking
> for "the history of file B/foo.c" does not make any sense, as
> git never tracks history of individual files.
Hi Junio,

Obviously you are making an important point here about the way Git is designed, but I think you misspoke slightly. Asking for the history of a file does make sense. Through path limiting you can ask to see just the subset of history that touched a certain file or directory etc..

In a simple repo where you don't have any subtree merge, with a file /B/foo.c that at some point earlier in the history was renamed from /foo.c, the command "git log --follow B/foo.c" will show changes extending back before the rename. However, that doesn't seem to work across a subtree merge. Obviously there's some technical reason for this that i'm overlooking, but on the surface the two cases seem similar.

It would be nice to be able to do "gitk --follow git-gui.sh" in git.git and
actually see the history of that file.  As it stands now, you have to type
"gitk -- git-gui.sh ../git-gui.sh".  Is there a fundamental reason Git can't
be taught to notice this particular type of subtree merge "rename" and
support --follow type semantics?
 
At least the message you referenced from Linus leaves hope that this may be
possible as it makes the case that this is the type of thing that you can do if
you avoid locking yourself into inadequate rename-tracking data structures.
Sean
Junio C Hamano· Jan 7, 2008, 21:04 UTC · re: Sean · lore

Re: how to use git merge -s subtree?

Sean <seanlkml@sympatico.ca> writes:
> ...  Asking for the history of a file does make
> sense.  Through path limiting you can ask to see just the subset of history that
> touched a certain file or directory etc..

That's asking for the history of a _path_ (or subset defined by paths, as in "what changes were made to paths under 'arch/'"), which is very different from asking "I have B/foo.c -- show me the history of that _file_".

Remember, David stated:
>> ... So you cannot do git-log B/foo.c as git doesnot know where
>> to search it as it thinks it is in /foo.c not in B/foo.c ...
Notice "as git does not know where to search it" part?
Think --- what does that "it" refer to in that sentence?

The statement is not about paths. If it were about paths, then the output of "git log B/foo.c" does show what he wants. The question "git log B/foo.c" asks is "what change were made to the path at B/foo.c". The changes made to B/foo.c (i.e. what's shown with the diff headers that begin with "--- a/B/foo.c") are shown. The changes made to foo.c are not shown.

But that is different from what David is asking. He wants to know what changes were made to B/foo.c or to foo.c, and wants to make the choice between the two depend on commit. The reason you think you can pick between foo.c and B/foo.c is because there is an illusion that somehow there is an i-node like file identity that is kept track of, and it is preserved across renames and merges.

That's keeping track of the history of a _file_.
And as you know, git doesn't do that.

What git does is to keep track of the history of the whole tree, but prune the parts that are not interesting away when you view the history. And the pruning can be specified by _PATH_.

See the difference?
Sean· Jan 8, 2008, 05:34 UTC · re: Junio C Hamano · lore

Re: how to use git merge -s subtree?

On Mon, 07 Jan 2008 13:04:34 -0800 Junio C Hamano <gitster@pobox.com> wrote:

Show 39 quoted lines
> That's asking for the history of a _path_ (or subset defined by
> paths, as in "what changes were made to paths under 'arch/'"),
> which is very different from asking "I have B/foo.c -- show me
> the history of that _file_".
> 
> Remember, David stated:
> 
> >> ... So you cannot do git-log B/foo.c as git doesnot know where
> >> to search it as it thinks it is in /foo.c not in B/foo.c ...
> 
> Notice "as git does not know where to search it" part?
> 
> Think --- what does that "it" refer to in that sentence?
> 
> The statement is not about paths.  If it were about paths, then
> the output of "git log B/foo.c" does show what he wants.  The
> question "git log B/foo.c" asks is "what change were made to the
> path at B/foo.c".  The changes made to B/foo.c (i.e. what's
> shown with the diff headers that begin with "--- a/B/foo.c") are
> shown.  The changes made to foo.c are not shown.
> 
> But that is different from what David is asking.  He wants to
> know what changes were made to B/foo.c or to foo.c, and wants to
> make the choice between the two depend on commit.  The reason
> you think you can pick between foo.c and B/foo.c is because
> there is an illusion that somehow there is an i-node like file
> identity that is kept track of, and it is preserved across
> renames and merges.
> 
> That's keeping track of the history of a _file_.
> 
> And as you know, git doesn't do that.
> 
> What git does is to keep track of the history of the whole tree,
> but prune the parts that are not interesting away when you view
> the history.  And the pruning can be specified by _PATH_.
> 
> See the difference?
> 
Junio,

Thanks for taking the time to answer, I know you're busy with much more pressing issues getting 1.5.4 out the door. You make the distinction between paths and files quite clear, and why it's important to keep in mind.

In the case of the "--follow" option though, the user might be forgiven for thinking in terms of files rather than paths. Even the git-log documentation says for "--follow", "Continue listing the history of a file beyond renames". Which at least implies that Git knows about file history, rather than just paths.

It's reasonable for a user to expect "git log --follow B/foo.c" to do The-Right-Thing in the subtree merge case. But I recognize that it would actually be rather difficult to implement, perhaps making it impractical.

Cheers, Sean

Junio C Hamano· Jan 8, 2008, 06:57 UTC · re: Sean · lore

Re: how to use git merge -s subtree?

Sean <seanlkml@sympatico.ca> writes:
Show 5 quoted lines
> In the case of the "--follow" option though, the user might be forgiven
> for thinking in terms of files rather than paths.  Even the git-log
> documentation says for "--follow", "Continue listing the history of a
> file beyond renames".   Which at least implies that Git knows about
> file history, rather than just paths.
But you know git does not know nor care about "file" history.
And the thing is, I just tried this:
  $ git log --follow --pretty=short --stat gitk-git/gitk | head -n 20
  commit 62ba5143ec2ab9d4083669b1b1679355e7639cd5
  Author: Junio C Hamano <gitster@pobox.com>
      Move gitk to its own subdirectory
   gitk => gitk-git/gitk |    0 
   1 files changed, 0 insertions(+), 0 deletions(-)
  commit 7388bcbc5431552718dde5c3259d861d2fa75a12
  Author: Paul Mackerras <paulus@samba.org>
      gitk: Use the UI font for the diff/old version/new version radio buttons
   gitk |    6 +++---
   1 files changed, 3 insertions(+), 3 deletions(-)
  commit cca5d946d692fde7ea5408a694cb4b1c97a5a838
  Author: Paul Mackerras <paulus@samba.org>
      gitk: Simplify the code for finding commits

So there is something else going on, if David actually tried to follow "B/foo" that was subtree-merged from a parent that had it at toplevel "foo" and "log --follow" did not work for him.

Two things that come to mind offhand are that (1) --follow looks for a path that has similar contents elsewhere only when the path it is following in the child disappears in the parent. So if you start from B/foo that was subtree-merged (i.e. the other parent has foo at the top) into a parent that already had B/foo, it will not follow the other parent that does not have B/foo; (2) I do not know if the original example by David tried to use "-n $count" offhand, but it seems that currently --follow does not play well with -n (try the above gitk-git/gitk without piping the result into "head -n 20" but instead by limiting with "git log -3 --follow ..."); if that is the case that definitely is a bug.

Miklos Vajna· Jan 6, 2008, 03:45 UTC · re: Sean · lore

Re: how to use git merge -s subtree?

On Sat, Jan 05, 2008 at 08:17:40PM -0500, Sean <seanlkml@sympatico.ca> wrote:
Show 10 quoted lines
> Here's another way that is perhaps a little cleaner:
> 
> $ git remote add -f B /path/to/B
> $ git merge -s ours --no-commit B/master
> $ git read-tree --prefix=sub/ -u B/master 
> $ git commit -m "subtree merged B"
> 
> The first line creates and fetches the remote.  The second line initiates a merge, but
> stops before committing it.  The third line reads B/master into the subdirectory "sub",
> at which point all that remains is committing the completed merge.

great, thanks. that's more simple than mine, and this is necessary for the first commit only. the rest (i hope i'm right) can be done using just by

$ git fetch $ git merge -s subtree B/master

do you mind if i send a patch to add this to Documentation/howto? i don't think it's trivial :)

thanks,
- VMiklos
David Soria Parra· Jan 6, 2008, 01:20 UTC · re: Miklos Vajna · lore

Re: how to use git merge -s subtree?

As said in IRC, subtree is not made for that as far is a understand.

Quite frankly the given workflow doesnot work for me as subtree imports the things into some directory that it was not supposed to merge it into (x/z/ instead of B).

                              dsp

← back to recent threads