Browse Source
* jn/merge-renormalize: merge-recursive --renormalize rerere: never renormalize rerere: migrate to parse-options API t4200 (rerere): modernize style ll-merge: let caller decide whether to renormalize ll-merge: make flag easier to populate Documentation/technical: document ll_merge merge-trees: let caller decide whether to renormalize merge-trees: push choice to renormalize away from low level t6038 (merge.renormalize): check that it can be turned off t6038 (merge.renormalize): try checkout -m and cherry-pick t6038 (merge.renormalize): style nitpicks Don't expand CRLFs when normalizing text during merge Try normalizing files to avoid delete/modify conflicts when merging Avoid conflicts when merging branches with mixed normalization Conflicts: builtin/rerere.c t/t4200-rerere.shmaint
Junio C Hamano
15 years ago
18 changed files with 785 additions and 170 deletions
@ -0,0 +1,73 @@ |
|||||||
|
merge API |
||||||
|
========= |
||||||
|
|
||||||
|
The merge API helps a program to reconcile two competing sets of |
||||||
|
improvements to some files (e.g., unregistered changes from the work |
||||||
|
tree versus changes involved in switching to a new branch), reporting |
||||||
|
conflicts if found. The library called through this API is |
||||||
|
responsible for a few things. |
||||||
|
|
||||||
|
* determining which trees to merge (recursive ancestor consolidation); |
||||||
|
|
||||||
|
* lining up corresponding files in the trees to be merged (rename |
||||||
|
detection, subtree shifting), reporting edge cases like add/add |
||||||
|
and rename/rename conflicts to the user; |
||||||
|
|
||||||
|
* performing a three-way merge of corresponding files, taking |
||||||
|
path-specific merge drivers (specified in `.gitattributes`) |
||||||
|
into account. |
||||||
|
|
||||||
|
Low-level (single file) merge |
||||||
|
----------------------------- |
||||||
|
|
||||||
|
`ll_merge`:: |
||||||
|
|
||||||
|
Perform a three-way single-file merge in core. This is |
||||||
|
a thin wrapper around `xdl_merge` that takes the path and |
||||||
|
any merge backend specified in `.gitattributes` or |
||||||
|
`.git/info/attributes` into account. Returns 0 for a |
||||||
|
clean merge. |
||||||
|
|
||||||
|
The caller: |
||||||
|
|
||||||
|
1. allocates an mmbuffer_t variable for the result; |
||||||
|
2. allocates and fills variables with the file's original content |
||||||
|
and two modified versions (using `read_mmfile`, for example); |
||||||
|
3. calls ll_merge(); |
||||||
|
4. reads the output from result_buf.ptr and result_buf.size; |
||||||
|
5. releases buffers when finished (free(ancestor.ptr); free(ours.ptr); |
||||||
|
free(theirs.ptr); free(result_buf.ptr);). |
||||||
|
|
||||||
|
If the modifications do not merge cleanly, `ll_merge` will return a |
||||||
|
nonzero value and `result_buf` will generally include a description of |
||||||
|
the conflict bracketed by markers such as the traditional `<<<<<<<` |
||||||
|
and `>>>>>>>`. |
||||||
|
|
||||||
|
The `ancestor_label`, `our_label`, and `their_label` parameters are |
||||||
|
used to label the different sides of a conflict if the merge driver |
||||||
|
supports this. |
||||||
|
|
||||||
|
The `flag` parameter is a bitfield: |
||||||
|
|
||||||
|
- The `LL_OPT_VIRTUAL_ANCESTOR` bit indicates whether this is an |
||||||
|
internal merge to consolidate ancestors for a recursive merge. |
||||||
|
|
||||||
|
- The `LL_OPT_FAVOR_MASK` bits allow local conflicts to be automatically |
||||||
|
resolved in favor of one side or the other (as in 'git merge-file' |
||||||
|
`--ours`/`--theirs`/`--union`). |
||||||
|
They can be populated by `create_ll_flag`, whose argument can be |
||||||
|
`XDL_MERGE_FAVOR_OURS`, `XDL_MERGE_FAVOR_THEIRS`, or |
||||||
|
`XDL_MERGE_FAVOR_UNION`. |
||||||
|
|
||||||
|
Everything else |
||||||
|
--------------- |
||||||
|
|
||||||
|
Talk about <merge-recursive.h> and merge_file(): |
||||||
|
|
||||||
|
- merge_trees() to merge with rename detection |
||||||
|
- merge_recursive() for ancestor consolidation |
||||||
|
- try_merge_command() for other strategies |
||||||
|
- conflict format |
||||||
|
- merge options |
||||||
|
|
||||||
|
(Daniel, Miklos, Stephan, JC) |
@ -0,0 +1,189 @@ |
|||||||
|
#!/bin/sh |
||||||
|
|
||||||
|
test_description='CRLF merge conflict across text=auto change |
||||||
|
|
||||||
|
* [master] remove .gitattributes |
||||||
|
! [side] add line from b |
||||||
|
-- |
||||||
|
+ [side] add line from b |
||||||
|
* [master] remove .gitattributes |
||||||
|
* [master^] add line from a |
||||||
|
* [master~2] normalize file |
||||||
|
*+ [side^] Initial |
||||||
|
' |
||||||
|
|
||||||
|
. ./test-lib.sh |
||||||
|
|
||||||
|
test_expect_success setup ' |
||||||
|
git config core.autocrlf false && |
||||||
|
|
||||||
|
echo first line | append_cr >file && |
||||||
|
echo first line >control_file && |
||||||
|
echo only line >inert_file && |
||||||
|
|
||||||
|
git add file control_file inert_file && |
||||||
|
test_tick && |
||||||
|
git commit -m "Initial" && |
||||||
|
git tag initial && |
||||||
|
git branch side && |
||||||
|
|
||||||
|
echo "* text=auto" >.gitattributes && |
||||||
|
touch file && |
||||||
|
git add .gitattributes file && |
||||||
|
test_tick && |
||||||
|
git commit -m "normalize file" && |
||||||
|
|
||||||
|
echo same line | append_cr >>file && |
||||||
|
echo same line >>control_file && |
||||||
|
git add file control_file && |
||||||
|
test_tick && |
||||||
|
git commit -m "add line from a" && |
||||||
|
git tag a && |
||||||
|
|
||||||
|
git rm .gitattributes && |
||||||
|
rm file && |
||||||
|
git checkout file && |
||||||
|
test_tick && |
||||||
|
git commit -m "remove .gitattributes" && |
||||||
|
git tag c && |
||||||
|
|
||||||
|
git checkout side && |
||||||
|
echo same line | append_cr >>file && |
||||||
|
echo same line >>control_file && |
||||||
|
git add file control_file && |
||||||
|
test_tick && |
||||||
|
git commit -m "add line from b" && |
||||||
|
git tag b && |
||||||
|
|
||||||
|
git checkout master |
||||||
|
' |
||||||
|
|
||||||
|
test_expect_success 'set up fuzz_conflict() helper' ' |
||||||
|
fuzz_conflict() { |
||||||
|
sed -e "s/^\([<>=]......\) .*/\1/" "$@" |
||||||
|
} |
||||||
|
' |
||||||
|
|
||||||
|
test_expect_success 'Merge after setting text=auto' ' |
||||||
|
cat <<-\EOF >expected && |
||||||
|
first line |
||||||
|
same line |
||||||
|
EOF |
||||||
|
|
||||||
|
git config merge.renormalize true && |
||||||
|
git rm -fr . && |
||||||
|
rm -f .gitattributes && |
||||||
|
git reset --hard a && |
||||||
|
git merge b && |
||||||
|
test_cmp expected file |
||||||
|
' |
||||||
|
|
||||||
|
test_expect_success 'Merge addition of text=auto' ' |
||||||
|
cat <<-\EOF >expected && |
||||||
|
first line |
||||||
|
same line |
||||||
|
EOF |
||||||
|
|
||||||
|
git config merge.renormalize true && |
||||||
|
git rm -fr . && |
||||||
|
rm -f .gitattributes && |
||||||
|
git reset --hard b && |
||||||
|
git merge a && |
||||||
|
test_cmp expected file |
||||||
|
' |
||||||
|
|
||||||
|
test_expect_success 'Detect CRLF/LF conflict after setting text=auto' ' |
||||||
|
q_to_cr <<-\EOF >expected && |
||||||
|
<<<<<<< |
||||||
|
first line |
||||||
|
same line |
||||||
|
======= |
||||||
|
first lineQ |
||||||
|
same lineQ |
||||||
|
>>>>>>> |
||||||
|
EOF |
||||||
|
|
||||||
|
git config merge.renormalize false && |
||||||
|
rm -f .gitattributes && |
||||||
|
git reset --hard a && |
||||||
|
test_must_fail git merge b && |
||||||
|
fuzz_conflict file >file.fuzzy && |
||||||
|
test_cmp expected file.fuzzy |
||||||
|
' |
||||||
|
|
||||||
|
test_expect_success 'Detect LF/CRLF conflict from addition of text=auto' ' |
||||||
|
q_to_cr <<-\EOF >expected && |
||||||
|
<<<<<<< |
||||||
|
first lineQ |
||||||
|
same lineQ |
||||||
|
======= |
||||||
|
first line |
||||||
|
same line |
||||||
|
>>>>>>> |
||||||
|
EOF |
||||||
|
|
||||||
|
git config merge.renormalize false && |
||||||
|
rm -f .gitattributes && |
||||||
|
git reset --hard b && |
||||||
|
test_must_fail git merge a && |
||||||
|
fuzz_conflict file >file.fuzzy && |
||||||
|
test_cmp expected file.fuzzy |
||||||
|
' |
||||||
|
|
||||||
|
test_expect_failure 'checkout -m after setting text=auto' ' |
||||||
|
cat <<-\EOF >expected && |
||||||
|
first line |
||||||
|
same line |
||||||
|
EOF |
||||||
|
|
||||||
|
git config merge.renormalize true && |
||||||
|
git rm -fr . && |
||||||
|
rm -f .gitattributes && |
||||||
|
git reset --hard initial && |
||||||
|
git checkout a -- . && |
||||||
|
git checkout -m b && |
||||||
|
test_cmp expected file |
||||||
|
' |
||||||
|
|
||||||
|
test_expect_failure 'checkout -m addition of text=auto' ' |
||||||
|
cat <<-\EOF >expected && |
||||||
|
first line |
||||||
|
same line |
||||||
|
EOF |
||||||
|
|
||||||
|
git config merge.renormalize true && |
||||||
|
git rm -fr . && |
||||||
|
rm -f .gitattributes file && |
||||||
|
git reset --hard initial && |
||||||
|
git checkout b -- . && |
||||||
|
git checkout -m a && |
||||||
|
test_cmp expected file |
||||||
|
' |
||||||
|
|
||||||
|
test_expect_failure 'cherry-pick patch from after text=auto was added' ' |
||||||
|
append_cr <<-\EOF >expected && |
||||||
|
first line |
||||||
|
same line |
||||||
|
EOF |
||||||
|
|
||||||
|
git config merge.renormalize true && |
||||||
|
git rm -fr . && |
||||||
|
git reset --hard b && |
||||||
|
test_must_fail git cherry-pick a >err 2>&1 && |
||||||
|
grep "[Nn]othing added" err && |
||||||
|
test_cmp expected file |
||||||
|
' |
||||||
|
|
||||||
|
test_expect_success 'Test delete/normalize conflict' ' |
||||||
|
git checkout -f side && |
||||||
|
git rm -fr . && |
||||||
|
rm -f .gitattributes && |
||||||
|
git reset --hard initial && |
||||||
|
git rm file && |
||||||
|
git commit -m "remove file" && |
||||||
|
git checkout master && |
||||||
|
git reset --hard a^ && |
||||||
|
git merge side |
||||||
|
' |
||||||
|
|
||||||
|
test_done |
Loading…
Reference in new issue