From: Junio C Hamano <hidden> Date: 2016-06-15 22:46:54
Linus Torvalds [off-list ref] writes:
For example, right now there is _no_ way to get even a "show diff relative
to first parent". You can do "-m", which will show it relative to _both_
parents, but nobody ever wants that. And you can do "-c" or "--cc", but
that simplifies away all the paths that match in one.
Actually for that "Where did my file 'x' go across the merge chain", I was
going to suggest something like
git log --simplify-merges -m --raw -- x
So here's a challenge: in the git repository, get a nice view of what your
merges looked like. The closest I can get is
git log -c --stat --grep="Merge branch '"
which is actually very non-intuitive ("-c" on its own gives no useful
output, but "-c --stat" gives nice diffstat against the first parent,
which in this case is what we want).
I think the logical place to hook that into is the --first-parent option.
I actually have very hard resisted so far the temptation to do so because
your mantra has always been "in a merge, all parents are equal." If you
treat the first parent specially too heavily, it would go against the "I
got a pull request from you, but the resulting conflicts are too much for
me; you know the area much better than I do, so could you do the merge for
me and I'll fast forward to you later" workflow. The "first parent is the
mainline" and "I am important, so I'll merge with --no-ff to pee in the
snow" mentality problems will become worse.
From: Jeff King <hidden> Date: 2016-06-15 22:46:54
On Wed, Jun 03, 2009 at 03:38:00PM -0700, Junio C Hamano wrote:
Actually for that "Where did my file 'x' go across the merge chain", I was
going to suggest something like
git log --simplify-merges -m --raw -- x
But in the original example, the merge commit where 'x' is deleted isn't
shown _at all_ when path limiting is used. You end up either with
"git log -m" showing the two sides of the merge separately, "git log
--simplify-merges -- x" showing stuff that happened on the side branch
but _not_ the actual merge that made a change, or of course "git log --
x" showing nothing (because we don't traverse the changing side of the
merge).
Is there a way to say "show me everything that touched x, _including_
merges"?
-Peff
For example, right now there is _no_ way to get even a "show diff relative
to first parent". You can do "-m", which will show it relative to _both_
parents, but nobody ever wants that. And you can do "-c" or "--cc", but
that simplifies away all the paths that match in one.
Actually for that "Where did my file 'x' go across the merge chain", I was
going to suggest something like
git log --simplify-merges -m --raw -- x
Ok. Not very readable, but it's certainly getting closer.
quoted
So here's a challenge: in the git repository, get a nice view of what your
merges looked like. The closest I can get is
git log -c --stat --grep="Merge branch '"
which is actually very non-intuitive ("-c" on its own gives no useful
output, but "-c --stat" gives nice diffstat against the first parent,
which in this case is what we want).
I think the logical place to hook that into is the --first-parent option.
I actually have very hard resisted so far the temptation to do so because
your mantra has always been "in a merge, all parents are equal."
Oh, I agree. The challenge was just the first step - how to make it do
merges in general sanely would be the issue.
Because if you do just --first-parent, then that won't even show the
test-case that Graham actually had - because the missing file didn't come
in from the first parent of a merge.
The challenge was mainly as a way to point out that even some fairly
simple cases aren't all that simple.
Linus
Is there a way to say "show me everything that touched x, _including_
merges"?
Well, that's the "--simplify-merges" part.
It's just that our diff generation isn't very smart. We do show the
commit, we just don't show a meaningful diff in that case.
And doing good diffs for a merge is _hard_. The "--cc" thing is supremely
useful - it's just that it's useful for data conflicts, not for metadata
issues.
It's in fact somewhat dubious if you actually want to see the file removal
as a _diff_ in a merge, exactly because it's so verbose and yet often so
uninteresting (ie the removal may well be intentional).
It might be that the right thing to do is to expand on "-c" and '--cc" to
just give a summary of metadata changes.
Right now, "-c" and "--cc" ignore files that didn't change from one of the
parents.
But maybe the right thing to do is to entirely ignore files only if they
exist in all parents, but didn't change in one - and for things that have
actual metadata changes, just say
- exists in merge result, but not in parent 2:
File 'x' was created in parent 1
- does not exist in merge result, but exists in parent 1:
File 'y' was deleted by parent 2
or similar (with perhaps even rename detection some day if -M is
specified, although n-way rename detection is likely pretty painful).
IOW, do the whole "extended diff", but not actually show any diffs, just
summary information, for new/deleted files in merges.
That would be enough of a hint to then use other tools to see the exact
details of what happened..
Linus
From: Jeff King <hidden> Date: 2016-06-15 22:46:54
On Wed, Jun 03, 2009 at 03:56:01PM -0700, Linus Torvalds wrote:
quoted
Is there a way to say "show me everything that touched x, _including_
merges"?
Well, that's the "--simplify-merges" part.
It's just that our diff generation isn't very smart. We do show the
commit, we just don't show a meaningful diff in that case.
No, --simplify-merges doesn't show the merge, unless I am doing
something very wrong. Try (and this is a simplified version of the
original example):
mkdir repo && cd repo && git init &&
echo content >base && git add base && git commit -m base &&
echo context >a.txt && git add a.txt && git commit -m 'master 1' &&
git checkout -b other HEAD^ &&
echo content >b.txt && git add b.txt && git commit -m 'other 1' &&
echo conflict >a.txt && git add a.txt && git commit -m 'other 2' &&
git checkout master &&
git merge other ;# conflicts
rm b.txt && git add b.txt &&
echo resolve >a.txt && git add a.txt &&
git commit -m merged
Now try running git log on that. I can see the merge diff if I use "-m",
which is obviously too verbose, but at least works. But if I give
"b.txt" as a path limiter, I can't get the merge commit to display at
all. Doing "git log -m --simplify-merges --stat -- b.txt" yields only
the commit "other 1" in which b.txt was added.
-Peff
Try (and this is a simplified version of the original example):
mkdir repo && cd repo && git init &&
echo content >base && git add base && git commit -m base &&
echo context >a.txt && git add a.txt && git commit -m 'master 1' &&
git checkout -b other HEAD^ &&
echo content >b.txt && git add b.txt && git commit -m 'other 1' &&
echo conflict >a.txt && git add a.txt && git commit -m 'other 2' &&
git checkout master &&
git merge other ;# conflicts
rm b.txt && git add b.txt &&
echo resolve >a.txt && git add a.txt &&
git commit -m merged
This doesn't work at all for me.
Do
git show HEAD:b.txt
and it still shows b.txt in the commit. You should have used
git rm b.txt
rather than "git add b.txt" (or you use use "-u" or "-a" to git add).
That looks like a bug, btw, but whatever. It seems intentional (we do the
whole "ADD_CACHE_IGNORE_REMOVAL" flag thing).
But you're right. Even when fixed, it does seem to need "--full-history"
to stay around, and --simplify-merges is insufficient. Bug in merge
simplification?
Linus
This doesn't work at all for me.
Do
git show HEAD:b.txt
and it still shows b.txt in the commit. You should have used
git rm b.txt
rather than "git add b.txt" (or you use use "-u" or "-a" to git add).
Er, sorry, yeah, I botched the recipe (I initially used "git rm" by
itself, but it complains about "changes staged in the index", so I fixed
it up manually and then botched writing out the automated version).
But I see you figured out what I meant, so...
But you're right. Even when fixed, it does seem to need "--full-history"
to stay around, and --simplify-merges is insufficient. Bug in merge
simplification?
I don't even see it with --full-history. I get:
$ git log -m --stat --oneline | head
b1a38ec (from 2671fa7) merged
a.txt | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
b1a38ec (from d0bac65) merged
a.txt | 2 +-
b.txt | 1 -
2 files changed, 1 insertions(+), 2 deletions(-)
$ git log -m --stat --oneline -- b.txt
(no output)
$ git log --simplify-merges -m --stat --oneline -- b.txt
912ac84 other 1
b.txt | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
$ git log --full-history -m --stat --oneline -- b.txt
912ac84 other 1
b.txt | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
Is there some trick to enabling both path limiting _and_ still showing
the merge commit? Or is this a bug?
-Peff
Oh, that's because I used "gitk" rather than "git log", and that adds
--parents, which in turn means that it actually keeps the merges.
So with
git log --full-history --parents --stat -- b.txt
you actually finally get a visible removal.
Linus
From: Junio C Hamano <hidden> Date: 2016-06-15 22:46:54
Linus Torvalds [off-list ref] writes:
And doing good diffs for a merge is _hard_. The "--cc" thing is supremely
useful - it's just that it's useful for data conflicts, not for metadata
issues.
It's in fact somewhat dubious if you actually want to see the file removal
as a _diff_ in a merge, exactly because it's so verbose and yet often so
uninteresting (ie the removal may well be intentional).
Thinking about this a bit more, a "good" diff for merge that catches this
kind of "broken merge" would likely require redoing a merge when you ask
for a diff.
Earlier, in my "Here is a crude attempt" patch, I mentioned that the
merges in the beginning part of the output are uninteresting while the one
that merges "Build-in git-clone" is interesting (and "crude attempt" was
not useful because it shows both).
The reason? The uninteresting ones match mechanical merge result, while
the interesting one does not. For example, 17d778e (the first one in the
output --- an uninteresting one) is a merge between 5e97f46 and 450f437;
the former branch removes the path in question (git-clone.sh) compared to
their merge base, while the latter does not change it, hence the
mechanical resolution to remove the path matches what the final merge
records. On the other hand, b84c343 (Merge branch 'db/clone-in-c') merges
0dbaa5b (modifies "git-clone.sh" since the merge base) and b50c846
(removes) and the mechanical merge results in a conflict.
I think the original example can be handled in the same way. A side
branch created a new file since the merge base, but a merge lost the file
by mistake. The recorded result does not resemble the mechanical merge
result and we can flag it by detecting this condition (that is, if we
wanted to --- I do not think we want to spend cycles to recreate a merge
while traversing the history with "log --stat" by default).