git/t/t6409-merge-subtree.sh

153 строки
3.6 KiB
Bash
Исходник Обычный вид История

#!/bin/sh
test_description='subtree merge strategy'
. ./test-lib.sh
test_expect_success setup '
s="1 2 3 4 5 6 7 8" &&
for i in $s; do echo $i; done >hello &&
git add hello &&
git commit -m initial &&
git checkout -b side &&
echo >>hello world &&
git add hello &&
git commit -m second &&
git checkout master &&
for i in mundo $s; do echo $i; done >hello &&
git add hello &&
git commit -m master
'
test_expect_success 'subtree available and works like recursive' '
git merge -s subtree side &&
for i in mundo $s world; do echo $i; done >expect &&
test_cmp expect hello
'
score_trees(): fix iteration over trees with missing entries In score_trees(), we walk over two sorted trees to find which entries are missing or have different content between the two. So if we have two trees with these entries: one two --- --- a a b c c d we'd expect the loop to: - compare "a" to "a" - compare "b" to "c"; because these are sorted lists, we know that the second tree does not have "b" - compare "c" to "c" - compare "d" to end-of-list; we know that the first tree does not have "d" And prior to d8febde370 (match-trees: simplify score_trees() using tree_entry(), 2013-03-24) that worked. But after that commit, we mistakenly increment the tree pointers for every loop iteration, even when we've processed the entry for only one side. As a result, we end up doing this: - compare "a" to "a" - compare "b" to "c"; we know that we do not have "b", but we still increment both tree pointers; at this point we're out of sync and all further comparisons are wrong - compare "c" to "d" and mistakenly claim that the second tree does not have "c" - exit the loop, mistakenly not realizing that the first tree does not have "d" So contrary to the claim in d8febde370, we really do need to manually use update_tree_entry(), because advancing the tree pointer depends on the entry comparison. That means we must stop using tree_entry() to access each entry, since it auto-advances the pointer. Instead: - we'll use tree_desc.size directly to know if there's anything left to look at (which is what tree_entry() was doing under the hood) - rather than do an extra struct assignment to "e1" and "e2", we can just access the "entry" field of tree_desc directly That makes us a little more intimate with the tree_desc code, but that's not uncommon for its callers. The included test shows off the bug by adding a new entry "bar.t", which sorts early in the tree and de-syncs the comparison for "foo.t", which comes after. Reported-by: George Shammas <georgyo@gmail.com> Helped-by: René Scharfe <l.s.r@web.de> Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-02 21:58:21 +03:00
test_expect_success 'setup branch sub' '
git checkout --orphan sub &&
git rm -rf . &&
test_commit foo
'
test_expect_success 'setup topic branch' '
git checkout -b topic master &&
score_trees(): fix iteration over trees with missing entries In score_trees(), we walk over two sorted trees to find which entries are missing or have different content between the two. So if we have two trees with these entries: one two --- --- a a b c c d we'd expect the loop to: - compare "a" to "a" - compare "b" to "c"; because these are sorted lists, we know that the second tree does not have "b" - compare "c" to "c" - compare "d" to end-of-list; we know that the first tree does not have "d" And prior to d8febde370 (match-trees: simplify score_trees() using tree_entry(), 2013-03-24) that worked. But after that commit, we mistakenly increment the tree pointers for every loop iteration, even when we've processed the entry for only one side. As a result, we end up doing this: - compare "a" to "a" - compare "b" to "c"; we know that we do not have "b", but we still increment both tree pointers; at this point we're out of sync and all further comparisons are wrong - compare "c" to "d" and mistakenly claim that the second tree does not have "c" - exit the loop, mistakenly not realizing that the first tree does not have "d" So contrary to the claim in d8febde370, we really do need to manually use update_tree_entry(), because advancing the tree pointer depends on the entry comparison. That means we must stop using tree_entry() to access each entry, since it auto-advances the pointer. Instead: - we'll use tree_desc.size directly to know if there's anything left to look at (which is what tree_entry() was doing under the hood) - rather than do an extra struct assignment to "e1" and "e2", we can just access the "entry" field of tree_desc directly That makes us a little more intimate with the tree_desc code, but that's not uncommon for its callers. The included test shows off the bug by adding a new entry "bar.t", which sorts early in the tree and de-syncs the comparison for "foo.t", which comes after. Reported-by: George Shammas <georgyo@gmail.com> Helped-by: René Scharfe <l.s.r@web.de> Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-02 21:58:21 +03:00
git merge -s ours --no-commit --allow-unrelated-histories sub &&
git read-tree --prefix=dir/ -u sub &&
git commit -m "initial merge of sub into topic" &&
score_trees(): fix iteration over trees with missing entries In score_trees(), we walk over two sorted trees to find which entries are missing or have different content between the two. So if we have two trees with these entries: one two --- --- a a b c c d we'd expect the loop to: - compare "a" to "a" - compare "b" to "c"; because these are sorted lists, we know that the second tree does not have "b" - compare "c" to "c" - compare "d" to end-of-list; we know that the first tree does not have "d" And prior to d8febde370 (match-trees: simplify score_trees() using tree_entry(), 2013-03-24) that worked. But after that commit, we mistakenly increment the tree pointers for every loop iteration, even when we've processed the entry for only one side. As a result, we end up doing this: - compare "a" to "a" - compare "b" to "c"; we know that we do not have "b", but we still increment both tree pointers; at this point we're out of sync and all further comparisons are wrong - compare "c" to "d" and mistakenly claim that the second tree does not have "c" - exit the loop, mistakenly not realizing that the first tree does not have "d" So contrary to the claim in d8febde370, we really do need to manually use update_tree_entry(), because advancing the tree pointer depends on the entry comparison. That means we must stop using tree_entry() to access each entry, since it auto-advances the pointer. Instead: - we'll use tree_desc.size directly to know if there's anything left to look at (which is what tree_entry() was doing under the hood) - rather than do an extra struct assignment to "e1" and "e2", we can just access the "entry" field of tree_desc directly That makes us a little more intimate with the tree_desc code, but that's not uncommon for its callers. The included test shows off the bug by adding a new entry "bar.t", which sorts early in the tree and de-syncs the comparison for "foo.t", which comes after. Reported-by: George Shammas <georgyo@gmail.com> Helped-by: René Scharfe <l.s.r@web.de> Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-02 21:58:21 +03:00
test_path_is_file dir/foo.t &&
test_path_is_file hello
'
test_expect_success 'update branch sub' '
git checkout sub &&
test_commit bar
'
test_expect_success 'update topic branch' '
git checkout topic &&
git merge -s subtree sub -m "second merge of sub into topic" &&
score_trees(): fix iteration over trees with missing entries In score_trees(), we walk over two sorted trees to find which entries are missing or have different content between the two. So if we have two trees with these entries: one two --- --- a a b c c d we'd expect the loop to: - compare "a" to "a" - compare "b" to "c"; because these are sorted lists, we know that the second tree does not have "b" - compare "c" to "c" - compare "d" to end-of-list; we know that the first tree does not have "d" And prior to d8febde370 (match-trees: simplify score_trees() using tree_entry(), 2013-03-24) that worked. But after that commit, we mistakenly increment the tree pointers for every loop iteration, even when we've processed the entry for only one side. As a result, we end up doing this: - compare "a" to "a" - compare "b" to "c"; we know that we do not have "b", but we still increment both tree pointers; at this point we're out of sync and all further comparisons are wrong - compare "c" to "d" and mistakenly claim that the second tree does not have "c" - exit the loop, mistakenly not realizing that the first tree does not have "d" So contrary to the claim in d8febde370, we really do need to manually use update_tree_entry(), because advancing the tree pointer depends on the entry comparison. That means we must stop using tree_entry() to access each entry, since it auto-advances the pointer. Instead: - we'll use tree_desc.size directly to know if there's anything left to look at (which is what tree_entry() was doing under the hood) - rather than do an extra struct assignment to "e1" and "e2", we can just access the "entry" field of tree_desc directly That makes us a little more intimate with the tree_desc code, but that's not uncommon for its callers. The included test shows off the bug by adding a new entry "bar.t", which sorts early in the tree and de-syncs the comparison for "foo.t", which comes after. Reported-by: George Shammas <georgyo@gmail.com> Helped-by: René Scharfe <l.s.r@web.de> Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-02 21:58:21 +03:00
test_path_is_file dir/bar.t &&
test_path_is_file dir/foo.t &&
test_path_is_file hello
'
test_expect_success 'setup' '
mkdir git-gui &&
cd git-gui &&
git init &&
echo git-gui > git-gui.sh &&
o1=$(git hash-object git-gui.sh) &&
git add git-gui.sh &&
git commit -m "initial git-gui" &&
cd .. &&
mkdir git &&
cd git &&
git init &&
echo git >git.c &&
o2=$(git hash-object git.c) &&
git add git.c &&
git commit -m "initial git"
'
test_expect_success 'initial merge' '
git remote add -f gui ../git-gui &&
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 23:21:09 +03:00
git merge -s ours --no-commit --allow-unrelated-histories gui/master &&
git read-tree --prefix=git-gui/ -u gui/master &&
git commit -m "Merge git-gui as our subdirectory" &&
git checkout -b work &&
git ls-files -s >actual &&
(
echo "100644 $o1 0 git-gui/git-gui.sh" &&
echo "100644 $o2 0 git.c"
) >expected &&
test_cmp expected actual
'
test_expect_success 'merge update' '
cd ../git-gui &&
echo git-gui2 > git-gui.sh &&
o3=$(git hash-object git-gui.sh) &&
git add git-gui.sh &&
git checkout -b topic_2 &&
git commit -m "update git-gui" &&
cd ../git &&
git pull -s subtree gui topic_2 &&
git ls-files -s >actual &&
(
echo "100644 $o3 0 git-gui/git-gui.sh" &&
echo "100644 $o2 0 git.c"
) >expected &&
test_cmp expected actual
'
test_expect_success 'initial ambiguous subtree' '
cd ../git &&
git reset --hard master &&
git checkout -b topic_2 &&
git merge -s ours --no-commit gui/master &&
git read-tree --prefix=git-gui2/ -u gui/master &&
git commit -m "Merge git-gui2 as our subdirectory" &&
git checkout -b work2 &&
git ls-files -s >actual &&
(
echo "100644 $o1 0 git-gui/git-gui.sh" &&
echo "100644 $o1 0 git-gui2/git-gui.sh" &&
echo "100644 $o2 0 git.c"
) >expected &&
test_cmp expected actual
'
test_expect_success 'merge using explicit' '
cd ../git &&
git reset --hard topic_2 &&
git pull -Xsubtree=git-gui gui topic_2 &&
git ls-files -s >actual &&
(
echo "100644 $o3 0 git-gui/git-gui.sh" &&
echo "100644 $o1 0 git-gui2/git-gui.sh" &&
echo "100644 $o2 0 git.c"
) >expected &&
test_cmp expected actual
'
test_expect_success 'merge2 using explicit' '
cd ../git &&
git reset --hard topic_2 &&
git pull -Xsubtree=git-gui2 gui topic_2 &&
git ls-files -s >actual &&
(
echo "100644 $o1 0 git-gui/git-gui.sh" &&
echo "100644 $o3 0 git-gui2/git-gui.sh" &&
echo "100644 $o2 0 git.c"
) >expected &&
test_cmp expected actual
'
test_done