threads / discuss / 6059

Re: Updated Kernel Hacker's guide to git

Subject: Re: Updated Kernel Hacker's guide to git

## tl;dr

12 messages between Dec 21, 2006 and Dec 22, 2006.

replies: 11people: 5as markdown or json

Francis Moreau· Dec 21, 2006, 12:24 UTC · lore
Hi,
[ sorry for breaking up the thread but I'd like to fully
  understand that point and I just subscribed to git mail
  list... ]
Linus Torvalds wrote:
Show 39 quoted lines
>
> On Wed, 20 Dec 2006, Jay Cliburn wrote:
>> $ git diff $(git merge-base master driver)..driver
>
> Be careful. This ONLY works if you don't have criss-cross merges etc, so
> you need to be somewhat careful about it. If you end up having a complex
> merge history between the origin branch and your development tree, things
> will stop working
>
> So it's actually worth understanding what the above does.
>
> Here's the picture to keep in mind: you started off with something like
> this:
>
> 	<- older			newer ->
>
> 	..--A---B---C---D
>
> where the top commit was D, and you started doing your own work off there.
> However, the tree you are tracking (doing "git fetch origin" or somethng)
> ALSO continues, so soon enough you'll have a history that looks like
>
> 	<- older			newer ->
>
> 	..--A---B---C---D---E---F---G---H <-"origin"
> 	                 \
> 	                  --X---Y---Z <- your "driver" branch"
>
> where your work is the "X-Y-Z" sequence.
>
> How, the reason you must NOT do
>
> 	git diff origin..driver
>
> or something like that, is that it will LITERALLY do a diff between those
> two heads. And while that is a perfectly normal diff, it's not what you
> want: it's the diff between the up-stream work and yours, and it will show
> that a lot of work has _not_ happened in your branch (all the E-F-G-H
> stuff), and then you've added some work (X-Y-Z).

I must really miss something but is a diff between origin and driver heads achieved by:

	$ git diff driver origin
instead of:
	$ git diff origin..driver
?
>From the git-rev-list documentation I can read that:
	git-rev-list origin..driver == git-rev-list driver ^origin

I assume it's still true for git-diff. And the documentation says that the above command "lists all the commits which are included in driver, but not in origin".

So all commits included in driver are: (A-B-C-D-X-Y-Z) all commits included in origin are: (A-B-C-D-E-F-G-H)

All the commits which are included in driver, but not in origin are: (X-Y-Z), which is definitely not what you claimed.

I know that I'm wrong, so please shed some light in my stupid brain.
-- 
Francis
Linus Torvalds· Dec 21, 2006, 18:23 UTC · re: Francis Moreau · lore
On Thu, 21 Dec 2006, Francis Moreau wrote:
Show 9 quoted lines
> 
> I must really miss something but is a diff between origin and driver heads
> achieved by:
> 
> 	$ git diff driver origin
> 
> instead of:
> 
> 	$ git diff origin..driver
No. 
	git diff origin driver
and
	git diff origin..driver
is exactly the same thing.
> From the git-rev-list documentation I can read that:
> 
> 	git-rev-list origin..driver == git-rev-list driver ^origin

Correct. However, "git diff" is very aware of things like "^origin", and understands that

	git diff driver ^origin

is the same thing as saying "I want what is in driver, but not in origin", so it needs to switch the arguments.

In short, for git diff (and ONLY) git diff, all of these are the same:
	git diff a..b
	git diff a b
	git diff b ^a
[ ADDITIONALLY git diff _also_ has a magic special case of
	git diff a b ^c
  which actually means the same as "git diff c..a" (and "b" is 
  totally ignored). That may sound strange, but it's because the 
  expression "a...b" means "b a --not $(git-merge-base a b)", and so what 
  you actually WANT is that if you do
	git diff a...b
  you should get "diff from merge-base to b", so when "a...b" expands to 
  "b a ^merge-base", then git understands that if it gets that stange 
  command line with THREE commits, and one of them is negated, you really 
  wanted the diff from the negated one to the first one ]
It basically all boils down to:
	"git diff" is special

exactly because unlike almost ALL other git commands, "git diff" does not work on a _list_ of commits, it only works on two end-points. That means that the "list operations" actually end up meaning something else for git diff than they do for "git log" and friends.

		Linus
Carl Worth· Dec 22, 2006, 01:23 UTC · re: Linus Torvalds · lore
On Thu, 21 Dec 2006 10:23:30 -0800 (PST), Linus Torvalds wrote:
> In short, for git diff (and ONLY) git diff, all of these are the same:
>
> 	git diff a..b
> 	git diff a b

I admit that I had had never passed a range of commits to git diff, nor even given any thought to what it might do, but I definitely find the above very surprising---and not necessarily very useful. Why is anyone ever typing those two dots here if they have no effect on the result?

Show 8 quoted lines
> It basically all boils down to:
>
> 	"git diff" is special
>
> exactly because unlike almost ALL other git commands, "git diff" does not
> work on a _list_ of commits, it only works on two end-points. That means
> that the "list operations" actually end up meaning something else for git
> diff than they do for "git log" and friends.
Yes, "git diff" can only work on two end points. So a command like:
	git diff a b

is really easy to understand. And if the user wants to compare those two points, why would the user ever provide them with any other syntax?

In other words, what's the advantage of "git diff" accepting a special syntax for a range ("a..b") and yet not actually doing anything special with it?

In particular, the operation that would be interesting here is what one can get with:

	git diff $(git merge-base a b) b
so why isn't _that_ the operation that is accessed with the syntax of:
	git diff a..b

Said another way, if "git log a..b" displays a range of commits, why doesn't "git diff a..b" display the diff from the beginning of that range to the end?

Of course, all the warnings you gave about what "git diff" would do in the case of various criss-cross merge scenarios would still apply.

-Carl
Linus Torvalds· Dec 22, 2006, 04:13 UTC · re: Carl Worth · lore
On Thu, 21 Dec 2006, Carl Worth wrote:
Show 12 quoted lines
>
> On Thu, 21 Dec 2006 10:23:30 -0800 (PST), Linus Torvalds wrote:
> > In short, for git diff (and ONLY) git diff, all of these are the same:
> >
> > 	git diff a..b
> > 	git diff a b
> 
> I admit that I had had never passed a range of commits to git diff,
> nor even given any thought to what it might do, but I definitely find
> the above very surprising---and not necessarily very useful. Why is
> anyone ever typing those two dots here if they have no effect on the
> result?

I do it all the time, I never even use the old-fashioned syntax any more. It's much more concise and easy to read, and it has all the nice shortcuts (like empty meaning "HEAD", so you can do "git diff ..next" to see the diff from HEAD to another branch).

It's also useful exactly because of the semantics of things like "...".

In other words, sure, "git diff a b" works, but it just _looks_ more pleasing to use "a..b" and you mentally always pronounce the ".." as "to".

So "git diff a b" doesn't even look good to me any more, because it's literally missing that mental "to" that the ".." adds for me when I read it.

		Linus
Carl Worth· Dec 22, 2006, 22:20 UTC · re: Linus Torvalds · lore
On Thu, 21 Dec 2006 20:13:52 -0800 (PST), Linus Torvalds wrote:
> I do it all the time, I never even use the old-fashioned syntax any more.
> It's much more concise and easy to read, and it has all the nice shortcuts
> (like empty meaning "HEAD", so you can do "git diff ..next" to see the
> diff from HEAD to another branch).

I can understand the advantage of a shortcut like "git diff ..next", but I still don't understand why it's the comparison of HEAD and next that's really interesting here. Wouldn't comparing the merge-base to next be more desirable? For example, if I'm considering whether to merge in next or not, why should I care to see in the diff all the irrelevant stuff that's happened on HEAD since next branched off?

But, really, I still don't understand exactly _what_ "diff a..b" even means. Can you explain it to me?

Presumably the rev-parse magic is happening to the arguments. So does the diff code just end up seeing the expanded equivalent of "b ^a" and then just use the ^ to decide which tree to be on the left side or something?

> It's also useful exactly because of the semantics of things like "...".

And now I'm really confused. If I'm not mistaken, rev-parse will turn "a...b" into something like "a b ^$(merge-base a b)", right? So does the diff code now end up seeing three different tree specifiers? What does it do with that? And how is this useful? (As you said before, diff is always going to end up acting on only two items, so I don't see where there could be an interesting distinction from how you obtain two items from "a..b" compared to "a...b".) But it might be just that I'm really confused here.

> So "git diff a b" doesn't even look good to me any more, because it's
> literally missing that mental "to" that the ".." adds for me when I read
> it.

OK, that's fine. But can you comment on why you want the comparison between the tips and not something based on a comparison from the merge-base to a tip?

-Carl
Linus Torvalds· Dec 22, 2006, 22:34 UTC · re: Carl Worth · lore
On Fri, 22 Dec 2006, Carl Worth wrote:
> 
> I can understand the advantage of a shortcut like "git diff ..next",
> but I still don't understand why it's the comparison of HEAD and next
> that's really interesting here.
I can't understand why people complain about this.
YOU DON'T HAVE TO USE IT. 

Nobvody forces you. Really. And dammit, if I want to compare HEAD and next, I compare HEAD and next. You have absolutely no business saying that you don't understand why I'd want to do it. It's MY LIFE.

> But, really, I still don't understand exactly _what_ "diff a..b" even
> means. Can you explain it to me?
It means exactly the same as "diff a b".
It's that simple.
So now go away. Stop complaining.
		Linus
Junio C Hamano· Dec 22, 2006, 22:45 UTC · re: Carl Worth · lore
Carl Worth <cworth@cworth.org> writes:
Show 10 quoted lines
> On Thu, 21 Dec 2006 20:13:52 -0800 (PST), Linus Torvalds wrote:
>> I do it all the time, I never even use the old-fashioned syntax any more.
>> It's much more concise and easy to read, and it has all the nice shortcuts
>> (like empty meaning "HEAD", so you can do "git diff ..next" to see the
>> diff from HEAD to another branch).
>
> I can understand the advantage of a shortcut like "git diff ..next",
> but I still don't understand why it's the comparison of HEAD and next
> that's really interesting here. Wouldn't comparing the merge-base to
> next be more desirable?
"git diff ...next" should work just as well, I think.
> But, really, I still don't understand exactly _what_ "diff a..b" even
> means. Can you explain it to me?
diff between two points.
> Presumably the rev-parse magic is happening to the arguments. So does
> the diff code just end up seeing the expanded equivalent of "b ^a" and
> then just use the ^ to decide which tree to be on the left side or
> something?

Exactly; diff just reuses the non-magic part of revision parameter parsing without using the ancestry traversal machinery (which is the magic part).

Show 6 quoted lines
>> It's also useful exactly because of the semantics of things like "...".
>
> And now I'm really confused. If I'm not mistaken, rev-parse will turn
> "a...b" into something like "a b ^$(merge-base a b)", right? So does
> the diff code now end up seeing three different tree specifiers? What
> does it do with that?

It knows that rev-parse makes it "b a ^base", and it knows what the user meant is "base..b".

That's all because we teach them to spell "a...b" when they want "base..b". Similarly, because we allow them to spell "a..b" when they mean "between two points, a and b", it handles "b ^a" as the equivalent to giving two trees separately, as "a b".

Show 5 quoted lines
> And how is this useful? (As you said before,
> diff is always going to end up acting on only two items, so I don't
> see where there could be an interesting distinction from how you
> obtain two items from "a..b" compared to "a...b".) But it might be
> just that I'm really confused here.

I do not think you are seriously saying that comparing A and B in this picture is meaningless:

      o---o---B
     /
 ---M---o---A

Yes, it is often useful to compare M and B, but that "often" is limited to the phase of the workflow where you are at A and are contemplating of merging B. But merging two forked branches is not the only thing you do.

Comparing two revisions regardless of how they are topologically related is useful as often. It is only that it may not as useful while you are trying to merge B into A.

So there are two ways to let you do both, and they need to be specified differently (.. vs ...) because you need to tell it to do which one.

Carl Worth· Dec 22, 2006, 23:31 UTC · re: Junio C Hamano · lore
On Fri, 22 Dec 2006 14:45:18 -0800, Junio C Hamano wrote:
> Exactly; diff just reuses the non-magic part of revision
> parameter parsing without using the ancestry traversal
> machinery (which is the magic part).
Thanks, that helps.
> It knows that rev-parse makes it "b a ^base", and it knows what
> the user meant is "base..b".
Ahah! So the "a...b" syntax is how to get at what:
	git diff $(git merge-base a b) b
would give. Thanks for explaining, since that was not at all obvious.
> That's all because we teach them to spell "a...b" when they want
> "base..b".  Similarly, because we allow them to spell "a..b"
> when they mean "between two points, a and b", it handles "b ^a"
> as the equivalent to giving two trees separately, as "a b".

Where is that teaching happening exactly? Both the "a..b" and "a...b" forms seem to be entirely missing from the documentation of git diff. :-)

Show 6 quoted lines
> I do not think you are seriously saying that comparing A and
> B in this picture is meaningless:
>
>       o---o---B
>      /
>  ---M---o---A
No, I'm certainly not saying that that that comparison is meaningless.

All I was trying to say is that comparing M to B would be meaningful too, and I hoped that git would provide good syntax for both. If I'm understanding things correctly now, what exists is:

  git diff A B
  git diff A..B
	Compare A to B
  git diff A...B
	Compare M to B

So, at the beginning of this thread I wasn't aware that git provided any short syntax for the "M to B" comparison, (the thread started with suggestions for adding "git diff $(git merge-base A B) B" to the hackers guide).

Now, I'm happy to know that git does provide a short syntax for that useful operation.

My only comment now is that the distinction between ".." and "..." in git-diff is quite surprising. In fact, in one sense it's exactly opposite from the distinction of ".." and "..." as used in git log.

For example, given the above graph, "git diff A..B" shows changes due to all 5 of commits on both branches while "git log -p A..B" shows only the changes of the 3 commits from M to B.

Meanwhile, "git diff A...B" shows changes due to the 3 commits from M to B while "git log -p A...B" shows the changes from all 5 of the commits on both branches.

That's really surprising behavior (to me at least).
-Carl
Jakub Narebski· Dec 22, 2006, 23:00 UTC · re: Carl Worth · lore
[Cc: Carl Worth <cworth@cworth.org>, Linus Torvalds <torvalds@osdl.org>
 git@vger.kernel.org]
Carl Worth wrote:
Show 15 quoted lines
> On Thu, 21 Dec 2006 20:13:52 -0800 (PST), Linus Torvalds wrote:
>> I do it all the time, I never even use the old-fashioned syntax any more.
>> It's much more concise and easy to read, and it has all the nice shortcuts
>> (like empty meaning "HEAD", so you can do "git diff ..next" to see the
>> diff from HEAD to another branch).
> 
> I can understand the advantage of a shortcut like "git diff ..next",
> but I still don't understand why it's the comparison of HEAD and next
> that's really interesting here. Wouldn't comparing the merge-base to
> next be more desirable? For example, if I'm considering whether to
> merge in next or not, why should I care to see in the diff all the
> irrelevant stuff that's happened on HEAD since next branched off?
> 
> But, really, I still don't understand exactly _what_ "diff a..b" even
> means. Can you explain it to me?
For me, it's just a bit of syntactic sugar (I always have in mind that
git-log and friends outputs commit list and use revisions range, while
git-diff and friends needs two (or less) revisions) allowing to
copy'n'paste arguments from "git log a..b" to "git diff a..b"
 
> Presumably the rev-parse magic is happening to the arguments. So does
> the diff code just end up seeing the expanded equivalent of "b ^a" and
> then just use the ^ to decide which tree to be on the left side or
> something?
And that is just implementation. I don't think anyone uses "git diff b ^a".
Show 10 quoted lines
>> It's also useful exactly because of the semantics of things like "...".
> 
> And now I'm really confused. If I'm not mistaken, rev-parse will turn
> "a...b" into something like "a b ^$(merge-base a b)", right? So does
> the diff code now end up seeing three different tree specifiers? What
> does it do with that? And how is this useful? (As you said before,
> diff is always going to end up acting on only two items, so I don't
> see where there could be an interesting distinction from how you
> obtain two items from "a..b" compared to "a...b".) But it might be
> just that I'm really confused here.

I would have thought that it would be combined diff of a and b against it's merge base... but it is not.

"git diff a...b" is turned into "git diff a b ^$(git merge-base a b)", and by a bit of magic (and by a convention) it is turned into "git diff ^$(merge-base a b) a" (and a...b ceases to be _symmetric_ for git-diff).

I'd like for "git diff --cc a...b" to do 'the right thing' and show git diff --cc for pretended merge (I'm not sure if with or without resolving trivial conflicts).

Show 7 quoted lines
>> So "git diff a b" doesn't even look good to me any more, because it's
>> literally missing that mental "to" that the ".." adds for me when I read
>> it.
> 
> OK, that's fine. But can you comment on why you want the comparison
> between the tips and not something based on a comparison from the
> merge-base to a tip?
a..b does not imply merge-base, a...b does.
-- 
Jakub Narebski
Warsaw, Poland
ShadeHawk on #git
Francis Moreau· Dec 22, 2006, 09:35 UTC · re: Linus Torvalds · lore
Linus Torvalds wrote:
Show 7 quoted lines
>
> In short, for git diff (and ONLY) git diff, all of these are the same:
>
> 	git diff a..b
> 	git diff a b
> 	git diff b ^a
>
I think this part is really confusing. For a new comer, saying that:
	git diff a b == git diff a..b

is really not intuitive. Maybe just because adding a new symbol ".." in git diff command line means (for me) that we're doing a special diff. I would never thought by my own that ".." means a simple "to".

Show 20 quoted lines
> [ ADDITIONALLY git diff _also_ has a magic special case of
>
> 	git diff a b ^c
>
>   which actually means the same as "git diff c..a" (and "b" is
>   totally ignored). That may sound strange, but it's because the
>   expression "a...b" means "b a --not $(git-merge-base a b)", and so what
>   you actually WANT is that if you do
>
> 	git diff a...b
>
>   you should get "diff from merge-base to b", so when "a...b" expands to
>   "b a ^merge-base", then git understands that if it gets that stange
>   command line with THREE commits, and one of them is negated, you really
>   wanted the diff from the negated one to the first one ]
>
> It basically all boils down to:
>
> 	"git diff" is special
>

but this very special part of git diff is also not documented at all when reading the manual of git-diff... Maybe it can be reached by others manuals ?

Show 5 quoted lines
> exactly because unlike almost ALL other git commands, "git diff" does not
> work on a _list_ of commits, it only works on two end-points. That means
> that the "list operations" actually end up meaning something else for git
> diff than they do for "git log" and friends.
>
I think I got your point now: git-diff only works on two end-points.
Why not making it less special as follow:
	git diff a b
	git diff b ^a

These two diff commands are the same. They do a diff between a and b end-points (maybe commits is better there since we don't add one more keyword). It's similar to diff command. For me it's quite intuitive.

	git diff a..b

Ok now the command syntax is more special (maybe simply because traditional diff() does not have a similar syntax, it's really specific to git). "git log" and friends have a similar syntax and they do work on a list of commits. For consistency sake, make this commad works on commit list too. Therefore this command would end up doing exactly the same thing like "git diff a...b"

	git diff a...b
No more need of this special syntax.
What do you think ?
thanks
-- 
Francis
Junio C Hamano· Dec 22, 2006, 10:26 UTC · re: Francis Moreau · lore
"Francis Moreau" <francis.moro@gmail.com> writes:
Show 7 quoted lines
> I think this part is really confusing. For a new comer, saying that:
>
> 	git diff a b == git diff a..b
>
> is really not intuitive. Maybe just because adding a new symbol ".."
> in git diff command line means (for me) that we're doing a special
> diff. I would never thought by my own that ".." means a simple "to".

We did not originally have "A B"; you were supposed to always say "A..B". But all other SCM had "A B" notation, so we added support for both because doing so was trivial and there is no risk of confusion (because diff is about two points while log is about a set). These two notations are interchangeable for "diff". If it confuses you, you can stick to the "git diff A B" notation. Of if you are like Linus, stick to "A..B" notation. Either way, you can pretend that the other notation does not even exist and be happy ;-).

Yes, users often wondered why "git diff" accepts "A B", "git log" wants "A..B" and "git log A B" is a disaster. But the root cause of the confusion was not about notation but about the conceptual difference (two points vs a set).

I do not think changing the meaning of "diff A..B" to what "diff A...B" means is a good thing. The notation "..." even _looks_ like a magic, and in diff context, what it does _is_ magic (it is magic in log context, too). You are giving two points, but what actually is used as the two points for diff are different from what you gave; in that sense, it is a very good notation. Changing it would confuse and inconvenience people who already understood and got used to the difference between "diff" and "log": diff takes two points, so given usual A..B notation it uses A and B, while log is about a set and means 'the ones reachable from B, excluding the ones reachable from A'; "A...B" is magic and does a magical thing in both "diff" and "log", taking the merge bases between A and B into account.

Francis Moreau· Dec 22, 2006, 20:34 UTC · re: Junio C Hamano · lore
On 12/22/06, Junio C Hamano <junkio@cox.net> wrote:
Show 20 quoted lines
> "Francis Moreau" <francis.moro@gmail.com> writes:
>
> > I think this part is really confusing. For a new comer, saying that:
> >
> >       git diff a b == git diff a..b
> >
> > is really not intuitive. Maybe just because adding a new symbol ".."
> > in git diff command line means (for me) that we're doing a special
> > diff. I would never thought by my own that ".." means a simple "to".
>
> We did not originally have "A B"; you were supposed to always
> say "A..B".  But all other SCM had "A B" notation, so we added
> support for both because doing so was trivial and there is no
> risk of confusion (because diff is about two points while log is
> about a set).  These two notations are interchangeable for
> "diff".  If it confuses you, you can stick to the "git diff A B"
> notation.  Of if you are like Linus, stick to "A..B" notation.
> Either way, you can pretend that the other notation does not
> even exist and be happy ;-).
>

no, no, I think you miss my point here. What is confusing, and it seems I'm not the only one to find it confusing, is the fact that "git diff A B == git diff A..B", not the "A..B" notation per se.

git diff A B, is really intuitive and easy to understand because we all use to using the diff "A B" notation and it always has worked with 2 endpoints. So I would bet that all newbies who use for the first time the git diff command will use "A B" notation, not "A..B" one.

Show 9 quoted lines
> Yes, users often wondered why "git diff" accepts "A B", "git
> log" wants "A..B" and "git log A B" is a disaster.  But the root
> cause of the confusion was not about notation but about the
> conceptual difference (two points vs a set).
>
> I do not think changing the meaning of "diff A..B" to what "diff
> A...B" means is a good thing.  The notation "..." even _looks_
> like a magic, and in diff context, what it does _is_ magic (it
> is magic in log context, too).  You are giving two points, but

But ".." is magic too ! I would really expect something magic to happen when using the "A..B" notation because I'm a new comer in git and I never used the "A..B" notation before.

And since I already used this notation when using git log command, I would expect "git diff A..B" to give the current result given by "git diff A...B".

But yes, now I have understand that git diff works with end points unlike git log, I can stick with two notations:

        git diff A B
        git diff A...B (3 dots)
and I'm happy ;)
thanks
-- 
Francis

← back to recent threads