2018-06-27 15:23:18 +08:00
|
|
|
#!/bin/sh
|
|
|
|
|
|
|
|
test_description='git rebase + directory rename tests'
|
|
|
|
|
|
|
|
. ./test-lib.sh
|
|
|
|
. "$TEST_DIRECTORY"/lib-rebase.sh
|
|
|
|
|
2018-08-29 15:06:11 +08:00
|
|
|
test_expect_success 'setup testcase where directory rename should be detected' '
|
2018-06-27 15:23:18 +08:00
|
|
|
test_create_repo dir-rename &&
|
|
|
|
(
|
|
|
|
cd dir-rename &&
|
|
|
|
|
|
|
|
mkdir x &&
|
|
|
|
test_seq 1 10 >x/a &&
|
|
|
|
test_seq 11 20 >x/b &&
|
|
|
|
test_seq 21 30 >x/c &&
|
|
|
|
test_write_lines a b c d e f g h i >l &&
|
|
|
|
git add x l &&
|
|
|
|
git commit -m "Initial" &&
|
|
|
|
|
|
|
|
git branch O &&
|
|
|
|
git branch A &&
|
|
|
|
git branch B &&
|
|
|
|
|
|
|
|
git checkout A &&
|
|
|
|
git mv x y &&
|
|
|
|
git mv l letters &&
|
|
|
|
git commit -m "Rename x to y, l to letters" &&
|
|
|
|
|
|
|
|
git checkout B &&
|
|
|
|
echo j >>l &&
|
|
|
|
test_seq 31 40 >x/d &&
|
|
|
|
git add l x/d &&
|
|
|
|
git commit -m "Modify l, add x/d"
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'rebase --interactive: directory rename detected' '
|
|
|
|
(
|
|
|
|
cd dir-rename &&
|
|
|
|
|
|
|
|
git checkout B^0 &&
|
|
|
|
|
|
|
|
set_fake_editor &&
|
merge-recursive: switch directory rename detection default
When all of x/a, x/b, and x/c have moved to z/a, z/b, and z/c on one
branch, there is a question about whether x/d added on a different
branch should remain at x/d or appear at z/d when the two branches are
merged. There are different possible viewpoints here:
A) The file was placed at x/d; it's unrelated to the other files in
x/ so it doesn't matter that all the files from x/ moved to z/ on
one branch; x/d should still remain at x/d.
B) x/d is related to the other files in x/, and x/ was renamed to z/;
therefore x/d should be moved to z/d.
Since there was no ability to detect directory renames prior to
git-2.18, users experienced (A) regardless of context. Choice (B) was
implemented in git-2.18, with no option to go back to (A), and has been
in use since. However, one user reported that the merge results did not
match their expectations, making the change of default problematic,
especially since there was no notice printed when directory rename
detection moved files.
Note that there is also a third possibility here:
C) There are different answers depending on the context and content
that cannot be determined by git, so this is a conflict. Use a
higher stage in the index to record the conflict and notify the
user of the potential issue instead of silently selecting a
resolution for them.
Add an option for users to specify their preference for whether to use
directory rename detection, and default to (C). Even when directory
rename detection is on, add notice messages about files moved into new
directories.
As a sidenote, x/d did not have to be a new file here; it could have
already existed at some other path and been renamed to x/d, with
directory rename detection just renaming it again to z/d. Thus, it's
not just new files, but also a modification to all rename types (normal
renames, rename/add, rename/delete, rename/rename(1to1),
rename/rename(1to2), and rename/rename(2to1)).
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-04-05 23:00:26 +08:00
|
|
|
FAKE_LINES="1" git -c merge.directoryRenames=true rebase --interactive A &&
|
2018-06-27 15:23:18 +08:00
|
|
|
|
|
|
|
git ls-files -s >out &&
|
|
|
|
test_line_count = 5 out &&
|
|
|
|
|
|
|
|
test_path_is_file y/d &&
|
|
|
|
test_path_is_missing x/d
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
2020-02-16 05:36:41 +08:00
|
|
|
test_expect_failure 'rebase --apply: directory rename detected' '
|
2018-06-27 15:23:18 +08:00
|
|
|
(
|
|
|
|
cd dir-rename &&
|
|
|
|
|
|
|
|
git checkout B^0 &&
|
|
|
|
|
2020-02-16 05:36:41 +08:00
|
|
|
git -c merge.directoryRenames=true rebase --apply A &&
|
2018-06-27 15:23:18 +08:00
|
|
|
|
|
|
|
git ls-files -s >out &&
|
|
|
|
test_line_count = 5 out &&
|
|
|
|
|
|
|
|
test_path_is_file y/d &&
|
|
|
|
test_path_is_missing x/d
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'rebase --merge: directory rename detected' '
|
|
|
|
(
|
|
|
|
cd dir-rename &&
|
|
|
|
|
|
|
|
git checkout B^0 &&
|
|
|
|
|
merge-recursive: switch directory rename detection default
When all of x/a, x/b, and x/c have moved to z/a, z/b, and z/c on one
branch, there is a question about whether x/d added on a different
branch should remain at x/d or appear at z/d when the two branches are
merged. There are different possible viewpoints here:
A) The file was placed at x/d; it's unrelated to the other files in
x/ so it doesn't matter that all the files from x/ moved to z/ on
one branch; x/d should still remain at x/d.
B) x/d is related to the other files in x/, and x/ was renamed to z/;
therefore x/d should be moved to z/d.
Since there was no ability to detect directory renames prior to
git-2.18, users experienced (A) regardless of context. Choice (B) was
implemented in git-2.18, with no option to go back to (A), and has been
in use since. However, one user reported that the merge results did not
match their expectations, making the change of default problematic,
especially since there was no notice printed when directory rename
detection moved files.
Note that there is also a third possibility here:
C) There are different answers depending on the context and content
that cannot be determined by git, so this is a conflict. Use a
higher stage in the index to record the conflict and notify the
user of the potential issue instead of silently selecting a
resolution for them.
Add an option for users to specify their preference for whether to use
directory rename detection, and default to (C). Even when directory
rename detection is on, add notice messages about files moved into new
directories.
As a sidenote, x/d did not have to be a new file here; it could have
already existed at some other path and been renamed to x/d, with
directory rename detection just renaming it again to z/d. Thus, it's
not just new files, but also a modification to all rename types (normal
renames, rename/add, rename/delete, rename/rename(1to1),
rename/rename(1to2), and rename/rename(2to1)).
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-04-05 23:00:26 +08:00
|
|
|
git -c merge.directoryRenames=true rebase --merge A &&
|
2018-06-27 15:23:18 +08:00
|
|
|
|
|
|
|
git ls-files -s >out &&
|
|
|
|
test_line_count = 5 out &&
|
|
|
|
|
|
|
|
test_path_is_file y/d &&
|
|
|
|
test_path_is_missing x/d
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_failure 'am: directory rename detected' '
|
|
|
|
(
|
|
|
|
cd dir-rename &&
|
|
|
|
|
|
|
|
git checkout A^0 &&
|
|
|
|
|
|
|
|
git format-patch -1 B &&
|
|
|
|
|
merge-recursive: switch directory rename detection default
When all of x/a, x/b, and x/c have moved to z/a, z/b, and z/c on one
branch, there is a question about whether x/d added on a different
branch should remain at x/d or appear at z/d when the two branches are
merged. There are different possible viewpoints here:
A) The file was placed at x/d; it's unrelated to the other files in
x/ so it doesn't matter that all the files from x/ moved to z/ on
one branch; x/d should still remain at x/d.
B) x/d is related to the other files in x/, and x/ was renamed to z/;
therefore x/d should be moved to z/d.
Since there was no ability to detect directory renames prior to
git-2.18, users experienced (A) regardless of context. Choice (B) was
implemented in git-2.18, with no option to go back to (A), and has been
in use since. However, one user reported that the merge results did not
match their expectations, making the change of default problematic,
especially since there was no notice printed when directory rename
detection moved files.
Note that there is also a third possibility here:
C) There are different answers depending on the context and content
that cannot be determined by git, so this is a conflict. Use a
higher stage in the index to record the conflict and notify the
user of the potential issue instead of silently selecting a
resolution for them.
Add an option for users to specify their preference for whether to use
directory rename detection, and default to (C). Even when directory
rename detection is on, add notice messages about files moved into new
directories.
As a sidenote, x/d did not have to be a new file here; it could have
already existed at some other path and been renamed to x/d, with
directory rename detection just renaming it again to z/d. Thus, it's
not just new files, but also a modification to all rename types (normal
renames, rename/add, rename/delete, rename/rename(1to1),
rename/rename(1to2), and rename/rename(2to1)).
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-04-05 23:00:26 +08:00
|
|
|
git -c merge.directoryRenames=true am --3way 0001*.patch &&
|
2018-06-27 15:23:18 +08:00
|
|
|
|
|
|
|
git ls-files -s >out &&
|
|
|
|
test_line_count = 5 out &&
|
|
|
|
|
|
|
|
test_path_is_file y/d &&
|
|
|
|
test_path_is_missing x/d
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
2018-08-29 15:06:11 +08:00
|
|
|
test_expect_success 'setup testcase where directory rename should NOT be detected' '
|
|
|
|
test_create_repo no-dir-rename &&
|
|
|
|
(
|
|
|
|
cd no-dir-rename &&
|
|
|
|
|
|
|
|
mkdir x &&
|
|
|
|
test_seq 1 10 >x/a &&
|
|
|
|
test_seq 11 20 >x/b &&
|
|
|
|
test_seq 21 30 >x/c &&
|
|
|
|
echo original >project_info &&
|
|
|
|
git add x project_info &&
|
|
|
|
git commit -m "Initial" &&
|
|
|
|
|
|
|
|
git branch O &&
|
|
|
|
git branch A &&
|
|
|
|
git branch B &&
|
|
|
|
|
|
|
|
git checkout A &&
|
|
|
|
echo v2 >project_info &&
|
|
|
|
git add project_info &&
|
|
|
|
git commit -m "Modify project_info" &&
|
|
|
|
|
|
|
|
git checkout B &&
|
|
|
|
mkdir y &&
|
|
|
|
git mv x/c y/c &&
|
|
|
|
echo v1 >project_info &&
|
|
|
|
git add project_info &&
|
|
|
|
git commit -m "Rename x/c to y/c, modify project_info"
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'rebase --interactive: NO directory rename' '
|
|
|
|
test_when_finished "git -C no-dir-rename rebase --abort" &&
|
|
|
|
(
|
|
|
|
cd no-dir-rename &&
|
|
|
|
|
|
|
|
git checkout B^0 &&
|
|
|
|
|
|
|
|
set_fake_editor &&
|
|
|
|
test_must_fail env FAKE_LINES="1" git rebase --interactive A &&
|
|
|
|
|
|
|
|
git ls-files -s >out &&
|
|
|
|
test_line_count = 6 out &&
|
|
|
|
|
|
|
|
test_path_is_file x/a &&
|
|
|
|
test_path_is_file x/b &&
|
|
|
|
test_path_is_missing x/c
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
am: avoid directory rename detection when calling recursive merge machinery
Let's say you have the following three trees, where Base is from one commit
behind either master or branch:
Base : bar_v1, foo/{file1, file2, file3}
branch: bar_v2, foo/{file1, file2}, goo/file3
master: bar_v3, foo/{file1, file2, file3}
Using git-am (or am-based rebase) to apply the changes from branch onto
master results in the following tree:
Result: bar_merged, goo/{file1, file2, file3}
This is not what users want; they did not rename foo/ -> goo/, they only
renamed one file within that directory. The reason this happens is am
constructs fake trees (via build_fake_ancestor()) of the following form:
Base_bfa : bar_v1, foo/file3
branch_bfa: bar_v2, goo/file3
Combining these two trees with master's tree:
master: bar_v3, foo/{file1, file2, file3},
You can see that merge_recursive_generic() would see branch_bfa as renaming
foo/ -> goo/, and master as just adding both foo/file1 and foo/file2. As
such, it ends up with goo/{file1, file2, file3}
The core problem is that am does not have access to the original trees; it
can only construct trees using the blobs involved in the patch. As such,
it is not safe to perform directory rename detection within am -3.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-29 15:06:13 +08:00
|
|
|
test_expect_success 'rebase (am): NO directory rename' '
|
2018-08-29 15:06:11 +08:00
|
|
|
test_when_finished "git -C no-dir-rename rebase --abort" &&
|
|
|
|
(
|
|
|
|
cd no-dir-rename &&
|
|
|
|
|
|
|
|
git checkout B^0 &&
|
|
|
|
|
|
|
|
set_fake_editor &&
|
|
|
|
test_must_fail git rebase A &&
|
|
|
|
|
|
|
|
git ls-files -s >out &&
|
|
|
|
test_line_count = 6 out &&
|
|
|
|
|
|
|
|
test_path_is_file x/a &&
|
|
|
|
test_path_is_file x/b &&
|
|
|
|
test_path_is_missing x/c
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'rebase --merge: NO directory rename' '
|
|
|
|
test_when_finished "git -C no-dir-rename rebase --abort" &&
|
|
|
|
(
|
|
|
|
cd no-dir-rename &&
|
|
|
|
|
|
|
|
git checkout B^0 &&
|
|
|
|
|
|
|
|
set_fake_editor &&
|
|
|
|
test_must_fail git rebase --merge A &&
|
|
|
|
|
|
|
|
git ls-files -s >out &&
|
|
|
|
test_line_count = 6 out &&
|
|
|
|
|
|
|
|
test_path_is_file x/a &&
|
|
|
|
test_path_is_file x/b &&
|
|
|
|
test_path_is_missing x/c
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
am: avoid directory rename detection when calling recursive merge machinery
Let's say you have the following three trees, where Base is from one commit
behind either master or branch:
Base : bar_v1, foo/{file1, file2, file3}
branch: bar_v2, foo/{file1, file2}, goo/file3
master: bar_v3, foo/{file1, file2, file3}
Using git-am (or am-based rebase) to apply the changes from branch onto
master results in the following tree:
Result: bar_merged, goo/{file1, file2, file3}
This is not what users want; they did not rename foo/ -> goo/, they only
renamed one file within that directory. The reason this happens is am
constructs fake trees (via build_fake_ancestor()) of the following form:
Base_bfa : bar_v1, foo/file3
branch_bfa: bar_v2, goo/file3
Combining these two trees with master's tree:
master: bar_v3, foo/{file1, file2, file3},
You can see that merge_recursive_generic() would see branch_bfa as renaming
foo/ -> goo/, and master as just adding both foo/file1 and foo/file2. As
such, it ends up with goo/{file1, file2, file3}
The core problem is that am does not have access to the original trees; it
can only construct trees using the blobs involved in the patch. As such,
it is not safe to perform directory rename detection within am -3.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-29 15:06:13 +08:00
|
|
|
test_expect_success 'am: NO directory rename' '
|
2018-08-29 15:06:11 +08:00
|
|
|
test_when_finished "git -C no-dir-rename am --abort" &&
|
|
|
|
(
|
|
|
|
cd no-dir-rename &&
|
|
|
|
|
|
|
|
git checkout A^0 &&
|
|
|
|
|
|
|
|
git format-patch -1 B &&
|
|
|
|
|
|
|
|
test_must_fail git am --3way 0001*.patch &&
|
|
|
|
|
|
|
|
git ls-files -s >out &&
|
|
|
|
test_line_count = 6 out &&
|
|
|
|
|
|
|
|
test_path_is_file x/a &&
|
|
|
|
test_path_is_file x/b &&
|
|
|
|
test_path_is_missing x/c
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
2018-06-27 15:23:18 +08:00
|
|
|
test_done
|