2008-08-04 02:47:16 +02:00
|
|
|
#!/bin/sh
|
|
|
|
|
|
|
|
test_description='merge simplification'
|
|
|
|
|
|
|
|
. ./test-lib.sh
|
|
|
|
|
|
|
|
note () {
|
|
|
|
git tag "$1"
|
|
|
|
}
|
|
|
|
|
|
|
|
unnote () {
|
|
|
|
git name-rev --tags --stdin | sed -e "s|$_x40 (tags/\([^)]*\)) |\1 |g"
|
|
|
|
}
|
|
|
|
|
|
|
|
test_expect_success setup '
|
|
|
|
echo "Hi there" >file &&
|
2013-05-16 17:32:35 +02:00
|
|
|
echo "initial" >lost &&
|
|
|
|
git add file lost &&
|
|
|
|
test_tick && git commit -m "Initial file and lost" &&
|
2008-08-04 02:47:16 +02:00
|
|
|
note A &&
|
|
|
|
|
|
|
|
git branch other-branch &&
|
|
|
|
|
|
|
|
echo "Hello" >file &&
|
2013-05-16 17:32:35 +02:00
|
|
|
echo "second" >lost &&
|
|
|
|
git add file lost &&
|
|
|
|
test_tick && git commit -m "Modified file and lost" &&
|
2008-08-04 02:47:16 +02:00
|
|
|
note B &&
|
|
|
|
|
|
|
|
git checkout other-branch &&
|
|
|
|
|
|
|
|
echo "Hello" >file &&
|
2013-05-16 17:32:35 +02:00
|
|
|
>lost &&
|
|
|
|
git add file lost &&
|
2008-08-04 02:47:16 +02:00
|
|
|
test_tick && git commit -m "Modified the file identically" &&
|
|
|
|
note C &&
|
|
|
|
|
|
|
|
echo "This is a stupid example" >another-file &&
|
|
|
|
git add another-file &&
|
|
|
|
test_tick && git commit -m "Add another file" &&
|
|
|
|
note D &&
|
|
|
|
|
2013-05-16 17:32:35 +02:00
|
|
|
test_tick &&
|
|
|
|
test_must_fail git merge -m "merge" master &&
|
|
|
|
>lost && git commit -a -m "merge" &&
|
2008-08-04 02:47:16 +02:00
|
|
|
note E &&
|
|
|
|
|
|
|
|
echo "Yet another" >elif &&
|
|
|
|
git add elif &&
|
|
|
|
test_tick && git commit -m "Irrelevant change" &&
|
|
|
|
note F &&
|
|
|
|
|
|
|
|
git checkout master &&
|
|
|
|
echo "Yet another" >elif &&
|
|
|
|
git add elif &&
|
|
|
|
test_tick && git commit -m "Another irrelevant change" &&
|
|
|
|
note G &&
|
|
|
|
|
|
|
|
test_tick && git merge -m "merge" other-branch &&
|
|
|
|
note H &&
|
|
|
|
|
|
|
|
echo "Final change" >file &&
|
|
|
|
test_tick && git commit -a -m "Final change" &&
|
2013-01-17 23:23:03 +01:00
|
|
|
note I &&
|
|
|
|
|
|
|
|
git symbolic-ref HEAD refs/heads/unrelated &&
|
|
|
|
git rm -f "*" &&
|
|
|
|
echo "Unrelated branch" >side &&
|
|
|
|
git add side &&
|
|
|
|
test_tick && git commit -m "Side root" &&
|
|
|
|
note J &&
|
|
|
|
|
|
|
|
git checkout master &&
|
merge: refuse to create too cool a merge by default
While it makes sense to allow merging unrelated histories of two
projects that started independently into one, in the way "gitk" was
merged to "git" itself aka "the coolest merge ever", such a merge is
still an unusual event. Worse, if somebody creates an independent
history by starting from a tarball of an established project and
sends a pull request to the original project, "git merge" however
happily creates such a merge without any sign of something unusual
is happening.
Teach "git merge" to refuse to create such a merge by default,
unless the user passes a new "--allow-unrelated-histories" option to
tell it that the user is aware that two unrelated projects are
merged.
Because such a "two project merge" is a rare event, a configuration
option to always allow such a merge is not added.
We could add the same option to "git pull" and have it passed
through to underlying "git merge". I do not have a fundamental
opposition against such a feature, but this commit does not do so
and instead leaves it as low-hanging fruit for others, because such
a "two project merge" would be done after fetching the other project
into some location in the working tree of an existing project and
making sure how well they fit together, it is sufficient to allow a
local merge without such an option pass-through from "git pull" to
"git merge". Many tests that are updated by this patch does the
pass-through manually by turning:
git pull something
into its equivalent:
git fetch something &&
git merge --allow-unrelated-histories FETCH_HEAD
If somebody is inclined to add such an option, updated tests in this
change need to be adjusted back to:
git pull --allow-unrelated-histories something
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-03-18 21:21:09 +01:00
|
|
|
test_tick && git merge --allow-unrelated-histories -m "Coolest" unrelated &&
|
2013-01-17 23:23:03 +01:00
|
|
|
note K &&
|
|
|
|
|
|
|
|
echo "Immaterial" >elif &&
|
|
|
|
git add elif &&
|
|
|
|
test_tick && git commit -m "Last" &&
|
|
|
|
note L
|
2008-08-04 02:47:16 +02:00
|
|
|
'
|
|
|
|
|
|
|
|
FMT='tformat:%P %H | %s'
|
|
|
|
|
2013-04-08 22:10:27 +02:00
|
|
|
check_outcome () {
|
|
|
|
outcome=$1
|
|
|
|
shift
|
2008-08-04 02:47:16 +02:00
|
|
|
for c in $1
|
|
|
|
do
|
|
|
|
echo "$c"
|
|
|
|
done >expect &&
|
|
|
|
shift &&
|
|
|
|
param="$*" &&
|
2013-04-08 22:10:27 +02:00
|
|
|
test_expect_$outcome "log $param" '
|
2008-08-04 02:47:16 +02:00
|
|
|
git log --pretty="$FMT" --parents $param |
|
|
|
|
unnote >actual &&
|
|
|
|
sed -e "s/^.* \([^ ]*\) .*/\1/" >check <actual &&
|
2015-03-20 11:07:52 +01:00
|
|
|
test_cmp expect check
|
2008-08-04 02:47:16 +02:00
|
|
|
'
|
|
|
|
}
|
|
|
|
|
2013-04-08 22:10:27 +02:00
|
|
|
check_result () {
|
|
|
|
check_outcome success "$@"
|
|
|
|
}
|
|
|
|
|
2013-01-17 23:23:03 +01:00
|
|
|
check_result 'L K J I H G F E D C B A' --full-history
|
|
|
|
check_result 'K I H E C B A' --full-history -- file
|
|
|
|
check_result 'K I H E C B A' --full-history --topo-order -- file
|
|
|
|
check_result 'K I H E C B A' --full-history --date-order -- file
|
2013-05-16 17:32:37 +02:00
|
|
|
check_result 'I E C B A' --simplify-merges -- file
|
2008-08-04 02:47:16 +02:00
|
|
|
check_result 'I B A' -- file
|
|
|
|
check_result 'I B A' --topo-order -- file
|
Making pathspec limited log play nicer with --first-parent
In a topic branch workflow, you often want to find the latest commit that
merged a side branch that touched a particular area of the system, so that
a new topic branch to work on that area can be forked from that commit.
For example, I wanted to find an appropriate fork-point to queue Luke's
changes related to git-p4 in contrib/fast-import/.
"git log --first-parent" traverses the first-parent chain, and "-m --stat"
shows the list of paths touched by commits including merge commits. We
could ask the question this way:
# What is the latest commit that touched that path?
$ git log --first-parent --oneline -m --stat master |
sed -e '/^ contrib\/fast-import\/git-p4 /q' | tail
The above finds that 8cbfc11 (Merge branch 'pw/p4-view-updates',
2012-01-06) was such a commit.
But a more natural way to spell this question is this:
$ git log --first-parent --oneline -m --stat -1 master -- \
contrib/fast-import/git-p4
Unfortunately, this does not work. It finds ecb7cf9 (git-p4: rewrite view
handling, 2012-01-02). This commit is a part of the merged topic branch
and is _not_ on the first-parent path from the 'master':
$ git show-branch 8cbfc11 ecb7cf9
! [8cbfc11] Merge branch 'pw/p4-view-updates'
! [ecb7cf9] git-p4: rewrite view handling
--
- [8cbfc11] Merge branch 'pw/p4-view-updates'
+ [8cbfc11^2] git-p4: view spec documentation
++ [ecb7cf9] git-p4: rewrite view handling
The problem is caused by the merge simplification logic when it inspects
the merge commit 8cbfc11. In this case, the history leading to the tip of
'master' did not touch git-p4 since 'pw/p4-view-updates' topic forked, and
the result of the merge is simply a copy from the tip of the topic branch
in the view limited by the given pathspec. The merge simplification logic
discards the history on the mainline side of the merge, and pretends as if
the sole parent of the merge is its second parent, i.e. the tip of the
topic. While this simplification is correct in the general case, it is at
least surprising if not outright wrong when the user explicitly asked to
show the first-parent history.
Here is an attempt to fix this issue, by not allowing us to compare the
merge result with anything but the first parent when --first-parent is in
effect, to avoid the history traversal veering off to the side branch.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2012-01-19 20:58:45 +01:00
|
|
|
check_result 'H' --first-parent -- another-file
|
2008-08-04 02:47:16 +02:00
|
|
|
|
2013-05-16 17:32:35 +02:00
|
|
|
check_result 'E C B A' --full-history E -- lost
|
|
|
|
test_expect_success 'full history simplification without parent' '
|
|
|
|
printf "%s\n" E C B A >expect &&
|
|
|
|
git log --pretty="$FMT" --full-history E -- lost |
|
|
|
|
unnote >actual &&
|
|
|
|
sed -e "s/^.* \([^ ]*\) .*/\1/" >check <actual &&
|
2015-03-20 11:07:52 +01:00
|
|
|
test_cmp expect check
|
2013-05-16 17:32:35 +02:00
|
|
|
'
|
|
|
|
|
log: use true parents for diff even when rewriting
When using pathspec filtering in combination with diff-based log
output, parent simplification happens before the diff is computed.
The diff is therefore against the *simplified* parents.
This works okay, arguably by accident, in the normal case:
simplification reduces to one parent as long as the commit is TREESAME
to it. So the simplified parent of any given commit must have the
same tree contents on the filtered paths as its true (unfiltered)
parent.
However, --full-diff breaks this guarantee, and indeed gives pretty
spectacular results when comparing the output of
git log --graph --stat ...
git log --graph --full-diff --stat ...
(--graph internally kicks in parent simplification, much like
--parents).
To fix it, store a copy of the parent list before simplification (in a
slab) whenever --full-diff is in effect. Then use the stored parents
instead of the simplified ones in the commit display code paths. The
latter do not actually check for --full-diff to avoid duplicated code;
they just grab the original parents if save_parents() has not been
called for this revision walk.
For ordinary commits it should be obvious that this is the right thing
to do.
Merge commits are a bit subtle. Observe that with default
simplification, merge simplification is an all-or-nothing decision:
either the merge is TREESAME to one parent and disappears, or it is
different from all parents and the parent list remains intact.
Redundant parents are not pruned, so the existing code also shows them
as a merge.
So if we do show a merge commit, the parent list just consists of the
rewrite result on each parent. Running, e.g., --cc on this in
--full-diff mode is not very useful: if any commits were skipped, some
hunks will disagree with all sides of the merge (with one side,
because commits were skipped; with the others, because they didn't
have those changes in the first place). This triggers --cc showing
these hunks spuriously.
Therefore I believe that even for merge commits it is better to show
the diffs wrt. the original parents.
Reported-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Ramsay Jones <ramsay@ramsay1.demon.co.uk>
Signed-off-by: Thomas Rast <trast@inf.ethz.ch>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-07-31 22:13:20 +02:00
|
|
|
test_expect_success '--full-diff is not affected by --parents' '
|
|
|
|
git log -p --pretty="%H" --full-diff -- file >expected &&
|
|
|
|
git log -p --pretty="%H" --full-diff --parents -- file >actual &&
|
|
|
|
test_cmp expected actual
|
|
|
|
'
|
|
|
|
|
2008-08-04 02:47:16 +02:00
|
|
|
test_done
|