From: Linus Torvalds <torvalds@osdl.org>
To: Jay Cliburn <jacliburn@bellsouth.net>
Cc: git@vger.kernel.org, Jeff Garzik <jeff@garzik.org>
Subject: Re: Updated Kernel Hacker's guide to git
Date: Wed, 20 Dec 2006 23:51:20 -0800 (PST) [thread overview]
Message-ID: <Pine.LNX.4.64.0612202317110.3576@woody.osdl.org> (raw)
In-Reply-To: <4589FD9E.2010000@bellsouth.net>
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).
What you obviously WANT to have is the "diff" between "D" (which was the
point where you started) and "Z" (which is where you are now. That's what
you want to send up-stream.
Now, the way people used to do this under CVS, was to simply _remember_ D.
Preferably by doing a tag at the point where they started working. Then
you can always diff against that tag.
You can do that in git too, of course, but it would just be stupid.
Because git actually _knows_ the history, so you can just ask git
"What is the most recent commit that both 'origin' and 'driver'
have in common?"
the answer, of course, is the very commit you want: D. And how do you ask
for that "most recent common commit"? That's right: it's called the "merge
base", because it's also the thing you'd use as the base in a bog-standard
three-way merge.
So "git merge-base origin driver" just returns "D", without you having had
to tag it or remember it at all.
So when you do
git diff $(git merge-base origin driver)..driver
you're literally asking for the diff between your current top of the
"driver" branch, and the place where you diverged from "origin".
Now, why do I mention the fact that THIS DOES NOT WORK if you do merges
and you no longer have a linear tree? Think about it. If you actually ever
pull on the up-stream and create a merge in your development tree, the
commit history graph will now look something like this:
<- older newer ->
..--A---B---C---D---E---F---G---H <-"origin"
\ \
--X---Y--M--Z <- your "driver" branch"
where the "M" commit is because you merged some new work from origin.
But see what that did to the most recent commit? Now, the most recent
commit is no longer "D". In fact, it's not even your "merge" commit, if
you think about it (even though you might naively think so), because that
merge commit doesn't even exist in "origin", so clearly it cannot be the
most recent common commit.
The most recent commit in common is "F". So now,
git diff $(git merge-base origin driver)..driver
will give the difference between _F_ and your current tip Z.
The thing is, THIS IS STILL THE RIGHT THING TO DO. It's magic. Since you
did a merge in M, all the things up until F have been merged into Z too,
so you actually NO LONGER want to diff against D. Nor do you want to diff
against M (since that would mean that you would not see the work you did
in X and Y).
By doing a diff against F (and your own merge having done the right
thing), you actually end up getting the "union" of work for X-Y-Z when you
do that magic command line. If you had done it against D, you'd have seen
the work in E and F that the merge M brought in - but you don't want that,
because you want just the work YOU did, not anything in "origin".
So what you want is actually exactly again the most recent common commit.
HOWEVER. While it magically "just works" in things like this, it doesn't
actually work for the really complex cases. If I've merged an eariler
version of your work AND you did a merge from me too, you can have a
criss-cross merge where there simply isn't one "most recent common commit"
at all any more, but two or more. And then you really do need to do more.
This is one reason why I suggest developers not merge from me very often:
if your development tree is just a straight line (ie you only had that one
merge), you can't ever even get in that situation, and perhaps as
importantly, if/when I pull from you, the result will also tend to look
more understandable to people who look at the history.
But another way to avoid a criss-cross merge is actually to never let me
see your tree at all, and then you can merge with me as often as you damn
well like. And in that case, the magic
git diff $(git merge-base origin driver)..driver
is always going to give you exactly what you want. You can use merges to
keep up-to-date, and always see what you want to send off by email
upstream. The history will look like crap (you'll end up having a ton of
merges in your tree), but if you aren't going to publicise your history
anyway (just the diff end result), why would you care?
(And yes, you can actually do "git diff origin...driver" with _three_
dots. I'm not even going to explain _why_ that works, because that's a
whole other kettle of fish, and is actually a magic special case in
builtin-diff.c. So feel free to use it because it's short and sweet, but
the long format is actually easier to explain what it actually MEANS).
Linus
next prev parent reply other threads:[~2006-12-21 7:51 UTC|newest]
Thread overview: 45+ messages / expand[flat|nested] mbox.gz Atom feed top
2006-12-21 3:04 Updated Kernel Hacker's guide to git Jeff Garzik
2006-12-21 3:21 ` Jay Cliburn
2006-12-21 7:04 ` Martin Langhoff
2006-12-21 7:32 ` Junio C Hamano
2006-12-21 7:51 ` Linus Torvalds [this message]
2006-12-21 11:53 ` Jeff Garzik
2006-12-21 5:44 ` Willy Tarreau
2006-12-21 5:53 ` Nigel Cunningham
2006-12-21 11:44 ` Jeff Garzik
2006-12-21 21:17 ` Nigel Cunningham
2006-12-21 13:53 ` Francois Romieu
2006-12-21 20:40 ` Guennadi Liakhovetski
2006-12-21 20:46 ` Jeff Garzik
2006-12-22 8:50 ` Jesper Juhl
2006-12-24 18:07 ` Horst H. von Brand
2007-12-23 11:13 ` Jeff Garzik
2007-12-23 12:08 ` Robert P. J. Day
2007-12-23 12:13 ` Jeff Garzik
2007-12-23 12:20 ` Robert P. J. Day
2007-12-23 13:05 ` Dieter Ries
2007-12-23 17:23 ` Robert P. J. Day
2007-12-23 20:14 ` Stefan Richter
2007-12-24 14:19 ` Robert P. J. Day
2007-12-23 12:25 ` WANG Cong
2007-12-24 12:50 ` Miklos Vajna
2007-12-25 13:08 ` Salikh Zakirov
2007-12-31 2:50 ` Jan Engelhardt
2007-12-31 11:26 ` Stefan Richter
2007-12-31 17:31 ` Junio C Hamano
2008-06-30 2:51 ` Jeff Garzik
2008-06-30 6:27 ` Stefan Richter
2008-06-30 2:49 ` Jeff Garzik
2008-07-03 6:26 ` Christian Couder
-- strict thread matches above, loose matches on Subject: below --
2006-12-21 12:24 Francis Moreau
2006-12-21 18:23 ` Linus Torvalds
2006-12-22 1:23 ` Carl Worth
2006-12-22 4:13 ` Linus Torvalds
2006-12-22 22:20 ` Carl Worth
2006-12-22 22:34 ` Linus Torvalds
2006-12-22 22:45 ` Junio C Hamano
2006-12-22 23:31 ` Carl Worth
2006-12-22 23:00 ` Jakub Narebski
2006-12-22 9:35 ` Francis Moreau
2006-12-22 10:26 ` Junio C Hamano
2006-12-22 20:34 ` Francis Moreau
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=Pine.LNX.4.64.0612202317110.3576@woody.osdl.org \
--to=torvalds@osdl.org \
--cc=git@vger.kernel.org \
--cc=jacliburn@bellsouth.net \
--cc=jeff@garzik.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).