Viewing files at commit b76ab8b42dfdCommit detailsView latest

suites/merge/vendor/gitoxide/tree-baseline.sh 3441 lines · 115.1 KB · bash
1
#!/usr/bin/env bash
2
set -eu -o pipefail
3
4
function tick () {
5
  if test -z "${tick+set}"
6
  then
7
    tick=1112911993
8
  else
9
    tick=$(($tick + 60))
10
  fi
11
  GIT_COMMITTER_DATE="$tick -0700"
12
  GIT_AUTHOR_DATE="$tick -0700"
13
  export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
14
}
15
16
# Translate a historical SHA-1 object id (as hard-coded in the `git update-index
17
# --index-info` blocks below) to the equivalent id under the repository's
18
# current hash algorithm, so these baselines work under both SHA-1 and SHA-256.
19
function oid () {
20
  case "${GIT_DEFAULT_HASH:-sha1}" in
21
  sha1)
22
    printf '%s' "$1"
23
    return
24
    ;;
25
  sha256)
26
    ;;
27
  *)
28
    echo "oid(): unsupported object hash '${GIT_DEFAULT_HASH}', expected 'sha1' or 'sha256'" >&2
29
    return 1
30
    ;;
31
  esac
32
  case "$1" in
33
    092bfb9bdf74dd8cfd22e812151281ee9aa6f01a) echo d246bf89e9d0f356301a75184ab79e68cfcb0aac1a7acbf3d4de81400b5ee231 ;;
34
    09c277aa66897c58157f57a374eacc63a407dcab) echo cf8aedf347c947abf6c6e2c4ecaa2e086ea61ce81f52491c453b81c6455f305e ;;
35
    0a6a0ba83635bc00e7c79a4b5b6e50381385c1af) echo be45c0023fb9c5e05387235ef07be7e7e2034838216cd24cca06840cc01bc771 ;;
36
    0cde534c2fca6c92c07b3e7a696665e844b9b933) echo cc0d90a1291b62ffbf9288fad4e290e3a85160cef42c708be3b8a90ac13d501b ;;
37
    19102815663d23f8b75a47e7a01965dcdc96468c) echo a65ca4376b51e98097fec3a009b7e5adce3255aecaa1a6da7b1cdc92b90e025e ;;
38
    1a2664a9924754c698e323f756f9f87f3f2fb337) echo cff0bb6fba5633bd804f0a15fed6329ede0a9dfc4a7f53e31581f3d1a2005e0f ;;
39
    257cc5642cb1a054f08cc83f2d943e56fd3ebe99) echo 47d6aca82756ff2e61e53520bfdf1faa6c86d933be4854eb34840c57d12e0c85 ;;
40
    4178ea6795c4c3e07b4e17e6a04aa49584b07ecd) echo 60becdf11d1fcac5bdafbebf1f3e5c7dec1463f8381bcc8bfb533262ec3b1fa8 ;;
41
    44065282f89b9bd6439ed2e4674721383fd987eb) echo 6632209c4f376d0d893dcb49cd85f0abf9fb1bb0f79c2c6c998adef4d95cac95 ;;
42
    45b983be36b73c0788dc9cbcb76cbb80fc7bb057) echo 96c18f0297e38d01f4b2dacddea4259aea6b2961eb0822bd2c0c3f6029030045 ;;
43
    4b5599c7c2ed4390417d9699bec86144a386873d) echo 54b65f98d2bdee38cacc335639121482f497d71b6c9874002b637173f2cc212b ;;
44
    542802a799ded74fa01c47ba2f8925e284a369e2) echo 863a67a88f3ae07c9c4784fbed16d4f8eabea50aa40923804f7435c45c4bcb1e ;;
45
    5716ca5987cbf97d6bb54920bea6adde242d87e6) echo a52e146ac2ab2d0efbb768ab8ebd1e98a6055764c81fe424fbae4522f5b4cb92 ;;
46
    61780798228d17af2d34fce4cfbdf35556832472) echo 9b69d308c97f2c5933fdd0e8ce04acce91c09cb969e36a1f86756fc5a5d3323a ;;
47
    64012489f118cb4011c8902b4a635f70dcb0c0ca) echo 24bba3b983b179b8ceb5b7af0995c745dbe07f36b75e593b28e000c7d26aa1e5 ;;
48
    65bc6a1e238f4bf05b28fd05240636e2cfb657e0) echo 315347e306b9d8222324e6f48a1f585074712267eae89d34ef607663046cb3e3 ;;
49
    78981922613b2afb6025042ff6bd878ac1994e85) echo f8625e43f9e04f24291f77cdbe4c71b3c2a3b0003f60419b3ed06a058d766c8b ;;
50
    8a1218a1024a212bb3db30becd860315f9f3ac52) echo 6841122c240e69074e82e38506c1fcf806c3ee2469673392a9ed7650d7a6d51d ;;
51
    9dc97bdc2426e68423360e3e5299280b2cf6b8ff) echo 7108d070fd9fde47d46504102c6935d9251cce816fdbdcc6c1d56710f11790d1 ;;
52
    a4ae6e4709228b5da6001cb9d1cfa7736851e2a6) echo c3ae6188533b9bea9ad1049892c6b5488fffe5cfc518b30ba21b64a124bc7afe ;;
53
    b414108e81e5091fe0974a1858b4d0d22b107f70) echo f5f52958fe19d6073227d52208826e0d189bfdb9d0c428984244075155e57c71 ;;
54
    d0549c3d3c96a464289f3b820b7d96aedc58924b) echo 7960eaf2c5f3ac192aa208323a78bf4b0f2fb21d79813af79a78674698689442 ;;
55
    d5f7fc3f74f7dec08280f370a975b112e8f60818) echo ffc23e0956239fed93c95c4cf3d3152a887825f756309251d71bb16f261afabd ;;
56
    df967b96a579e45a18b8251732d16804b2e56a55) echo abed979e3cd3667c5a295c2641f8319f950860c65cc168eebd1571c51bb4f6fc ;;
57
    e29fa63dae4ccf0788897a7025da868083178fdf) echo 577603f537e595c5324b5987646cb9073d159a4f0c9e73f820d2ee83c8926acf ;;
58
    e33f5e94470d3b5fa0220ff6a9cabb78a3f72fa3) echo 72a7259eef0664ef8dae2039c11c61ec4dd039f94eee87a21c6ee1c637b7cca0 ;;
59
    e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) echo 473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813 ;;
60
    ea28dcd7f627a2a7bbd09daa679c452180617c9f) echo efd6b0b052df02752b7f371021660541dcb8f2b0665c19b672b031af7c086588 ;;
61
    f286e5cdd97ac6895438ea4548638bb98ac9bd6b) echo 33ba40b57f32a1383518de4f91d9eaf8bf5eb55e318f47215af5c367f9778b9b ;;
62
    f2ad6c76f0115a6ba5b00456a849810e7ec0af20) echo 2abe107e3b1b618efafa0df5e5f1118e5bf86694eb8c185741e67795ae314aa4 ;;
63
    f801a62deed900f8a80ff35e3339474ad6352a93) echo 970d16592cccbb8ce3846be7a74ecb94bc3bf8d01bec1eeb15a57380aae5d5a3 ;;
64
    f89a08d1e226b9a319210641b63b07dcf0bd705f) echo 43d8308eb726995b8b6a3f826252dd21dc35d01386931b8eea4125723e36cc83 ;;
65
    fa49b077972391ad58037050f2a75f74e3671e92) echo 6d5fd291bb0f67444e99ab492f1bf1fcdf5dca09dab24cf331e05111b4cfc1a3 ;;
66
    # gitlinks: submodule 'sub' commits (sub-root, sub-a, sub-b), deterministic from the fixed tick dates
67
    e835c0c403c8e494c0ca98f3d25d0b8464c18d38) echo ed2d65b7fe3c3f869a21cfec7f54867db6c8eb2cc9b0bf671522d87599704d70 ;;
68
    64466ebdff775ad618d9cc993cf52840e0af528c) echo df5d889015388598b724a741b53437e4dee9d67a5dbe0fa9f361fedd4c9ac6e8 ;;
69
    ea6eb701e03c2497915c25a851f3da8f8e362ca0) echo c79d38d6c8161bf2aec16032f82127c7ac237eeab69e1490422c93b5c314ce9c ;;
70
    *) echo "oid(): no SHA-256 mapping for '$1'" >&2; return 1 ;;
71
  esac
72
}
73
74
function write_lines () {
75
	printf "%s\n" "$@"
76
}
77
78
function seq () {
79
	case $# in
80
	1)	set 1 "$@" ;;
81
	2)	;;
82
	*)	{ echo "need 1 or 2 parameters: <end> or <start> <end>" 1>&2 && exit 2; } ;;
83
	esac
84
	local seq_counter=$1
85
	while test "$seq_counter" -le "$2"
86
	do
87
		echo "$seq_counter"
88
		seq_counter=$(( seq_counter + 1 ))
89
	done
90
}
91
92
function make_conflict_index() {
93
  local identifier=${1:?The first argument is the name of the parent directory along with the output name}
94
  cp .git/index .git/"${identifier}".index
95
}
96
97
function make_resolve_tree() {
98
  local resolve=${1:?Their 'ancestor' or 'ours'}
99
  local our_side=${2:-}
100
  local their_side=${3:-}
101
102
  local filename="resolve-${our_side}-${their_side}-with-${resolve}"
103
  git write-tree > ".git/${filename}.tree"
104
}
105
106
function baseline () (
107
  local dir=${1:?the directory to enter}
108
  local output_name=${2:?the basename of the output of the merge}
109
  local our_committish=${3:?our side from which a commit can be derived}
110
  local their_committish=${4:?Their side from which a commit can be derived}
111
  local opt_deviation_message=${5:-}
112
  local one_side=${6:-}
113
114
  cd "$dir"
115
  local our_commit_id
116
  local their_commit_id
117
118
  local conflict_style="merge"
119
   if [[ "$output_name" == *-merge ]]; then
120
       conflict_style="merge"
121
   elif [[ "$output_name" == *-diff3 ]]; then
122
       conflict_style="diff3"
123
   fi
124
125
  our_commit_id="$(git rev-parse "$our_committish")"
126
  their_commit_id="$(git rev-parse "$their_committish")"
127
  local maybe_expected_tree="$(git rev-parse expected^{tree})"
128
  local maybe_expected_reversed_tree="$(git rev-parse expected-reversed^{tree})"
129
  if [ "$maybe_expected_reversed_tree" == "expected-reversed^{tree}" ]; then
130
     maybe_expected_reversed_tree="$(git rev-parse expected^{tree} || :)"
131
  fi
132
  if [ -z "$opt_deviation_message" ]; then
133
    maybe_expected_tree="expected^{tree}"
134
    maybe_expected_reversed_tree="expected^{tree}"
135
  fi
136
137
  local merge_info="${output_name}.merge-info"
138
  git -c merge.conflictStyle=$conflict_style merge-tree -z --write-tree --allow-unrelated-histories "$our_committish" "$their_committish" > "$merge_info" || :
139
  echo "$dir" "$conflict_style" "$our_commit_id" "$our_committish" "$their_commit_id" "$their_committish" "$merge_info" "$maybe_expected_tree" "$opt_deviation_message" >> ../baseline.cases
140
141
  if [[ "$one_side" != "no-reverse" ]]; then
142
    local merge_info="${output_name}-reversed.merge-info"
143
    git -c merge.conflictStyle=$conflict_style merge-tree -z --write-tree --allow-unrelated-histories "$their_committish" "$our_committish" > "$merge_info" || :
144
    echo "$dir" "$conflict_style" "$their_commit_id" "$their_committish" "$our_commit_id" "$our_committish" "$merge_info" "$maybe_expected_reversed_tree" "$opt_deviation_message" >> ../baseline.cases
145
  fi
146
)
147
148
149
git init non-tree-to-tree
150
(cd non-tree-to-tree
151
  write_lines original 1 2 3 4 5 >a
152
  git add a && git commit -m "init"
153
154
  git branch A
155
  git branch B
156
157
  git checkout A
158
  write_lines 1 2 3 4 5 6 >a
159
  git commit -am "'A' changes 'a'"
160
161
  git checkout B
162
  rm a
163
  mkdir -p a/sub
164
  touch a/sub/b a/sub/c a/d a/e
165
  git add a && git commit -m "mv 'a' to 'a/sub/b', populate 'a/' with empty files"
166
)
167
168
git init deleted-file-added-dir
169
(cd deleted-file-added-dir
170
  echo original >to-be-deleted
171
  git add to-be-deleted && git commit -m "init"
172
173
  git branch A
174
  git branch B
175
176
  git checkout A
177
  git rm to-be-deleted
178
  git commit -m "delete file"
179
180
  git checkout B
181
  git rm to-be-deleted
182
  mkdir to-be-deleted
183
  touch to-be-deleted/a
184
  git add to-be-deleted/a && git commit -m "replace file with directory"
185
)
186
187
git init deleted-file-added-gitlink-directory
188
(cd deleted-file-added-gitlink-directory
189
  write_lines original >a
190
  git add a
191
  git commit -m "file base"
192
  base=$(git rev-parse HEAD)
193
194
  git branch A
195
  git branch B
196
197
  # Both sides delete `a`; B additionally replaces it with a directory containing
198
  # a gitlink. The shared deletion and descendant addition are compatible even
199
  # though the gitlink takes a different structural merge path than a blob.
200
  git checkout A
201
  git rm a
202
  git commit -m "delete a"
203
204
  git checkout B
205
  git rm a
206
  git update-index --add --cacheinfo 160000,$base,a/a
207
  git commit -m "replace a with a gitlink directory"
208
)
209
210
git init tree-to-non-tree
211
(cd tree-to-non-tree
212
  mkdir -p a/sub
213
  write_lines original 1 2 3 4 5 >a/sub/b
214
  touch a/sub/c a/d a/e
215
  git add a && git commit -m "init"
216
217
  git branch A
218
  git branch B
219
220
  git checkout A
221
  write_lines 1 2 3 4 5 6 >a/sub/b
222
  git commit -am "'A' changes 'a/sub/b'"
223
224
  git checkout B
225
  rm -Rf a
226
  echo "new file" > a
227
  git add a && git commit -m "rm -Rf a/ && add non-empty 'a'"
228
)
229
230
git init non-tree-to-tree-with-rename
231
(cd non-tree-to-tree-with-rename
232
  write_lines original 1 2 3 4 5 >a
233
  git add a && git commit -m "init"
234
235
  git branch A
236
  git branch B
237
238
  git checkout A
239
  write_lines 1 2 3 4 5 6 >a
240
  git commit -am "'A' changes 'a'"
241
242
  git checkout B
243
  mv a tmp
244
  mkdir -p a/sub
245
  mv tmp a/sub/b
246
  touch a/sub/c a/d a/e
247
  git add a && git commit -m "mv 'a' to 'a/sub/b', populate 'a/' with empty files"
248
)
249
250
git init tree-to-non-tree-with-rename
251
(cd tree-to-non-tree-with-rename
252
  mkdir -p a/sub
253
  write_lines original 1 2 3 4 5 >a/sub/b
254
  touch a/sub/c a/d a/e
255
  git add a && git commit -m "init"
256
257
  git branch A
258
  git branch B
259
260
  git checkout A
261
  write_lines 1 2 3 4 5 6 >a/sub/b
262
  git commit -am "'A' changes 'a/sub/b'"
263
264
  git checkout B
265
  rm -Rf a
266
  touch a
267
  git add a && git commit -m "rm -Rf a/ && add empty 'a' (which is like a rename from an empty deleted file)"
268
  # And because it's so thrown off, it gets a completely different result if reversed.
269
  git branch expected-reversed
270
271
  rm .git/index
272
  git update-index --index-info <<EOF
273
100644 $(oid 44065282f89b9bd6439ed2e4674721383fd987eb) 1	a/sub/b
274
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 2	a/sub/b
275
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 3	a~B
276
EOF
277
  make_conflict_index tree-to-non-tree-with-rename-A-B
278
279
  rm .git/index
280
  git update-index --index-info <<EOF
281
100644 $(oid 44065282f89b9bd6439ed2e4674721383fd987eb) 1	a/sub/b
282
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 3	a/sub/b
283
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 2	a~B
284
EOF
285
  make_conflict_index tree-to-non-tree-with-rename-A-B-reversed
286
)
287
288
git init simple
289
(cd simple
290
  rm -Rf .git/hooks
291
  write_lines 1 2 3 4 5 >numbers
292
  echo hello >greeting
293
  echo foo >whatever
294
  git add numbers greeting whatever
295
  tick
296
  git commit -m initial
297
298
  git branch side1
299
  git branch side2
300
  git branch side3
301
  git branch side4
302
303
  git checkout side1
304
  write_lines 1 2 3 4 5 6 >numbers
305
  echo hi >greeting
306
  echo bar >whatever
307
  git add numbers greeting whatever
308
  tick
309
  git commit -m modify-stuff
310
311
  git checkout side2
312
  write_lines 0 1 2 3 4 5 >numbers
313
  echo yo >greeting
314
  git rm whatever
315
  mkdir whatever
316
  >whatever/empty
317
  git add numbers greeting whatever/empty
318
  tick
319
  git commit -m other-modifications
320
321
  git checkout side3
322
  git mv numbers sequence
323
  tick
324
  git commit -m rename-numbers
325
326
  git checkout side4
327
  write_lines 0 1 2 3 4 5 >numbers
328
  echo yo >greeting
329
  git add numbers greeting
330
  tick
331
  git commit -m other-content-modifications
332
333
  git switch --orphan unrelated
334
  >something-else
335
  git add something-else
336
  tick
337
  git commit -m first-commit
338
339
  git checkout -b tweak1 side1
340
  write_lines zero 1 2 3 4 5 6 >numbers
341
  git add numbers
342
  git mv numbers "Αυτά μου φαίνονται κινέζικα"
343
  git commit -m "Renamed numbers"
344
)
345
346
git init rename-delete
347
(cd rename-delete
348
  write_lines 1 2 3 4 5 >foo
349
  mkdir olddir
350
  for i in a b c; do echo $i >olddir/$i; done
351
  git add foo olddir
352
  git commit -m "original"
353
354
  git branch A
355
  git branch B
356
357
  git checkout A
358
  write_lines 1 2 3 4 5 6 >foo
359
  git add foo
360
  git mv olddir newdir
361
  git commit -m "Modify foo, rename olddir to newdir"
362
363
  git checkout B
364
  write_lines 1 2 3 4 5 six >foo
365
  git add foo
366
  git mv foo olddir/bar
367
  git commit -m "Modify foo & rename foo -> olddir/bar"
368
)
369
370
git init rename-change-matrix
371
(cd rename-change-matrix
372
  # Each top-level directory is one independent rename interaction:
373
  #
374
  # * modify-source:
375
  #   A renames `file` to `renamed`; B modifies `file`.
376
  #   The modification follows the rename and the result is clean at `renamed`.
377
  # * delete-source:
378
  #   A renames `file` to `renamed`; B deletes `file`.
379
  #   This is a rename/delete conflict, with A's renamed file retained.
380
  # * add-destination:
381
  #   A renames `file` to `target`; B adds an unrelated `target`.
382
  #   Both additions occupy the rename destination, producing an add/add conflict.
383
  # * modify-destination:
384
  #   A replaces the existing `target` by renaming `source` onto it; B modifies the old `target`.
385
  #   The merge remains tied to the destination path: the base is the old `target`, A replaces all
386
  #   of it with `source`, and B appends to the old `target`. Those edits conflict, preserving the
387
  #   complete A and B versions inside conflict markers.
388
  # * delete-destination:
389
  #   A replaces the existing `target` by renaming `source` onto it; B deletes the old `target`.
390
  #   The deletion conflicts with A's replacement, which remains at `target`.
391
  # * different-renames:
392
  #   A and B both modify `file`, then rename it to different destinations.
393
  #   Both destinations contain the one merged result, with a content conflict for the two appends.
394
  #   Its configured marker size is 10; the rename/rename conflict adds one, yielding 11.
395
  # * directory-old:
396
  #   A renames the directory; B adds a child below its old name.
397
  #   The child follows the directory rename and the result is clean below `directory-renamed`.
398
  #
399
  # Distinct repeated words keep rename similarity pairing local to each case.
400
  mkdir -p modify-source delete-source add-destination modify-destination delete-destination \
401
    different-renames directory-old
402
  write_lines alpha alpha alpha alpha alpha >modify-source/file
403
  write_lines bravo bravo bravo bravo bravo >delete-source/file
404
  write_lines charlie charlie charlie charlie charlie >add-destination/file
405
  write_lines delta-source delta-source delta-source delta-source delta-source >modify-destination/source
406
  write_lines delta-target delta-target delta-target delta-target delta-target >modify-destination/target
407
  write_lines echo-source echo-source echo-source echo-source echo-source >delete-destination/source
408
  write_lines echo-target echo-target echo-target echo-target echo-target >delete-destination/target
409
  write_lines golf golf golf golf golf >different-renames/file
410
  write_lines juliet juliet juliet juliet juliet >directory-old/file
411
  echo 'different-renames/* conflict-marker-size=10' >.gitattributes
412
  git add . && git commit -m "base"
413
414
  git branch A
415
  git branch B
416
417
  git checkout A
418
  git mv modify-source/file modify-source/renamed
419
  git mv delete-source/file delete-source/renamed
420
  git mv add-destination/file add-destination/target
421
  git rm modify-destination/target
422
  git mv modify-destination/source modify-destination/target
423
  git rm delete-destination/target
424
  git mv delete-destination/source delete-destination/target
425
  echo changed-by-A >>different-renames/file
426
  git mv different-renames/file different-renames/ours
427
  git mv directory-old directory-renamed
428
  git commit -am "rename one side of each matrix entry"
429
430
  git checkout B
431
  echo changed-by-B >>modify-source/file
432
  git rm delete-source/file
433
  echo added-by-B >add-destination/target
434
  echo changed-by-B >>modify-destination/target
435
  git rm delete-destination/target
436
  echo changed-by-B >>different-renames/file
437
  git mv different-renames/file different-renames/theirs
438
  echo added-by-B >directory-old/added
439
  git add . && git commit -m "apply the other change in each matrix entry"
440
441
)
442
443
git init same-rename-with-content
444
(cd same-rename-with-content
445
  # Both sides modify the same source and rename it to the same destination.
446
  # The two appends conflict, but the rename itself agrees. The result should therefore be
447
  # exactly one content merge at `target`, never a second merge of an already-merged blob.
448
  write_lines foxtrot foxtrot foxtrot foxtrot foxtrot >file
449
  git add file && git commit -m "base"
450
451
  git branch A
452
  git branch B
453
454
  git checkout A
455
  echo changed-by-A >>file
456
  git mv file target
457
  git commit -am "A renames and changes file"
458
459
  git checkout B
460
  echo changed-by-B >>file
461
  git mv file target
462
  git commit -am "B renames and changes file"
463
464
  # Both sides agree on the rename, so this is an ordinary content conflict using the
465
  # configured marker size. These branches provide the merged blob for gix's index shape.
466
  git checkout -b expected main
467
  git rm file
468
  write_lines \
469
    foxtrot foxtrot foxtrot foxtrot foxtrot \
470
    '<<<<<<< A' \
471
    changed-by-A \
472
    ======= \
473
    changed-by-B \
474
    '>>>>>>> B' >target
475
  git add target && git commit -m "single content merge at shared rename destination"
476
477
  git checkout -b expected-reversed main
478
  git rm file
479
  write_lines \
480
    foxtrot foxtrot foxtrot foxtrot foxtrot \
481
    '<<<<<<< B' \
482
    changed-by-B \
483
    ======= \
484
    changed-by-A \
485
    '>>>>>>> A' >target
486
  git add target && git commit -m "single reversed content merge at shared rename destination"
487
)
488
489
git init same-rename-and-file-to-directory
490
(cd same-rename-and-file-to-directory
491
  # Both sides perform the same compound operation:
492
  #
493
  # * rename `source` to `moved`;
494
  # * replace the old `source` path with a directory;
495
  # * add different content at `source/child`.
496
  #
497
  # The identical rewrite must be applied only once. Applying it again after merging the
498
  # children would recursively remove `source` and silently discard both payloads.
499
  write_lines source source source source source >source
500
  git add source && git commit -m "base"
501
502
  git branch A
503
  git branch B
504
505
  git checkout A
506
  git mv source moved
507
  mkdir source
508
  echo changed-by-A >source/child
509
  git add . && git commit -m "A renames source and adds a child at its old path"
510
511
  git checkout B
512
  git mv source moved
513
  mkdir source
514
  echo changed-by-B >source/child
515
  git add . && git commit -m "B renames source and adds a child at its old path"
516
)
517
518
git init renames-to-same-destination
519
(cd renames-to-same-destination
520
  # The sides rename different source files onto the same destination:
521
  #
522
  # * A: `one` -> `target`
523
  # * B: `two` -> `target`
524
  #
525
  # Neither rename should silently win. A normal merge removes both sources and records the
526
  # two destination contents as an add/add conflict. Forced ancestor resolution keeps `one`
527
  # and `two`; forced ours applies only the rename belonging to the selected first side.
528
  write_lines one one one one one >one
529
  write_lines two two two two two >two
530
  git add . && git commit -m "base"
531
532
  git branch A
533
  git branch B
534
535
  git checkout A
536
  git mv one target
537
  git commit -m "rename one to target"
538
539
  git checkout B
540
  git mv two target
541
  git commit -m "rename two to target"
542
)
543
544
git init identical-renames-to-same-destination
545
(cd identical-renames-to-same-destination
546
  write_lines same >one
547
  cp one two
548
  git add .
549
  git commit -m "two identical files"
550
551
  git branch A
552
  git branch B
553
554
  # Both sides rename a different source to `target`, but the entries have the
555
  # same mode and object ID. There is nothing to content-merge and the two
556
  # operations collapse cleanly to the shared destination in either direction.
557
  git checkout A
558
  git mv one target
559
  git commit -m "rename one to target"
560
561
  git checkout B
562
  git mv two target
563
  git commit -m "rename two to target"
564
)
565
566
git init identical-renames-to-same-destination-with-mode-change
567
(cd identical-renames-to-same-destination-with-mode-change
568
  write_lines same >one
569
  cp one two
570
  git add .
571
  git commit -m "two identical files"
572
573
  git branch A
574
  git branch B
575
576
  # The destinations have the same blob ID but differ in executable mode.
577
  # Content merging must see those original modes even though the final mode
578
  # has already been selected.
579
  git checkout A
580
  git mv one target
581
  chmod +x target
582
  # For this to work on windows, we need explicit executable bit handling.
583
  git update-index --chmod=+x target
584
  git commit -m "rename one to executable target"
585
586
  git checkout B
587
  git mv two target
588
  git commit -m "rename two to target"
589
590
  # gix treats the identical content as clean and carries the executable mode
591
  # selected from A to the shared destination in both merge directions.
592
  git checkout -b expected A
593
  git rm two
594
  git commit -m "expected gix merge"
595
)
596
597
git init deleted-file-added-dir-with-rename
598
(cd deleted-file-added-dir-with-rename
599
  # Regression for a deletion that is processed but not applied:
600
  #
601
  # * A deletes the file `x`.
602
  # * B renames that file to `renamed`, then creates the directory entry `x/a`.
603
  #
604
  # With ancestor conflict resolution, A's deletion must not count as applied merely because
605
  # the rename/delete conflict was handled. The ancestor file `x` remains, so `x/a` cannot
606
  # turn it into a directory.
607
  echo base >x
608
  git add x
609
  git commit -m "add x"
610
611
  git branch A
612
  git branch B
613
614
  git checkout A
615
  git rm x
616
  git commit -m "delete x"
617
618
  git checkout B
619
  git mv x renamed
620
  mkdir x
621
  echo added >x/a
622
  git add x/a
623
  git commit -m "rename x and add x/a"
624
)
625
626
git init deleted-file-replaced-by-renamed-dir
627
(cd deleted-file-replaced-by-renamed-dir
628
  # Regression for a file that one side replaces with a directory filled only by renames:
629
  #
630
  # * A deletes the file `a/b` and moves `d/g` and `d/h` into the new directory `a/b/`.
631
  # * B only edits the unrelated file `u`.
632
  #
633
  # Rename tracking reports A's renames before the unpaired deletion of `a/b`, so the deletion
634
  # reaches the tree editor after `a/b/` exists and must not remove that directory.
635
  mkdir a d
636
  echo original >a/b
637
  seq 1 10 >d/g
638
  seq 11 20 >d/h
639
  seq 21 30 >u
640
  git add . && git commit -m "init"
641
642
  git branch A
643
  git branch B
644
645
  git checkout A
646
  git rm a/b
647
  mkdir -p a/b
648
  git mv d/g a/b/g
649
  git mv d/h a/b/h
650
  git commit -m "replace the file a/b with the moved directory d"
651
652
  git checkout B
653
  seq 21 31 >u
654
  git commit -am "edit unrelated file"
655
)
656
657
git init renamed-file-replaced-by-renamed-dir
658
(cd renamed-file-replaced-by-renamed-dir
659
  # Like `deleted-file-replaced-by-renamed-dir`, but A moves the file `a/b` to `c` instead of
660
  # deleting it, and B edits `a/b`. B's edit follows the rename to `c`, and removing the rename
661
  # source `a/b` must not remove the directory `a/b/` that A's other rename created.
662
  mkdir a d
663
  seq 1 10 >a/b
664
  seq 11 20 >d/g
665
  git add . && git commit -m "init"
666
667
  git branch A
668
  git branch B
669
670
  git checkout A
671
  git mv a/b c
672
  mkdir -p a/b
673
  git mv d/g a/b/g
674
  git commit -m "move the file a/b away and d/g into a/b/"
675
676
  git checkout B
677
  seq 1 11 >a/b
678
  git commit -am "edit a/b"
679
)
680
681
git init rename-add
682
(cd rename-add
683
		write_lines original 1 2 3 4 5 >foo
684
		git add foo
685
		git commit -m "original"
686
687
		git branch A
688
		git branch B
689
690
		git checkout A
691
		write_lines 1 2 3 4 5 >foo
692
		echo "different file" >bar
693
		git add foo bar
694
		git commit -m "Modify foo, add bar"
695
696
		git checkout B
697
		write_lines original 1 2 3 4 5 6 >foo
698
		git add foo
699
		git mv foo bar
700
		git commit -m "rename foo to bar"
701
)
702
703
git init rename-add-exe-bit-conflict
704
(cd rename-add-exe-bit-conflict
705
		touch a b
706
		chmod +x a
707
    git add --chmod=+x a
708
		git add b
709
		git commit -m "original"
710
711
		git branch A
712
		git branch B
713
714
		git checkout A
715
		chmod -x a
716
    git update-index --chmod=-x a
717
		git commit -m "-x a"
718
719
		git checkout B
720
		git mv --force b a
721
		chmod +x a
722
    git update-index --chmod=+x a
723
		git commit -m "mv b a; chmod +x a"
724
)
725
726
git init rename-add-symlink
727
(cd rename-add-symlink
728
  write_lines original 1 2 3 4 5 >foo
729
  git add foo
730
  git commit -m "original"
731
732
  git branch A
733
  git branch B
734
735
  git checkout A
736
  write_lines 1 2 3 4 5 >foo
737
  ln -s foo bar
738
  git add foo bar
739
  git commit -m "Modify foo, add symlink bar"
740
741
  git checkout B
742
  write_lines original 1 2 3 4 5 6 >foo
743
  git add foo
744
  git mv foo bar
745
  git commit -m "rename foo to bar"
746
)
747
748
git init rename-add-same-symlink
749
(cd rename-add-same-symlink
750
  touch target
751
  ln -s target link
752
  git add .
753
  git commit -m "original"
754
755
  git branch A
756
  git branch B
757
758
  git checkout A
759
  git mv link link-new
760
  git commit -m "rename link to link-new"
761
762
  git checkout B
763
  ln -s target link-new
764
  git add link-new
765
  git commit -m "create link-new"
766
)
767
768
git init rename-rename-plus-content
769
(cd rename-rename-plus-content
770
  write_lines 1 2 3 4 5 >foo
771
  git add foo
772
  git commit -m "original"
773
774
  git branch A
775
  git branch B
776
777
  git checkout A
778
  write_lines 1 2 3 4 5 six >foo
779
  git add foo
780
  git mv foo bar
781
  git commit -m "Modify foo + rename to bar"
782
783
  git checkout B
784
  write_lines 1 2 3 4 5 6 >foo
785
  git add foo
786
  git mv foo baz
787
  git commit -m "Modify foo + rename to baz"
788
)
789
790
git init rename-add-delete
791
(
792
  cd rename-add-delete
793
  echo "original file" >foo
794
  git add foo
795
  git commit -m "original"
796
797
  git branch A
798
  git branch B
799
800
  git checkout A
801
  git rm foo
802
  echo "different file" >bar
803
  git add bar
804
  git commit -m "Remove foo, add bar"
805
806
  git checkout B
807
  git mv foo bar
808
  git commit -m "rename foo to bar"
809
)
810
811
git init rename-rename-delete-delete
812
(
813
  cd rename-rename-delete-delete
814
  echo foo >foo
815
  echo bar >bar
816
  git add foo bar
817
  git commit -m O
818
819
  git branch A
820
  git branch B
821
822
  git checkout A
823
  git mv foo baz
824
  git rm bar
825
  git commit -m "Rename foo, remove bar"
826
827
  git checkout B
828
  git mv bar baz
829
  git rm foo
830
  git commit -m "Rename bar, remove foo"
831
)
832
833
git init super-1
834
(cd super-1
835
  seq 11 19 >one
836
  seq 31 39 >three
837
  seq 51 59 >five
838
  git add .
839
  tick
840
  git commit -m "O"
841
842
  git branch A
843
  git branch B
844
845
  git checkout A
846
  seq 10 19 >one
847
  echo 40        >>three
848
  git add one three
849
  git mv  one   two
850
  git mv  three four
851
  git mv  five  six
852
  tick
853
  git commit -m "A"
854
855
  git checkout B
856
  echo 20    >>one
857
  echo forty >>three
858
  echo 60    >>five
859
  git add one three five
860
  git mv  one   six
861
  git mv  three two
862
  git mv  five  four
863
  tick
864
  git commit -m "B"
865
)
866
867
git init super-2
868
(cd super-2
869
  write_lines 1 2 3 4 5 >foo
870
  mkdir olddir
871
  for i in a b c; do echo $i >olddir/$i || exit 1; done
872
  git add foo olddir
873
  git commit -m "original"
874
875
  git branch A
876
  git branch B
877
878
  git checkout A
879
  git rm foo
880
  git mv olddir newdir
881
  mkdir newdir/bar
882
  >newdir/bar/file
883
  git add newdir/bar/file
884
  git commit -m "rm foo, olddir/ -> newdir/, + newdir/bar/file"
885
886
  git checkout B
887
  write_lines 1 2 3 4 5 6 >foo
888
  git add foo
889
  git mv foo olddir/bar
890
  git commit -m "Modify foo & rename foo -> olddir/bar"
891
892
  rm .git/index
893
  git update-index --index-info <<EOF
894
100644 $(oid 78981922613b2afb6025042ff6bd878ac1994e85) 0	newdir/a
895
100644 $(oid 61780798228d17af2d34fce4cfbdf35556832472) 0	newdir/b
896
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 0	newdir/bar/file
897
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 3	newdir/bar~B
898
100644 $(oid f2ad6c76f0115a6ba5b00456a849810e7ec0af20) 0	newdir/c
899
EOF
900
  # Git also has
901
  # 100644 b414108e81e5091fe0974a1858b4d0d22b107f70 1	newdir/bar~B
902
  # which then looks like "deleted by us: newdir/bar-B`
903
  # Our index here doesn't manage to track the base across so many renames, but it ends up looking like
904
  # `added by them: newdir/bar~B` which to my mind is more helpful, in a situation where the index simply
905
  # cannot properly show what happened.
906
  make_conflict_index super-2-A-B
907
  make_conflict_index super-2-A-B-diff3
908
909
  rm .git/index
910
  git update-index --index-info <<EOF
911
100644 $(oid 78981922613b2afb6025042ff6bd878ac1994e85) 0	newdir/a
912
100644 $(oid 61780798228d17af2d34fce4cfbdf35556832472) 0	newdir/b
913
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 0	newdir/bar/file
914
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 2	newdir/bar~B
915
100644 $(oid f2ad6c76f0115a6ba5b00456a849810e7ec0af20) 0	newdir/c
916
EOF
917
  make_conflict_index super-2-A-B-reversed
918
  make_conflict_index super-2-A-B-diff3-reversed
919
)
920
921
git init rename-within-rename
922
(cd rename-within-rename
923
  mkdir a && write_lines original 1 2 3 4 5 >a/x.f
924
  mkdir a/sub && write_lines original 1 2 3 4 5 >a/sub/y.f
925
  touch a/w a/sub/z
926
  git add . && git commit -m "original"
927
928
  git branch A
929
  git branch B
930
  git branch expected
931
932
  git checkout A
933
  write_lines 1 2 3 4 5 >a/x.f
934
  write_lines 1 2 3 4 5 >a/sub/y.f
935
  git mv a a-renamed
936
  git commit -am "changed all content, renamed a -> a-renamed"
937
938
  git checkout B
939
  write_lines original 1 2 3 4 5 6 >a/x.f
940
  write_lines original 1 2 3 4 5 6 >a/sub/y.f
941
  git mv a/sub a/sub-renamed
942
  git commit -am "changed all content, renamed a/sub -> a/sub-renamed"
943
944
  git checkout expected
945
  write_lines 1 2 3 4 5 6 >a/x.f
946
  write_lines 1 2 3 4 5 6 >a/sub/y.f
947
  cp -Rv a/sub a/sub-renamed
948
  git add .
949
  git mv a a-renamed
950
  git commit -am "we also have duplication just like Git, but we are consistent independently of the side, hence the expectation"
951
952
  # We have duplication just like Git, but our index is definitely more complex. This one seems more plausible.
953
  # The problem is that renames can't be indicated correctly in the index.
954
  rm .git/index
955
  git update-index --index-info <<EOF
956
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 2	a-renamed/sub/y.f
957
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 2	a-renamed/sub/z
958
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 0	a-renamed/sub-renamed/y.f
959
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 0	a-renamed/sub-renamed/z
960
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 0	a-renamed/w
961
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 0	a-renamed/x.f
962
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 3	a/sub-renamed/y.f
963
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 3	a/sub-renamed/z
964
100644 $(oid 44065282f89b9bd6439ed2e4674721383fd987eb) 1	a/sub/y.f
965
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 1	a/sub/z
966
EOF
967
  make_conflict_index rename-within-rename-A-B-deviates
968
  rm .git/index
969
  git update-index --index-info <<EOF
970
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 3	a-renamed/sub/y.f
971
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 3	a-renamed/sub/z
972
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 0	a-renamed/sub-renamed/y.f
973
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 0	a-renamed/sub-renamed/z
974
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 0	a-renamed/w
975
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 0	a-renamed/x.f
976
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 2	a/sub-renamed/y.f
977
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 2	a/sub-renamed/z
978
100644 $(oid 44065282f89b9bd6439ed2e4674721383fd987eb) 1	a/sub/y.f
979
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 1	a/sub/z
980
EOF
981
  make_conflict_index rename-within-rename-A-B-deviates-reversed
982
)
983
984
git init rename-within-rename-2
985
(cd rename-within-rename-2
986
  mkdir a && write_lines original 1 2 3 4 5 >a/x.f
987
  mkdir a/sub && write_lines original 1 2 3 4 5 >a/sub/y.f
988
  touch a/w a/sub/z
989
  git add . && git commit -m "original"
990
991
  git branch A
992
  git branch B
993
  git branch expected
994
995
  git checkout A
996
  write_lines 1 2 3 4 5 >a/x.f
997
  write_lines 1 2 3 4 5 >a/sub/y.f
998
  git mv a/sub a/sub-renamed
999
  git mv a a-renamed
1000
  git commit -am "changed all content, renamed a -> a-renamed, a/sub -> a/sub-renamed"
1001
1002
  git checkout B
1003
  write_lines original 1 2 3 4 5 6 >a/x.f
1004
  write_lines original 1 2 3 4 5 6 >a/sub/y.f
1005
  git mv a/sub a/sub-renamed
1006
  git commit -am "changed all content, renamed a/sub -> a/sub-renamed"
1007
1008
  git checkout expected
1009
  write_lines 1 2 3 4 5 6 >a/x.f
1010
  write_lines 1 2 3 4 5 6 >a/sub/y.f
1011
  git mv a/sub a/sub-renamed
1012
  git mv a a-renamed
1013
  git commit -am "tracked both renames, applied all modifications by merge"
1014
  # Both merge directions yield this tree, so `expected` serves the reversed case as well.
1015
1016
1017
  # Both directions produce the same cleanly merged state, and the index is the same as well.
1018
  rm .git/index
1019
  git update-index --index-info <<EOF
1020
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 0	a-renamed/sub-renamed/y.f
1021
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 0	a-renamed/sub-renamed/z
1022
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 0	a-renamed/w
1023
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 0	a-renamed/x.f
1024
EOF
1025
  make_conflict_index rename-within-rename-2-A-B-deviates
1026
  make_conflict_index rename-within-rename-2-A-B-deviates-reversed
1027
)
1028
1029
git init conflicting-rename
1030
(cd conflicting-rename
1031
  mkdir a && write_lines original 1 2 3 4 5 >a/x.f
1032
  mkdir a/sub && write_lines original 1 2 3 4 5 >a/sub/y.f
1033
  touch a/w a/sub/z
1034
  git add . && git commit -m "original"
1035
1036
  git branch A
1037
  git branch B
1038
1039
  git checkout A
1040
  write_lines 1 2 3 4 5 >a/x.f
1041
  write_lines 1 2 3 4 5 >a/sub/y.f
1042
  git mv a a-renamed
1043
  git commit -am "changed all content, renamed a -> a-renamed"
1044
1045
  git checkout B
1046
  write_lines original 1 2 3 4 5 6 >a/x.f
1047
  write_lines original 1 2 3 4 5 6 >a/sub/y.f
1048
  git mv a a-different
1049
  git commit -am "changed all content, renamed a -> a-different"
1050
1051
# Git only sees the files with content changes as conflicting, and somehow misses to add the
1052
# bases of the files without content changes. After all, these also have been renamed into
1053
# different places which must be a conflict just as much.
1054
  rm .git/index
1055
  git update-index --index-info <<EOF
1056
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 3	a-different/sub/y.f
1057
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 3	a-different/sub/z
1058
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 3	a-different/w
1059
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 3	a-different/x.f
1060
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 2	a-renamed/sub/y.f
1061
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 2	a-renamed/sub/z
1062
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 2	a-renamed/w
1063
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 2	a-renamed/x.f
1064
100644 $(oid 44065282f89b9bd6439ed2e4674721383fd987eb) 1	a/sub/y.f
1065
100644 $(oid 44065282f89b9bd6439ed2e4674721383fd987eb) 1	a/x.f
1066
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 1	a/sub/z
1067
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 1	a/w
1068
EOF
1069
  make_conflict_index conflicting-rename-A-B
1070
1071
  rm .git/index
1072
  git update-index --index-info <<EOF
1073
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 2	a-different/sub/y.f
1074
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 2	a-different/sub/z
1075
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 2	a-different/w
1076
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 2	a-different/x.f
1077
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 3	a-renamed/sub/y.f
1078
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 3	a-renamed/sub/z
1079
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 3	a-renamed/w
1080
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 3	a-renamed/x.f
1081
100644 $(oid 44065282f89b9bd6439ed2e4674721383fd987eb) 1	a/sub/y.f
1082
100644 $(oid 44065282f89b9bd6439ed2e4674721383fd987eb) 1	a/x.f
1083
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 1	a/sub/z
1084
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 1	a/w
1085
EOF
1086
  make_conflict_index conflicting-rename-A-B-reversed
1087
)
1088
1089
git init conflicting-rename-2
1090
(cd conflicting-rename-2
1091
  mkdir a && write_lines original 1 2 3 4 5 >a/x.f
1092
  mkdir a/sub && write_lines original 1 2 3 4 5 >a/sub/y.f
1093
  touch a/w a/sub/z
1094
  git add . && git commit -m "original"
1095
1096
  git branch A
1097
  git branch B
1098
1099
  git checkout A
1100
  write_lines 1 2 3 4 5 >a/x.f
1101
  write_lines 1 2 3 4 5 >a/sub/y.f
1102
  git mv a/sub a/sub-renamed
1103
  git commit -am "changed all content, renamed a/sub -> a/sub-renamed"
1104
1105
  git checkout B
1106
  write_lines original 1 2 3 4 5 6 >a/x.f
1107
  write_lines original 1 2 3 4 5 6 >a/sub/y.f
1108
  git mv a/sub a/sub-different
1109
  git commit -am "changed all content, renamed a/sub -> a/sub-different"
1110
1111
# Here it's the same as above, i.e. Git doesn't list files as conflicting if
1112
# they didn't change, even though they have a conflicting rename.
1113
  rm .git/index
1114
  git update-index --index-info <<EOF
1115
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 3	a/sub-different/y.f
1116
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 3	a/sub-different/z
1117
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 2	a/sub-renamed/y.f
1118
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 2	a/sub-renamed/z
1119
100644 $(oid 44065282f89b9bd6439ed2e4674721383fd987eb) 1	a/sub/y.f
1120
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 1	a/sub/z
1121
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 0	a/w
1122
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 0	a/x.f
1123
EOF
1124
  make_conflict_index conflicting-rename-2-A-B
1125
1126
  rm .git/index
1127
  git update-index --index-info <<EOF
1128
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 2	a/sub-different/y.f
1129
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 2	a/sub-different/z
1130
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 3	a/sub-renamed/y.f
1131
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 3	a/sub-renamed/z
1132
100644 $(oid 44065282f89b9bd6439ed2e4674721383fd987eb) 1	a/sub/y.f
1133
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 1	a/sub/z
1134
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 0	a/w
1135
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 0	a/x.f
1136
EOF
1137
  make_conflict_index conflicting-rename-2-A-B-reversed
1138
)
1139
1140
git init conflicting-rename-complex
1141
(cd conflicting-rename-complex
1142
  mkdir a && write_lines original 1 2 3 4 5 >a/x.f
1143
  mkdir a/sub && write_lines original 1 2 3 4 5 >a/sub/y.f
1144
  touch a/w a/sub/z
1145
  git add . && git commit -m "original"
1146
1147
  git branch A
1148
  git branch B
1149
  git branch expected
1150
1151
  git checkout A
1152
  write_lines 1 2 3 4 5 >a/x.f
1153
  write_lines 1 2 3 4 5 >a/sub/y.f
1154
  git mv a a-renamed
1155
  git commit -am "changed all content, renamed a -> a-renamed"
1156
1157
  git checkout B
1158
  write_lines original 1 2 3 4 5 6 >a/sub/y.f
1159
  git mv a/sub tmp
1160
  git rm -r a
1161
  git mv tmp a
1162
  git commit -am "change something in subdirectory, then overwrite directory with subdirectory"
1163
1164
  git checkout expected
1165
  rm .git/index
1166
  rm -Rf ./a
1167
  mkdir -p a-renamed/sub
1168
  write_lines 1 2 3 4 5 6 >a-renamed/sub/y.f
1169
  write_lines 1 2 3 4 5 >a-renamed/x.f
1170
  write_lines 1 2 3 4 5 6 >a-renamed/y.f
1171
  touch a-renamed/z a-renamed/w a-renamed/sub/z
1172
  git add .
1173
  git commit -m "Close to what Git has, but different due to rename tracking. Contents follow their true renames thanks to filename-aware matching, while Git also keeps rename/rename destinations that we compose."
1174
1175
1176
  # Since the whole state is very different, the expected index is as well, but at least it should make sense for what it is.
1177
  # Files pair up by matching filename like in Git, so `y.f` and `z` follow their true renames,
1178
  # while `a/x.f` and `a/w` which `B` deleted conflict with `A`'s directory rename and keep our side.
1179
  rm .git/index
1180
  git update-index --index-info <<EOF
1181
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 2	a-renamed/sub/y.f
1182
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 2	a-renamed/sub/z
1183
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 0	a-renamed/y.f
1184
100644 $(oid 8a1218a1024a212bb3db30becd860315f9f3ac52) 2	a-renamed/x.f
1185
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 2	a-renamed/w
1186
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 0	a-renamed/z
1187
100644 $(oid 44065282f89b9bd6439ed2e4674721383fd987eb) 1	a/sub/y.f
1188
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 3	a/y.f
1189
EOF
1190
  make_conflict_index conflicting-rename-complex-A-B
1191
1192
  rm .git/index
1193
  git update-index --index-info <<EOF
1194
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 3	a-renamed/sub/y.f
1195
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 3	a-renamed/sub/z
1196
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 0	a-renamed/y.f
1197
100644 $(oid 8a1218a1024a212bb3db30becd860315f9f3ac52) 3	a-renamed/x.f
1198
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 3	a-renamed/w
1199
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 0	a-renamed/z
1200
100644 $(oid 44065282f89b9bd6439ed2e4674721383fd987eb) 1	a/sub/y.f
1201
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 2	a/y.f
1202
EOF
1203
  make_conflict_index conflicting-rename-complex-A-B-reversed
1204
)
1205
1206
git init same-rename-different-mode
1207
(cd same-rename-different-mode
1208
  mkdir a && write_lines original 1 2 3 4 5 >a/x.f
1209
  touch a/w
1210
  git add . && git commit -m "original"
1211
1212
  git branch A
1213
  git branch B
1214
  git branch expected
1215
1216
  git checkout A
1217
  write_lines 1 2 3 4 5 >a/x.f
1218
  chmod +x a/x.f a/w
1219
  git update-index --chmod=+x a/x.f a/w
1220
  git mv a a-renamed
1221
  git commit -am "changed a/xf, add +x everywhere, renamed a -> a-renamed"
1222
1223
  git checkout B
1224
  write_lines original 1 2 3 4 5 6 >a/x.f
1225
  git mv a a-renamed
1226
  git commit -am "changed all content, renamed a -> a-renamed"
1227
1228
  git checkout expected
1229
  chmod +x a/x.f a/w
1230
  git update-index --chmod=+x a/x.f a/w
1231
  write_lines 1 2 3 4 5 6 >a/x.f
1232
  git mv a a-renamed
1233
  git commit -am "Git, when branches are reversed, doesn't keep the +x flag on a/w so we specify our own expectation"
1234
  # Git sets +x and adds it as conflict, even though the merge is perfect, i.e. one side adds +x on top, perfectly additive.
1235
  make_conflict_index same-rename-different-mode-A-B
1236
  make_conflict_index same-rename-different-mode-A-B-reversed
1237
)
1238
1239
git init remove-executable-mode
1240
(cd remove-executable-mode
1241
  touch w
1242
  chmod +x w
1243
  git add --chmod=+x w
1244
  git add . && git commit -m "original"
1245
1246
  git branch A
1247
  git branch B
1248
1249
  git checkout A
1250
  chmod -x w
1251
  git update-index --chmod=-x w
1252
  git commit -am "remove executable bit from w"
1253
1254
  git checkout B
1255
  write_lines 1 2 3 4 5  >w
1256
  git commit -am "unrelated change to w"
1257
)
1258
1259
git init renamed-symlink-with-conflict
1260
(cd renamed-symlink-with-conflict
1261
  mkdir a && write_lines original 1 2 3 4 5 >a/x.f
1262
  ln -s a/x.f link
1263
  git add . && git commit -m "original"
1264
1265
  git branch A
1266
  git branch B
1267
1268
  git checkout A
1269
  write_lines 1 2 3 4 5 >a/x.f
1270
  git mv link link-renamed
1271
  git commit -am "changed a/x.f, renamed link -> link-renamed"
1272
1273
  git checkout B
1274
  write_lines original 1 2 3 4 5 6 >a/x.f
1275
  git mv link link-different
1276
  git commit -am "change content, renamed link -> link-different"
1277
)
1278
1279
git init added-file-changed-content-and-mode
1280
(cd added-file-changed-content-and-mode
1281
  mkdir a && write_lines original 1 2 3 4 5 >a/x.f
1282
  git add . && git commit -m "original"
1283
1284
  git branch A
1285
  git branch B
1286
  git branch expected
1287
1288
  git checkout A
1289
  write_lines 1 2 3 4 5 >new
1290
  git add .
1291
  git commit -m "add 'new' with content A"
1292
1293
  git checkout B
1294
  write_lines original 1 2 3 4 5 6 >new
1295
  chmod +x new
1296
  git add --chmod=+x new
1297
  git commit -m "add new with content B and +x"
1298
1299
  git checkout expected
1300
  echo -n $'<<<<<<< A\n1\n2\n3\n4\n5\n=======\noriginal\n1\n2\n3\n4\n5\n6\n>>>>>>> B\n' >new
1301
  chmod +x new
1302
  git add --chmod=+x new
1303
  git commit -m "Git has a better merge here, but that's due to better hunk handling/hunk splitting. We, however, consistently use +x"
1304
)
1305
1306
git init type-change-and-renamed
1307
(cd type-change-and-renamed
1308
  mkdir a && >a/x.f
1309
  ln -s a/x.f link
1310
  git add . && git commit -m "original"
1311
1312
  git branch A
1313
  git branch B
1314
1315
  git checkout A
1316
  rm link && echo not-link > link
1317
  git commit -am "link type-changed, file changed"
1318
1319
  git checkout B
1320
  git mv link link-renamed
1321
  git commit -am "just renamed the link"
1322
)
1323
1324
git init change-and-delete
1325
(cd change-and-delete
1326
  mkdir a && write_lines original 1 2 3 4 5 >a/x.f
1327
  ln -s a/x.f link
1328
  git add . && git commit -m "original"
1329
1330
  git branch A
1331
  git branch B
1332
1333
  git checkout A
1334
  write_lines 1 2 3 4 5 6 >a/x.f
1335
  rm link && echo not-link > link
1336
  git commit -am "link type-changed, file changed"
1337
1338
  git checkout B
1339
  git rm link a/x.f
1340
  git commit -am "delete everything"
1341
)
1342
1343
git init submodule-both-modify
1344
(cd submodule-both-modify
1345
	mkdir sub
1346
	(cd sub
1347
	 git init
1348
	 echo original > file
1349
	 git add file
1350
	 tick
1351
	 git commit -m sub-root
1352
	)
1353
	git add sub
1354
	tick
1355
	git commit -m root
1356
1357
	git branch expected
1358
1359
	git checkout -b A main
1360
	(cd sub
1361
	 echo A > file
1362
	 git add file
1363
	 tick
1364
	 git commit -m sub-a
1365
	)
1366
	git add sub
1367
	tick
1368
	git commit -m a
1369
	git branch -f expected
1370
1371
	git checkout -b B main
1372
	(cd sub
1373
	 echo B > file
1374
	 git add file
1375
	 tick
1376
	 git commit -m sub-b
1377
	)
1378
	git add sub
1379
	tick
1380
	git commit -m b
1381
	git branch expected-reversed
1382
1383
	# The tree merger retains Git's conflict stages and provisional ours entry without inspecting submodule history.
1384
	rm .git/index
1385
	git update-index --index-info <<EOF
1386
160000 $(oid e835c0c403c8e494c0ca98f3d25d0b8464c18d38) 1	sub
1387
160000 $(oid 64466ebdff775ad618d9cc993cf52840e0af528c) 2	sub
1388
160000 $(oid ea6eb701e03c2497915c25a851f3da8f8e362ca0) 3	sub
1389
EOF
1390
  make_conflict_index submodule-both-modify-A-B
1391
1392
	rm .git/index
1393
	git update-index --index-info <<EOF
1394
160000 $(oid e835c0c403c8e494c0ca98f3d25d0b8464c18d38) 1	sub
1395
160000 $(oid ea6eb701e03c2497915c25a851f3da8f8e362ca0) 2	sub
1396
160000 $(oid 64466ebdff775ad618d9cc993cf52840e0af528c) 3	sub
1397
EOF
1398
  make_conflict_index submodule-both-modify-A-B-reversed
1399
)
1400
1401
git init gitlink-replaced-by-files
1402
(cd gitlink-replaced-by-files
1403
  git commit --allow-empty -m "seed commit"
1404
  seed=$(git rev-parse HEAD)
1405
  git update-index --add --cacheinfo 160000,$seed,item
1406
  git commit -m "gitlink base"
1407
1408
  git branch A
1409
  git branch B
1410
1411
  # Both sides replace the gitlink with regular files. Their contents must be
1412
  # merged as an add/add pair with an empty blob ancestor; the commit named by
1413
  # the base entry is not a valid blob-merge resource.
1414
  git checkout A
1415
  git rm item
1416
  write_lines changed-by-A >item
1417
  git add item
1418
  git commit -m "replace gitlink with A's file"
1419
1420
  git checkout B
1421
  git rm item
1422
  write_lines changed-by-B >item
1423
  git add item
1424
  git commit -m "replace gitlink with B's file"
1425
)
1426
1427
git init both-modify-union-attr
1428
(cd both-modify-union-attr
1429
  mkdir a && write_lines original 1 2 3 4 5 >a/x.f
1430
  echo "a/* merge=union" >.gitattributes
1431
  git add . && git commit -m "original"
1432
1433
  git branch A
1434
  git branch B
1435
1436
  git checkout A
1437
  write_lines A 1 2 3 4 5 6 >a/x.f
1438
  git commit -am "change file"
1439
1440
  git checkout B
1441
  write_lines B 1 2 3 4 5 7 >a/x.f
1442
  git commit -am "change file differently"
1443
)
1444
1445
git init both-modify-binary
1446
(cd both-modify-binary
1447
  mkdir a && printf '\x00 binary' >a/x.f
1448
  git add . && git commit -m "original"
1449
1450
  git branch A
1451
  git branch B
1452
1453
  git checkout A
1454
  printf '\x00 A' >a/x.f
1455
  git commit -am "change binary file"
1456
1457
  git checkout B
1458
  printf '\x00 B' >a/x.f
1459
  git commit -am "change binary file differently"
1460
)
1461
1462
git init both-modify-file-with-binary-attr
1463
(cd both-modify-file-with-binary-attr
1464
  mkdir a && echo 'not binary' >a/x.f
1465
  git add . && git commit -m "original"
1466
1467
  git branch A
1468
  git branch B
1469
1470
  git checkout A
1471
  echo 'A binary' >a/x.f
1472
  git commit -am "change pseudo-binary file"
1473
1474
  git checkout B
1475
  echo 'B binary' >a/x.f
1476
  git commit -am "change pseudo-binary file differently"
1477
)
1478
1479
git init big-file-merge
1480
(cd big-file-merge
1481
  git config --local core.bigFileThreshold 100
1482
  mkdir a && write_lines original 1 2 3 4 5 >a/x.f
1483
  git add . && git commit -m "original"
1484
1485
  git branch A
1486
  git branch B
1487
1488
  git checkout A
1489
  seq 37 >a/x.f
1490
  git commit -am "turn normal file into big one (102 bytes)"
1491
  git branch expected
1492
1493
  git checkout B
1494
  write_lines 1 2 3 4 5 6 >a/x.f
1495
  git commit -am "a normal but conflicting file change"
1496
)
1497
1498
git init no-merge-base
1499
(cd no-merge-base
1500
  git checkout -b A
1501
  echo "A" >content && git add . && git commit -m "content A"
1502
1503
  git checkout --orphan B
1504
  echo "B" >content && git add . && git commit -m "content B"
1505
1506
  git checkout -b expectation
1507
)
1508
1509
git init multiple-merge-bases
1510
(cd multiple-merge-bases
1511
  write_lines 1 2 3 4 5 >content
1512
  git add . && git commit -m "initial"
1513
1514
  git branch A
1515
  git branch B
1516
1517
  git checkout A
1518
  write_lines 0 1 2 3 4 5 >content
1519
  git commit -am "change in A" && git tag A1
1520
1521
  git checkout B
1522
  write_lines 1 2 3 4 5 6 >content
1523
  git commit -am "change in B" && git tag B1
1524
1525
  git checkout A
1526
  git merge B1
1527
1528
  git checkout B
1529
  git merge A1
1530
1531
  git checkout A
1532
  write_lines 0 1 2 3 4 5 A >content
1533
  git commit -am "conflicting in A"
1534
1535
  git checkout B
1536
  git rm content
1537
  write_lines 0 2 3 4 5 six >renamed
1538
  git commit -m "rename in B"
1539
)
1540
1541
git init multiple-merge-bases-with-renamed-dir
1542
(cd multiple-merge-bases-with-renamed-dir
1543
  # `deleted-file-replaced-by-renamed-dir` with two merge bases: building the virtual merge base
1544
  # replays that shape, and only if it keeps `a/b/` do A's and B's edits inside it merge cleanly.
1545
  mkdir a d
1546
  echo original >a/b
1547
  seq 1 10 >d/g
1548
  seq 11 20 >d/h
1549
  seq 21 30 >u
1550
  git add . && git commit -m "initial"
1551
1552
  git branch A
1553
  git branch B
1554
1555
  git checkout A
1556
  git rm a/b
1557
  mkdir -p a/b
1558
  git mv d/g a/b/g
1559
  git mv d/h a/b/h
1560
  git commit -m "replace the file a/b with the moved directory d" && git tag A1
1561
1562
  git checkout B
1563
  seq 21 31 >u
1564
  git commit -am "edit unrelated file" && git tag B1
1565
1566
  git checkout A
1567
  git merge B1
1568
1569
  git checkout B
1570
  git merge A1
1571
1572
  git checkout A
1573
  seq 1 11 >a/b/g
1574
  git commit -am "edit a/b/g in A"
1575
1576
  git checkout B
1577
  seq 11 21 >a/b/h
1578
  git commit -am "edit a/b/h in B"
1579
)
1580
1581
git init rename-and-modification
1582
(cd rename-and-modification
1583
  mkdir a && write_lines original 1 2 3 4 5 >a/x.f
1584
  git add . && git commit -m "original"
1585
1586
  git branch A
1587
  git branch B
1588
1589
  git checkout A
1590
  git mv a/x.f x.f
1591
  git commit -am "move a/x.f to the top-level"
1592
1593
  git checkout B
1594
  write_lines 1 2 3 4 5 6 >a/x.f
1595
  git commit -am "changed a/x.f"
1596
)
1597
1598
git init symlink-modification
1599
(cd symlink-modification
1600
  touch a b o
1601
  ln -s o link
1602
  git add . && git commit -m "original"
1603
1604
  git branch A
1605
  git branch B
1606
1607
  git checkout A
1608
  rm link && ln -s a link
1609
  git commit -am "set link to point to 'a'"
1610
1611
  git checkout B
1612
  rm link && ln -s b link
1613
  git commit -am "set link to point to 'b'"
1614
)
1615
1616
git init symlink-addition
1617
(cd symlink-addition
1618
  touch a b
1619
  git add . && git commit -m "original without symlink"
1620
1621
  git branch A
1622
  git branch B
1623
1624
  git checkout A
1625
  ln -s a link && git add .
1626
  git commit -m "new link to point to 'a'"
1627
1628
  git checkout B
1629
  ln -s b link && git add .
1630
  git commit -m "new link to point to 'b'"
1631
)
1632
1633
git init added-file-vs-added-directory
1634
(cd added-file-vs-added-directory
1635
  git commit --allow-empty -m "empty base"
1636
1637
  git branch A
1638
  git branch B
1639
1640
  # A adds a file at `e`, while B adds a file below the directory `e`. Resolving
1641
  # this tree/non-tree pair removes B's `e/e` path-tree leaf; its now-empty `e`
1642
  # parent must not remain visible as a change during the inverse scheduling pass.
1643
  git checkout A
1644
  write_lines file >e
1645
  git add e
1646
  git commit -m "add file e"
1647
1648
  git checkout B
1649
  mkdir e
1650
  write_lines nested >e/e
1651
  git add e/e
1652
  git commit -m "add directory e"
1653
)
1654
1655
git init added-symlink-blocks-gitlink-directory
1656
(cd added-symlink-blocks-gitlink-directory
1657
  git commit --allow-empty -m "empty base"
1658
  base="$(git rev-parse HEAD)"
1659
1660
  git branch A
1661
  git branch B
1662
1663
  # A adds a non-tree at `d`, while B adds a directory at the same path. The nested
1664
  # gitlink is deliberate: tree/non-tree handling must not accidentally route this
1665
  # structural conflict through the blob or submodule merge cases.
1666
  git checkout A
1667
  ln -s target d
1668
  git add d
1669
  git commit -m "add symlink d"
1670
1671
  git checkout B
1672
  git update-index --index-info <<EOF
1673
160000 commit $base	d/a/d
1674
EOF
1675
  git commit -m "add nested gitlink below d"
1676
)
1677
1678
git init gitlink-vs-renamed-symlink-directory-with-siblings
1679
(cd gitlink-vs-renamed-symlink-directory-with-siblings
1680
  git commit --allow-empty -m "gitlink target"
1681
  root="$(git rev-parse HEAD)"
1682
  mkdir -p a/a
1683
  ln -s target a/a/a
1684
  git add a/a/a
1685
  git update-index --add --cacheinfo 160000,$root,h
1686
  git commit -m "symlink and gitlink base"
1687
1688
  git branch A
1689
  git branch B
1690
1691
  # A replaces the symlink-containing directory with a regular child and keeps
1692
  # the gitlink at `h`. B instead removes that gitlink, moves the symlink below
1693
  # a new `h` directory, and adds a sibling below it. Resolving the resulting
1694
  # type mismatch may consume the sibling's structural path before cleanup.
1695
  git checkout A
1696
  git rm a/a/a
1697
  mkdir a
1698
  write_lines changed >a/b
1699
  git add a/b
1700
  git commit -m "replace the symlink directory"
1701
1702
  git checkout B
1703
  git mv a/a/a moved-link
1704
  git update-index --force-remove h
1705
  mkdir -p h/b
1706
  git mv moved-link h/a
1707
  write_lines sibling >h/b/a
1708
  git add .
1709
  git commit -m "replace the gitlink with a renamed symlink and sibling"
1710
1711
  # gix follows the detected `a` -> `h` directory rename and relocates A's
1712
  # added `a/b` to `h/b~A`. Git leaves the addition at `a/b`; this fixture
1713
  # records the existing semantic difference while guarding the path cleanup.
1714
  git checkout -b expected B
1715
  git update-index --add --cacheinfo "100644,$(git rev-parse A:a/b),h/b~A"
1716
  git commit -m "expected gix merge"
1717
1718
  # FIXME: merge symmetry: only when B is ours, gix also preserves A's blocking
1719
  # gitlink at its unique path.
1720
  git checkout -b expected-reversed
1721
  git update-index --add --cacheinfo "160000,$root,h~B"
1722
  git commit -m "expected reversed gix merge"
1723
)
1724
1725
git init same-source-rewrites-after-consumed-path
1726
(cd same-source-rewrites-after-consumed-path
1727
  mkdir -p a/a
1728
  printf payload >a/a/a
1729
  printf payload >b
1730
  git add .
1731
  git commit -m "identical files at nested and root paths"
1732
1733
  git branch A
1734
  git branch B
1735
1736
  # Every non-directory entry deliberately has the same object ID. This gives
1737
  # rewrite detection several equally valid source/destination pairings. A
1738
  # removes `a/a/a` and reuses its payload at unrelated paths.
1739
  git checkout A
1740
  git rm a/a/a
1741
  mkdir -p e/a g/e
1742
  ln -s payload e/a/g
1743
  printf payload >g/e/a
1744
  git add .
1745
  git commit -m "remove the nested source and add identical entries"
1746
1747
  # B retains `a/a/a`, adds an executable sibling, replaces `b` with a nested
1748
  # copy, and adds another copy. Resolving one ambiguous rewrite can consume
1749
  # the shared source before a later same-source rewrite cleans it up.
1750
  git checkout B
1751
  mkdir -p a/e h/e
1752
  printf payload >a/e/a
1753
  chmod +x a/e/a
1754
  git rm b
1755
  mkdir -p b/b
1756
  printf payload >b/b/f
1757
  printf payload >h/e/a
1758
  git add .
1759
  git update-index --chmod=+x a/e/a
1760
  git commit -m "retain and multiply the identical payload"
1761
1762
  # gix pairs both sides with the same ambiguous base source and carries B's
1763
  # executable mode to A's destination. Git leaves both additions in place.
1764
  git checkout -b expected B
1765
  git update-index --force-remove a/a/a
1766
  git update-index --force-remove a/e/a
1767
  git update-index --add --cacheinfo "120000,$(git rev-parse A:e/a/g),e/a/g"
1768
  git update-index --add --cacheinfo "100755,$(git rev-parse B:a/e/a),g/e/a"
1769
  git commit -m "expected gix merge"
1770
)
1771
1772
git init rename-delete-after-consumed-path
1773
(cd rename-delete-after-consumed-path
1774
  mkdir -p a h/d f e/f
1775
  write_lines shared >a/a
1776
  chmod +x a/a
1777
  write_lines four >b
1778
  write_lines seven >e/f/a
1779
  chmod +x e/f/a
1780
  write_lines shared >f/a
1781
  write_lines shared >h/d/a
1782
  git add .
1783
  git update-index --chmod=+x a/a e/f/a
1784
  git commit -m "base with repeated rename candidates"
1785
1786
  git branch A
1787
  git branch B
1788
1789
  # A moves `h/d/a` below `a`, turns `f/a` into `f`, and replaces `e/`.
1790
  # The repeated `shared` payload deliberately gives rename detection several
1791
  # possible sources, matching the scheduling ambiguity found by the fuzzer.
1792
  git checkout A
1793
  git rm -r a e
1794
  mkdir -p a/a a/d
1795
  write_lines four >a/a/a
1796
  git mv h/d/a a/d/a
1797
  mv f/a moved
1798
  rmdir f
1799
  mv moved f
1800
  write_lines five >d
1801
  write_lines shared >e
1802
  git add -A
1803
  git commit -m "rename repeated payloads and replace directories"
1804
1805
  # B deletes A's rename source and replaces `b`, `e`, and `f` with opposite
1806
  # file/directory shapes. Resolving another rename/delete pair can consume the
1807
  # path node for `h/d/a` before that pending deletion follows a directory rename.
1808
  git checkout B
1809
  git rm -r a b e f h
1810
  mkdir -p b/a
1811
  write_lines shared >b/a/a
1812
  write_lines four >e
1813
  write_lines four >f
1814
  git add .
1815
  git commit -m "delete rename sources and replace directories"
1816
1817
  # Ambiguous identity-only rename pairing makes gix keep a smaller tree than
1818
  # Git and merge the repeated payloads at the surviving paths.
1819
  shared_four=$(
1820
    printf '%s\n' \
1821
      '<<<<<<< A' \
1822
      shared \
1823
      ======= \
1824
      four \
1825
      '>>>>>>> B' |
1826
      git hash-object -w --stdin
1827
  )
1828
  four_shared=$(
1829
    printf '%s\n' \
1830
      '<<<<<<< A' \
1831
      four \
1832
      ======= \
1833
      shared \
1834
      '>>>>>>> B' |
1835
      git hash-object -w --stdin
1836
  )
1837
  git checkout -b expected B
1838
  git read-tree --empty
1839
  git update-index --add --cacheinfo "100644,$four_shared,b/a/a"
1840
  git update-index --add --cacheinfo "100644,$(git rev-parse A:a/d/a),b/d/a"
1841
  git update-index --add --cacheinfo "100644,$(git rev-parse A:d),d"
1842
  git update-index --add --cacheinfo "100644,$shared_four,e"
1843
  git update-index --add --cacheinfo "100644,$shared_four,f"
1844
  git commit -m "expected gix merge"
1845
1846
  reversed_four_shared=$(
1847
    printf '%s\n' \
1848
      '<<<<<<< B' \
1849
      four \
1850
      ======= \
1851
      shared \
1852
      '>>>>>>> A' |
1853
      git hash-object -w --stdin
1854
  )
1855
  reversed_shared_four=$(
1856
    printf '%s\n' \
1857
      '<<<<<<< B' \
1858
      shared \
1859
      ======= \
1860
      four \
1861
      '>>>>>>> A' |
1862
      git hash-object -w --stdin
1863
  )
1864
  # Reversing the merge keeps the same paths and pairings, with directional
1865
  # conflict-marker labels.
1866
  git checkout -f -b expected-reversed B
1867
  git read-tree --empty
1868
  git update-index --add --cacheinfo "100644,$reversed_shared_four,b/a/a"
1869
  git update-index --add --cacheinfo "100644,$(git rev-parse A:a/d/a),b/d/a"
1870
  git update-index --add --cacheinfo "100644,$(git rev-parse A:d),d"
1871
  git update-index --add --cacheinfo "100644,$reversed_four_shared,e"
1872
  git update-index --add --cacheinfo "100644,$reversed_four_shared,f"
1873
  git commit -m "expected reversed gix merge"
1874
)
1875
1876
git init modified-file-vs-gitlink-directory
1877
(cd modified-file-vs-gitlink-directory
1878
  ln -s target a
1879
  git add a
1880
  git commit -m "symlink base"
1881
  base="$(git rev-parse HEAD)"
1882
1883
  git branch A
1884
  git branch B
1885
1886
  # A replaces the symlink with an executable file while B replaces it with a
1887
  # directory. The nested gitlink makes its addition sort before the base-file
1888
  # deletion, exercising merge scheduling independently of diff order.
1889
  git checkout A
1890
  rm a
1891
  write_lines modified >a
1892
  chmod +x a
1893
  git add --chmod=+x a
1894
  git commit -m "replace a with an executable"
1895
1896
  git checkout B
1897
  git rm a
1898
  git update-index --index-info <<EOF
1899
160000 commit $base	a/h
1900
EOF
1901
  git commit -m "replace a with a gitlink directory"
1902
)
1903
1904
git init relocated-addition-blocked-by-rename
1905
(cd relocated-addition-blocked-by-rename
1906
  mkdir -p c
1907
  write_lines base >c/c
1908
  git add .
1909
  git commit -m "file in c"
1910
1911
  git branch A
1912
  git branch B
1913
1914
  # A's exact file rename also implies the directory rename `c` -> `a`. B adds
1915
  # `c/a/c`, so directory-rename handling relocates it to `a/a/c`, where A's
1916
  # renamed file at `a/a` blocks the required directory.
1917
  git checkout A
1918
  mkdir -p a
1919
  git mv c/c a/a
1920
  rmdir c
1921
  git commit -m "rename c/c to a/a"
1922
1923
  git checkout B
1924
  mkdir -p c/a
1925
  write_lines added >c/a/c
1926
  git add .
1927
  git commit -m "add below renamed directory"
1928
)
1929
1930
git init nested-rename-blocks-relocated-addition
1931
(cd nested-rename-blocks-relocated-addition
1932
  mkdir a
1933
  write_lines base >a/a
1934
  git add .
1935
  git commit -m "file in a"
1936
1937
  git branch A
1938
  git branch B
1939
1940
  # Both sides rename the same file. A puts it where its containing directory
1941
  # used to be, while B nests it another level below that directory.
1942
  git checkout A
1943
  git mv a/a moved
1944
  rmdir a
1945
  git mv moved a
1946
  git commit -m "move a/a to a"
1947
1948
  git checkout B
1949
  git mv a/a moved
1950
  mkdir -p a/a
1951
  git mv moved a/a/a
1952
  git commit -m "move a/a to a/a/a"
1953
)
1954
1955
git init directory-rename-vs-directory-to-file
1956
(cd directory-rename-vs-directory-to-file
1957
  mkdir a
1958
  write_lines same >a/a
1959
  git add .
1960
  git commit -m "file in a"
1961
1962
  git branch A
1963
  git branch B
1964
1965
  # A renames the containing directory. B moves its only file to the directory's
1966
  # former path, replacing the directory with that file. This is a different-renames
1967
  # conflict whose tree/non-tree handling must update the rename side's path tree.
1968
  git checkout A
1969
  git mv a e
1970
  git commit -m "rename a to e"
1971
1972
  git checkout B
1973
  git mv a/a moved
1974
  rmdir a
1975
  git mv moved a
1976
  git commit -m "replace a directory with its file"
1977
)
1978
1979
git init directory-rename-vs-renamed-file-replacement
1980
(cd directory-rename-vs-renamed-file-replacement
1981
  mkdir -p h/h
1982
  write_lines nested >h/h/a
1983
  write_lines outside >a
1984
  git add .
1985
  git commit -m "directory and outside file"
1986
1987
  git branch A
1988
  git branch B
1989
1990
  # A moves the directory away. B deletes its contents and moves an unrelated
1991
  # file onto the vacated directory path. Unlike the contained-file variant
1992
  # above, the replacement rename has a distinct source, so it can meet the
1993
  # structural directory rewrite before the nested rename/delete pair does.
1994
  git checkout A
1995
  git mv h/h f
1996
  rmdir h
1997
  git commit -m "rename the directory"
1998
1999
  git checkout B
2000
  git rm h/h/a
2001
  mkdir -p h
2002
  git mv a h/h
2003
  git commit -m "replace the directory with a renamed file"
2004
)
2005
2006
git init unrelated-renames-overlapping-destinations
2007
(cd unrelated-renames-overlapping-destinations
2008
  mkdir -p a/a h/b
2009
  write_lines first >a/a/a
2010
  write_lines second >h/b/a
2011
  git add .
2012
  git commit -m "two files in separate directories"
2013
2014
  git branch A
2015
  git branch B
2016
2017
  # A's directory rename places `h/b/a` below `c`. B independently renames
2018
  # `a/a/a` to the non-tree `c` and renames `h/b/a` elsewhere. The two rename
2019
  # destinations therefore overlap even though their source files are unrelated.
2020
  git checkout A
2021
  git mv h c
2022
  git commit -m "rename h to c"
2023
2024
  git checkout B
2025
  git mv h/b/a moved-h
2026
  git mv a/a/a moved-a
2027
  rmdir a/a
2028
  mkdir -p a
2029
  git mv moved-h a/a
2030
  git mv moved-a c
2031
  git commit -m "rename both files to crossing destinations"
2032
)
2033
2034
git init renamed-file-inside-renamed-directory
2035
(cd renamed-file-inside-renamed-directory
2036
  mkdir -p a/a h/b
2037
  write_lines first >a/a/a
2038
  write_lines second >h/b/a
2039
  git add .
2040
  git commit -m "two files in separate directories"
2041
2042
  git branch A
2043
  git branch B
2044
2045
  # A renames `h` to `c`. B replaces the contents of `h` with a file renamed
2046
  # from elsewhere while moving the original `h/b/a` to `a/a`. Directory-rename
2047
  # handling must therefore defer and relocate a rewrite, not just an addition.
2048
  git checkout A
2049
  git mv h c
2050
  git commit -m "rename h to c"
2051
2052
  git checkout B
2053
  git mv h/b/a moved-h
2054
  git mv a/a/a moved-a
2055
  rmdir a/a h/b h
2056
  mkdir -p a h
2057
  git mv moved-h a/a
2058
  git mv moved-a h/h
2059
  git commit -m "replace a renamed directory with an outside file"
2060
)
2061
2062
git init unrelated-renames-to-same-path-with-type-mismatch
2063
(cd unrelated-renames-to-same-path-with-type-mismatch
2064
  write_lines payload >file-source
2065
  ln -s payload link-source
2066
  git add .
2067
  git commit -m "regular file and symlink"
2068
2069
  git branch A
2070
  git branch B
2071
2072
  # Both sides rename a different base entry to `target`. A's entry is a
2073
  # symlink while B's is a regular file, so their destination cannot be
2074
  # content-merged. Git keeps the symlink at `target` and relocates the regular
2075
  # file to the side-qualified `target~B`, independently of merge direction.
2076
  git checkout A
2077
  git mv link-source target
2078
  git commit -m "rename the symlink to target"
2079
2080
  git checkout B
2081
  git mv file-source target
2082
  git commit -m "rename the regular file to target"
2083
)
2084
2085
git init renamed-file-vs-file-to-directory-with-siblings
2086
(cd renamed-file-vs-file-to-directory-with-siblings
2087
  write_lines base >a
2088
  git add a
2089
  git commit -m "base file"
2090
2091
  git branch A
2092
  git branch B
2093
2094
  # A renames the base file away while B replaces its old path with a directory
2095
  # containing two children. Both children encounter the same rename/delete
2096
  # conflict; handling the first must not make the second try to remove an
2097
  # already-pruned rename-destination node.
2098
  git checkout A
2099
  git mv a h
2100
  git commit -m "rename the file"
2101
2102
  git checkout B
2103
  git rm a
2104
  mkdir a
2105
  write_lines first >a/a
2106
  write_lines second >a/c
2107
  git add .
2108
  git commit -m "replace the file with two children"
2109
)
2110
2111
git init identical-additions-with-different-relations
2112
(cd identical-additions-with-different-relations
2113
  # A's earlier directory deletion shifts the relation ID assigned to the shared
2114
  # addition. The relation is diff-local, so both additions still have one effect.
2115
  mkdir -p a-shift
2116
  echo shift >a-shift/file
2117
  git add . && git commit -m "base"
2118
2119
  git branch A
2120
  git branch B
2121
2122
  git checkout A
2123
  git rm -r a-shift
2124
  mkdir -p b-target
2125
  echo shared-addition >b-target/file
2126
  git add . && git commit -m "delete a directory and add the shared tree"
2127
2128
  git checkout B
2129
  mkdir -p b-target
2130
  echo shared-addition >b-target/file
2131
  git add . && git commit -m "add the shared tree"
2132
)
2133
2134
git init identical-deletions-with-different-relations
2135
(cd identical-deletions-with-different-relations
2136
  # A deletes an earlier directory as well, so the shared deletion receives a
2137
  # different relation ID on each side despite removing the same tree.
2138
  mkdir -p a-shift b-target
2139
  echo shift >a-shift/file
2140
  echo shared-deletion >b-target/file
2141
  git add . && git commit -m "base"
2142
2143
  git branch A
2144
  git branch B
2145
2146
  git checkout A
2147
  git rm -r a-shift b-target
2148
  git commit -m "delete the earlier and shared trees"
2149
2150
  git checkout B
2151
  git rm -r b-target
2152
  git commit -m "delete the shared tree"
2153
)
2154
2155
git init identical-rewrites-with-different-relations
2156
(cd identical-rewrites-with-different-relations
2157
  # Moving the same file into a new directory gives each rewrite a destination
2158
  # relation. A's earlier deletion shifts that ID without changing the rename.
2159
  mkdir -p a-shift
2160
  echo shift >a-shift/file
2161
  echo shared-rewrite >b-source
2162
  git add . && git commit -m "base"
2163
2164
  git branch A
2165
  git branch B
2166
2167
  git checkout A
2168
  git rm -r a-shift
2169
  mkdir c-target
2170
  git mv b-source c-target/file
2171
  mkdir b-source
2172
  echo from-A >b-source/A-only
2173
  git add . && git commit -m "delete an earlier tree and rename the shared file"
2174
2175
  git checkout B
2176
  mkdir c-target
2177
  git mv b-source c-target/file
2178
  mkdir b-source
2179
  echo from-B >b-source/B-only
2180
  git add . && git commit -m "rename the shared file"
2181
)
2182
2183
git init type-change-to-symlink
2184
(cd type-change-to-symlink
2185
  touch a b link
2186
  git add . && git commit -m "original without symlink"
2187
2188
  git branch A
2189
  git branch B
2190
2191
  git checkout A
2192
  git rm link
2193
  ln -s a link && git add .
2194
  git commit -m "new link to point to 'a'"
2195
2196
  git checkout B
2197
  git rm link
2198
  ln -s b link && git add .
2199
  git commit -m "new link to point to 'b'"
2200
)
2201
2202
2203
2204
baseline non-tree-to-tree A-B A B
2205
baseline deleted-file-added-dir A-B A B
2206
baseline deleted-file-added-gitlink-directory A-B A B
2207
baseline tree-to-non-tree A-B A B
2208
baseline tree-to-non-tree-with-rename A-B A B
2209
baseline non-tree-to-tree-with-rename A-B A B
2210
baseline rename-add-same-symlink A-B A B
2211
baseline rename-add-exe-bit-conflict A-B A B
2212
baseline remove-executable-mode A-B A B
2213
baseline simple side-1-3-without-conflict side1 side3
2214
baseline simple fast-forward side1 main
2215
baseline simple no-change main main
2216
baseline simple side-1-3-without-conflict-diff3 side1 side3
2217
baseline simple side-1-2-various-conflicts side1 side2
2218
baseline simple side-1-2-various-conflicts-diff3 side1 side2
2219
baseline simple single-content-conflict side1 side4
2220
baseline simple single-content-conflict-diff3 side1 side4
2221
baseline simple tweak1-side2 tweak1 side2
2222
baseline simple tweak1-side2-diff3 tweak1 side2
2223
baseline simple side-1-unrelated side1 unrelated
2224
baseline simple side-1-unrelated-diff3 side1 unrelated
2225
baseline rename-delete A-B A B
2226
baseline rename-delete A-similar A A
2227
baseline rename-delete B-similar B B
2228
baseline rename-change-matrix A-B A B
2229
baseline same-rename-with-content A-B A B
2230
baseline same-rename-and-file-to-directory A-B A B
2231
baseline renames-to-same-destination A-B A B
2232
baseline identical-renames-to-same-destination A-B A B
2233
baseline identical-renames-to-same-destination-with-mode-change A-B A B "gix resolves the identical content and mode change cleanly, while Git leaves an add/add mode conflict"
2234
baseline deleted-file-added-dir-with-rename A-B A B
2235
baseline deleted-file-replaced-by-renamed-dir A-B A B
2236
baseline renamed-file-replaced-by-renamed-dir A-B A B
2237
baseline rename-add A-B A B
2238
baseline rename-add A-B-diff3 A B
2239
baseline rename-add-symlink A-B A B
2240
baseline rename-add-symlink A-B-diff3 A B
2241
baseline rename-rename-plus-content A-B A B
2242
baseline rename-rename-plus-content A-B-diff3 A B
2243
baseline rename-add-delete A-B A B
2244
baseline rename-rename-delete-delete A-B A B
2245
baseline super-1 A-B A B
2246
baseline super-1 A-B-diff3 A B
2247
baseline super-2 A-B A B
2248
baseline super-2 A-B-diff3 A B
2249
2250
baseline rename-within-rename A-B-deviates A B "Git doesn't detect the rename-nesting, we do neither"
2251
baseline rename-within-rename-2 A-B-deviates A B "Git keeps the doubly-renamed file in both rename destinations as it doesn't compose nested directory renames - we do, and merge contents cleanly in one place, in both merge directions"
2252
baseline conflicting-rename A-B A B
2253
baseline conflicting-rename-2 A-B A B
2254
baseline conflicting-rename-complex A-B A B "Git has different rename tracking - overall result it's still close enough"
2255
2256
baseline same-rename-different-mode A-B A B "Git works for the A/B case, but for B/A it forgets to set the executable bit"
2257
baseline renamed-symlink-with-conflict A-B A B
2258
baseline added-file-changed-content-and-mode A-B A B "We improve on executable bit handling, but loose on diff quality as we are definitely missing some tweaks"
2259
2260
baseline type-change-and-renamed A-B A B
2261
baseline change-and-delete A-B A B
2262
baseline submodule-both-modify A-B A B "The tree-only merge cannot inspect submodule reachability, so it keeps ours as the provisional entry and reports SubmoduleMerge."
2263
baseline gitlink-replaced-by-files A-B A B
2264
baseline both-modify-union-attr A-B A B
2265
baseline both-modify-union-attr A-B-diff3 A B
2266
baseline both-modify-binary A-B A B
2267
baseline both-modify-binary A-B A B
2268
baseline both-modify-file-with-binary-attr A-B A B
2269
baseline big-file-merge A-B A B "Git actually ignores core.bigFileThreshold during merging and tries a normal merge (or binary one) anyway. We don't ignore it and treat big files like binary files" \
2270
                                no-reverse
2271
baseline no-merge-base A-B A B
2272
baseline no-merge-base A-B-diff3 A B
2273
2274
baseline multiple-merge-bases A-B A B
2275
baseline multiple-merge-bases A-B-diff3 A B
2276
baseline multiple-merge-bases-with-renamed-dir A-B A B
2277
2278
baseline rename-and-modification A-B A B
2279
baseline symlink-modification A-B A B
2280
baseline symlink-addition A-B A B
2281
baseline added-file-vs-added-directory A-B A B
2282
baseline added-symlink-blocks-gitlink-directory A-B A B
2283
baseline gitlink-vs-renamed-symlink-directory-with-siblings A-B A B "gix relocates A's addition through the detected directory rename, while Git keeps it at its original path; FIXME: merge symmetry: reversing gix also preserves A's blocking gitlink"
2284
baseline same-source-rewrites-after-consumed-path A-B A B "ambiguous identical blobs make gix pair both sides with one base source, while Git retains both additions"
2285
baseline rename-delete-after-consumed-path A-B A B "ambiguous identical blobs make gix pair rename sources differently than Git"
2286
baseline modified-file-vs-gitlink-directory A-B A B
2287
baseline relocated-addition-blocked-by-rename A-B A B
2288
baseline nested-rename-blocks-relocated-addition A-B A B
2289
baseline directory-rename-vs-directory-to-file A-B A B
2290
baseline directory-rename-vs-renamed-file-replacement A-B A B
2291
baseline unrelated-renames-overlapping-destinations A-B A B
2292
baseline renamed-file-inside-renamed-directory A-B A B
2293
baseline unrelated-renames-to-same-path-with-type-mismatch A-B A B
2294
baseline renamed-file-vs-file-to-directory-with-siblings A-B A B
2295
baseline identical-additions-with-different-relations A-B A B
2296
baseline identical-deletions-with-different-relations A-B A B
2297
baseline identical-rewrites-with-different-relations A-B A B
2298
baseline type-change-to-symlink A-B A B
2299
2300
##
2301
## Only once the tree-merges were performed can we refer to their objects
2302
## when making tree-conflict resolution expectations. It's important
2303
## to get these right.
2304
##
2305
(cd added-file-vs-added-directory
2306
  # The ancestor is empty, so choosing it keeps neither addition.
2307
  git read-tree main
2308
  make_resolve_tree ancestor A B
2309
  make_resolve_tree ancestor B A
2310
2311
  # Choosing ours keeps precisely the side selected by the merge direction.
2312
  git read-tree A
2313
  make_resolve_tree ours A B
2314
  git read-tree B
2315
  make_resolve_tree ours B A
2316
)
2317
2318
(cd deleted-file-added-gitlink-directory
2319
  # Both operations are compatible, so forced conflict resolution changes nothing:
2320
  # the shared deletion applies and B's directory remains in both directions.
2321
  git read-tree B
2322
  make_resolve_tree ancestor A B
2323
  make_resolve_tree ancestor B A
2324
  make_resolve_tree ours A B
2325
  make_resolve_tree ours B A
2326
)
2327
2328
(cd added-symlink-blocks-gitlink-directory
2329
  # The ancestor is empty, so choosing it keeps neither addition in either direction.
2330
  git read-tree main
2331
  make_resolve_tree ancestor A B
2332
  make_resolve_tree ancestor B A
2333
2334
  # Choosing ours keeps precisely the side named first: either A's symlink or B's
2335
  # directory containing the nested gitlink.
2336
  git read-tree A
2337
  make_resolve_tree ours A B
2338
  git read-tree B
2339
  make_resolve_tree ours B A
2340
)
2341
2342
(cd modified-file-vs-gitlink-directory
2343
  # Ancestor resolution restores the original symlink in both directions.
2344
  git read-tree main
2345
  make_resolve_tree ancestor A B
2346
  make_resolve_tree ancestor B A
2347
2348
  # Choosing ours keeps precisely the selected replacement.
2349
  git read-tree A
2350
  make_resolve_tree ours A B
2351
  git read-tree B
2352
  make_resolve_tree ours B A
2353
)
2354
2355
(cd relocated-addition-blocked-by-rename
2356
  # The explicit file rename prevents the inferred directory rename from relocating
2357
  # B's addition through a non-tree. The changes are therefore compatible, and forced
2358
  # conflict resolution has nothing to discard in either direction.
2359
  IFS= read -r -d '' merged_tree_id <A-B.merge-info
2360
  git read-tree "$merged_tree_id"
2361
  make_resolve_tree ancestor A B
2362
  make_resolve_tree ours A B
2363
2364
  IFS= read -r -d '' merged_reversed_tree_id <A-B-reversed.merge-info
2365
  git read-tree "$merged_reversed_tree_id"
2366
  make_resolve_tree ancestor B A
2367
  make_resolve_tree ours B A
2368
)
2369
2370
(cd nested-rename-blocks-relocated-addition
2371
  # gix keeps the rename/rename stages in addition to the file/directory stages
2372
  # that Git reports. The resulting tree and retained side content are identical.
2373
  blob=$(git rev-parse main:a/a)
2374
  rm .git/index
2375
  git update-index --index-info <<EOF
2376
100644 blob $blob 2	a
2377
100644 blob $blob 1	a/a
2378
100644 blob $blob 3	a/a/a
2379
100644 blob $blob 2	a~A
2380
EOF
2381
  make_conflict_index nested-rename-blocks-relocated-addition-A-B
2382
2383
  rm .git/index
2384
  git update-index --index-info <<EOF
2385
100644 blob $blob 3	a
2386
100644 blob $blob 1	a/a
2387
100644 blob $blob 2	a/a/a
2388
100644 blob $blob 3	a~A
2389
EOF
2390
  make_conflict_index nested-rename-blocks-relocated-addition-A-B-reversed
2391
)
2392
2393
(cd directory-rename-vs-directory-to-file
2394
  # Ancestor resolution applies neither rename and restores `a/a`.
2395
  git read-tree main
2396
  make_resolve_tree ancestor A B
2397
  make_resolve_tree ancestor B A
2398
2399
  # Choosing ours keeps precisely the directory rename or directory-to-file
2400
  # replacement selected by the merge direction.
2401
  git read-tree A
2402
  make_resolve_tree ours A B
2403
  git read-tree B
2404
  make_resolve_tree ours B A
2405
)
2406
2407
(cd directory-rename-vs-renamed-file-replacement
2408
  # Git retains the original file as stage 1 at its rename destination. Like the
2409
  # other rename/delete cases, gix records only the side that kept the file.
2410
  IFS= read -r -d '' merged_tree_id <A-B.merge-info
2411
  rm .git/index
2412
  git read-tree "$merged_tree_id"
2413
  git update-index --force-remove f/a
2414
  git update-index --index-info <<EOF
2415
100644 blob $(git rev-parse A:f/a) 2	f/a
2416
EOF
2417
  make_conflict_index directory-rename-vs-renamed-file-replacement-A-B
2418
2419
  rm .git/index
2420
  git read-tree "$merged_tree_id"
2421
  git update-index --force-remove f/a
2422
  git update-index --index-info <<EOF
2423
100644 blob $(git rev-parse A:f/a) 3	f/a
2424
EOF
2425
  make_conflict_index directory-rename-vs-renamed-file-replacement-A-B-reversed
2426
2427
  # Ancestor resolution rejects both conflicting renames and restores the original
2428
  # directory and outside file.
2429
  git read-tree main
2430
  make_resolve_tree ancestor A B
2431
  make_resolve_tree ancestor B A
2432
2433
  # Choosing ours keeps precisely the directory rename or file replacement selected
2434
  # by the merge direction.
2435
  git read-tree A
2436
  make_resolve_tree ours A B
2437
  git read-tree B
2438
  make_resolve_tree ours B A
2439
)
2440
2441
(cd unrelated-renames-overlapping-destinations
2442
  # Ancestor rejects the conflicting rename destinations and keeps both base files.
2443
  git read-tree main
2444
  make_resolve_tree ancestor A B
2445
  make_resolve_tree ancestor B A
2446
2447
  # Choosing ours keeps the complete side selected by the merge direction.
2448
  git read-tree A
2449
  make_resolve_tree ours A B
2450
  git read-tree B
2451
  make_resolve_tree ours B A
2452
)
2453
2454
(cd renamed-file-inside-renamed-directory
2455
  # Git puts the relocated `c/h` into stages 1, 2, and 3 even though all three
2456
  # entries are identical. gix records the successful directory relocation as
2457
  # stage 0 and reserves conflict stages for the genuinely divergent rename.
2458
  rm .git/index
2459
  git update-index --index-info <<EOF
2460
100644 blob $(git rev-parse main:h/b/a) 3	a/a
2461
100644 blob $(git rev-parse main:h/b/a) 2	c/b/a
2462
100644 blob $(git rev-parse main:a/a/a)	c/h
2463
100644 blob $(git rev-parse main:h/b/a) 1	h/b/a
2464
EOF
2465
  make_conflict_index renamed-file-inside-renamed-directory-A-B
2466
2467
  rm .git/index
2468
  git update-index --index-info <<EOF
2469
100644 blob $(git rev-parse main:h/b/a) 2	a/a
2470
100644 blob $(git rev-parse main:h/b/a) 3	c/b/a
2471
100644 blob $(git rev-parse main:a/a/a)	c/h
2472
100644 blob $(git rev-parse main:h/b/a) 1	h/b/a
2473
EOF
2474
  make_conflict_index renamed-file-inside-renamed-directory-A-B-reversed
2475
2476
  # Ancestor rejects the divergent rename of `h/b/a`, but the separate file
2477
  # rename is cleanly relocated through A's directory rename to `c/h`.
2478
  git read-tree main
2479
  git update-index --force-remove a/a/a
2480
  git update-index --add --cacheinfo "100644,$(git rev-parse main:a/a/a),c/h"
2481
  make_resolve_tree ancestor A B
2482
  make_resolve_tree ancestor B A
2483
2484
  # Choosing A still retains the independently relocated file from B.
2485
  git read-tree A
2486
  git update-index --force-remove a/a/a
2487
  git update-index --add --cacheinfo "100644,$(git rev-parse main:a/a/a),c/h"
2488
  make_resolve_tree ours A B
2489
2490
  # Choosing B keeps B's divergent rename, while the other file still follows
2491
  # the independently resolved directory relocation.
2492
  git read-tree B
2493
  git update-index --force-remove h/h
2494
  git update-index --add --cacheinfo "100644,$(git rev-parse main:a/a/a),c/h"
2495
  make_resolve_tree ours B A
2496
)
2497
2498
(cd unrelated-renames-to-same-path-with-type-mismatch
2499
  # Ancestor resolution rejects both incompatible destination renames.
2500
  git read-tree main
2501
  make_resolve_tree ancestor A B
2502
  make_resolve_tree ancestor B A
2503
2504
  # Choosing ours applies only the rename from the side named first.
2505
  git read-tree A
2506
  make_resolve_tree ours A B
2507
  git read-tree B
2508
  make_resolve_tree ours B A
2509
)
2510
2511
(cd renamed-file-vs-file-to-directory-with-siblings
2512
  # Git retains the base file as stage 1 at its rename destination. As in the
2513
  # other rename/delete cases, gix records only the side that kept the file.
2514
  IFS= read -r -d '' merged_tree_id <A-B.merge-info
2515
  rm .git/index
2516
  git read-tree "$merged_tree_id"
2517
  git update-index --force-remove h
2518
  git update-index --index-info <<EOF
2519
100644 blob $(git rev-parse A:h) 2	h
2520
EOF
2521
  make_conflict_index renamed-file-vs-file-to-directory-with-siblings-A-B
2522
2523
  rm .git/index
2524
  git read-tree "$merged_tree_id"
2525
  git update-index --force-remove h
2526
  git update-index --index-info <<EOF
2527
100644 blob $(git rev-parse A:h) 3	h
2528
EOF
2529
  make_conflict_index renamed-file-vs-file-to-directory-with-siblings-A-B-reversed
2530
2531
  # Ancestor resolution rejects the rename and the file-to-directory
2532
  # replacement, restoring the base file.
2533
  git read-tree main
2534
  make_resolve_tree ancestor A B
2535
  make_resolve_tree ancestor B A
2536
2537
  # Choosing A keeps its rename while still accepting B's non-conflicting
2538
  # children at the now-vacant source path.
2539
  IFS= read -r -d '' merged_tree_id <A-B.merge-info
2540
  git read-tree "$merged_tree_id"
2541
  make_resolve_tree ours A B
2542
2543
  # Choosing B rejects A's rename and keeps B's replacement directory.
2544
  git read-tree B
2545
  make_resolve_tree ours B A
2546
)
2547
2548
(cd gitlink-vs-renamed-symlink-directory-with-siblings
2549
  # Git retains the renamed symlink's base stage and leaves `a/b` unconflicted.
2550
  # gix instead records only the surviving rename side and the relocated
2551
  # addition as the two sides of its file/directory conflict.
2552
  rm .git/index
2553
  git update-index --index-info <<EOF
2554
120000 blob $(git rev-parse B:h/a) 3	h/a
2555
100644 blob $(git rev-parse B:h/b/a)	h/b/a
2556
100644 blob $(git rev-parse A:a/b) 2	h/b~A
2557
EOF
2558
  make_conflict_index gitlink-vs-renamed-symlink-directory-with-siblings-A-B
2559
2560
  rm .git/index
2561
  git update-index --index-info <<EOF
2562
120000 blob $(git rev-parse B:h/a) 2	h/a
2563
100644 blob $(git rev-parse B:h/b/a)	h/b/a
2564
100644 blob $(git rev-parse A:a/b) 3	h/b~A
2565
160000 commit $(git rev-parse main:h) 2	h~B
2566
EOF
2567
  make_conflict_index gitlink-vs-renamed-symlink-directory-with-siblings-A-B-reversed
2568
2569
  # Ancestor keeps the renamed symlink at its base path, while the independent
2570
  # sibling below B's replacement directory remains applicable.
2571
  git read-tree main
2572
  git update-index --force-remove h
2573
  git update-index --add --cacheinfo "100644,$(git rev-parse B:h/b/a),h/b/a"
2574
  make_resolve_tree ancestor A B
2575
  git read-tree main
2576
  git update-index --force-remove h
2577
  git update-index --add --cacheinfo "100644,$(git rev-parse B:h/b/a),h/b/a"
2578
  make_resolve_tree ancestor B A
2579
2580
  # With A as ours, its addition follows the detected directory rename while
2581
  # the conflicting gitlink and B's sibling are rejected.
2582
  git read-tree --empty
2583
  git update-index --add --cacheinfo "100644,$(git rev-parse A:a/b),h/b"
2584
  make_resolve_tree ours A B
2585
2586
  # With B as ours, retain B's replacement directory.
2587
  git read-tree B
2588
  make_resolve_tree ours B A
2589
)
2590
2591
(cd rename-delete-after-consumed-path
2592
  # Git associates the repeated blobs with different rename sources. Record
2593
  # gix's structured conflicts explicitly; the worktree tree is documented by
2594
  # the `expected` branches above.
2595
  rm .git/index
2596
  git update-index --index-info <<EOF
2597
100755 blob $(git rev-parse main:a/a) 1	a/a
2598
100644 blob $(git rev-parse A:a/d/a) 2	a/d/a
2599
100644 blob $(git rev-parse A:a/a/a) 2	b/a/a
2600
100644 blob $(git rev-parse B:b/a/a) 3	b/a/a
2601
100644 blob $(git rev-parse A:a/d/a)	b/d/a
2602
100644 blob $(git rev-parse A:d)	d
2603
100644 blob $(git rev-parse A:e) 2	e
2604
100644 blob $(git rev-parse B:e) 3	e
2605
100644 blob $(git rev-parse A:f) 2	f
2606
100644 blob $(git rev-parse B:f) 3	f
2607
EOF
2608
  make_conflict_index rename-delete-after-consumed-path-A-B
2609
2610
  rm .git/index
2611
  git update-index --index-info <<EOF
2612
100755 blob $(git rev-parse main:a/a) 1	a/a
2613
100644 blob $(git rev-parse A:a/d/a) 3	a/d/a
2614
100644 blob $(git rev-parse B:b/a/a) 2	b/a/a
2615
100644 blob $(git rev-parse A:a/a/a) 3	b/a/a
2616
100644 blob $(git rev-parse A:a/d/a)	b/d/a
2617
100644 blob $(git rev-parse A:d)	d
2618
100644 blob $(git rev-parse B:e) 2	e
2619
100644 blob $(git rev-parse A:e) 3	e
2620
100644 blob $(git rev-parse B:f) 2	f
2621
100644 blob $(git rev-parse A:f) 3	f
2622
EOF
2623
  make_conflict_index rename-delete-after-consumed-path-A-B-reversed
2624
)
2625
2626
(cd simple
2627
  rm .git/index
2628
  # 'whatever' is tree-conflict, 'greeting' is content conflict with markers
2629
  git update-index --index-info <<EOF
2630
100644 $(oid 45b983be36b73c0788dc9cbcb76cbb80fc7bb057) 0	greeting
2631
100644 $(oid 09c277aa66897c58157f57a374eacc63a407dcab) 0	numbers
2632
100644 $(oid 5716ca5987cbf97d6bb54920bea6adde242d87e6) 0	whatever
2633
EOF
2634
  make_resolve_tree ours side1 side2
2635
2636
  rm .git/index
2637
  git update-index --index-info <<EOF
2638
100644 $(oid 092bfb9bdf74dd8cfd22e812151281ee9aa6f01a) 0	greeting
2639
100644 $(oid 09c277aa66897c58157f57a374eacc63a407dcab) 0	numbers
2640
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 0	whatever/empty
2641
EOF
2642
  make_resolve_tree ours side2 side1
2643
2644
  rm .git/index
2645
  git update-index --index-info <<EOF
2646
100644 $(oid 9dc97bdc2426e68423360e3e5299280b2cf6b8ff) 0	greeting
2647
100644 $(oid 09c277aa66897c58157f57a374eacc63a407dcab) 0	numbers
2648
100644 $(oid 257cc5642cb1a054f08cc83f2d943e56fd3ebe99) 0	whatever
2649
EOF
2650
  make_resolve_tree ancestor side1 side2
2651
2652
  rm .git/index
2653
  git update-index --index-info <<EOF
2654
100644 $(oid 1a2664a9924754c698e323f756f9f87f3f2fb337) 0	greeting
2655
100644 $(oid 09c277aa66897c58157f57a374eacc63a407dcab) 0	numbers
2656
100644 $(oid 257cc5642cb1a054f08cc83f2d943e56fd3ebe99) 0	whatever
2657
EOF
2658
  make_resolve_tree ancestor side2 side1
2659
2660
  rm .git/index
2661
  git update-index --index-info <<EOF
2662
100644 $(oid a4ae6e4709228b5da6001cb9d1cfa7736851e2a6) 0	greeting
2663
100644 $(oid 257cc5642cb1a054f08cc83f2d943e56fd3ebe99) 0	whatever
2664
100644 $(oid 542802a799ded74fa01c47ba2f8925e284a369e2) 0	Αυτά μου φαίνονται κινέζικα
2665
EOF
2666
  make_resolve_tree ancestor tweak1 side2
2667
2668
  rm .git/index
2669
  git update-index --index-info <<EOF
2670
100644 $(oid 45b983be36b73c0788dc9cbcb76cbb80fc7bb057) 0	greeting
2671
100644 $(oid 5716ca5987cbf97d6bb54920bea6adde242d87e6) 0	whatever
2672
100644 $(oid 65bc6a1e238f4bf05b28fd05240636e2cfb657e0) 0	Αυτά μου φαίνονται κινέζικα
2673
EOF
2674
  make_resolve_tree ours tweak1 side2
2675
2676
  rm .git/index
2677
  git update-index --index-info <<EOF
2678
100644 blob $(oid ea28dcd7f627a2a7bbd09daa679c452180617c9f)	greeting
2679
100644 blob $(oid 257cc5642cb1a054f08cc83f2d943e56fd3ebe99)	whatever
2680
100644 blob $(oid f801a62deed900f8a80ff35e3339474ad6352a93)	Αυτά μου φαίνονται κινέζικα
2681
EOF
2682
  make_resolve_tree ancestor side2 tweak1
2683
2684
  rm .git/index
2685
  git update-index --index-info <<EOF
2686
100644 blob $(oid 092bfb9bdf74dd8cfd22e812151281ee9aa6f01a)	greeting
2687
100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	whatever/empty
2688
100644 blob $(oid 09c277aa66897c58157f57a374eacc63a407dcab)	Αυτά μου φαίνονται κινέζικα
2689
EOF
2690
  make_resolve_tree ours side2 tweak1
2691
)
2692
2693
(cd rename-add-symlink
2694
  rm .git/index
2695
  # the symlink of 'bar' from A
2696
  git update-index --index-info <<EOF
2697
120000 blob $(oid 19102815663d23f8b75a47e7a01965dcdc96468c)	bar
2698
EOF
2699
  make_resolve_tree ours A B
2700
2701
  rm .git/index
2702
  # the merged form of 'bar' from B, not replaced by symlink
2703
  git update-index --index-info <<EOF
2704
100644 blob $(oid b414108e81e5091fe0974a1858b4d0d22b107f70)	bar
2705
EOF
2706
  make_resolve_tree ours B A
2707
2708
  rm .git/index
2709
  # foo is renamed to bar, type clash means neither A nor B can be added - empty tree
2710
  # It is not able to 'get foo back', it can't track that currently.
2711
  make_resolve_tree ancestor A B
2712
  make_resolve_tree ancestor B A
2713
)
2714
2715
(cd deleted-file-added-dir-with-rename
2716
  rm .git/index
2717
  # Unlike Git, we don't retain the base stage at the rename destination.
2718
  git update-index --index-info <<EOF
2719
100644 blob $(oid df967b96a579e45a18b8251732d16804b2e56a55) 3	renamed
2720
100644 blob $(oid d5f7fc3f74f7dec08280f370a975b112e8f60818)	x/a
2721
EOF
2722
  make_conflict_index deleted-file-added-dir-with-rename-A-B
2723
2724
  rm .git/index
2725
  git update-index --index-info <<EOF
2726
100644 blob $(oid df967b96a579e45a18b8251732d16804b2e56a55) 2	renamed
2727
100644 blob $(oid d5f7fc3f74f7dec08280f370a975b112e8f60818)	x/a
2728
EOF
2729
  make_conflict_index deleted-file-added-dir-with-rename-A-B-reversed
2730
2731
  rm .git/index
2732
  git update-index --index-info <<EOF
2733
100644 blob $(oid df967b96a579e45a18b8251732d16804b2e56a55)	x
2734
EOF
2735
  make_resolve_tree ancestor A B
2736
  make_resolve_tree ancestor B A
2737
)
2738
2739
(cd same-rename-and-file-to-directory
2740
  # Forced ours keeps the agreed rename and resolves the added child's content in favor
2741
  # of the current side. Use each complete side tree as the directional expectation.
2742
  git read-tree A
2743
  make_resolve_tree ours A B
2744
2745
  git read-tree B
2746
  make_resolve_tree ours B A
2747
)
2748
2749
(cd rename-change-matrix
2750
  # `different-renames` needs Git's content-conflicted blob at both rename destinations.
2751
  # The first NUL-delimited field is the merged tree written by `git merge-tree`.
2752
  IFS= read -r -d '' merged_tree_id <A-B.merge-info
2753
  different_merged_id=$(git rev-parse "$merged_tree_id:different-renames/ours")
2754
  IFS= read -r -d '' merged_reversed_tree_id <A-B-reversed.merge-info
2755
  different_merged_reversed_id=$(git rev-parse "$merged_reversed_tree_id:different-renames/theirs")
2756
2757
  # Expected A/B index, grouped by the matrix above. It differs from Git only in two places,
2758
  # neither of which alters the merged tree:
2759
  #
2760
  # * delete-source: only A's renamed side remains. Git also places the source's base blob at
2761
  #   stage 1 of the renamed path; gix doesn't invent that path for the base entry.
2762
  # * directory-old: gix considers the added child relocated by the directory rename and records
2763
  #   it at stage 0. Git reports a "file location" conflict and records the relocated child at
2764
  #   stage 3, even though its diagnostic also suggests the same destination.
2765
  #
2766
  # The other matrix entries match Git's index:
2767
  # * add-destination: A's rename and B's addition occupy stages 2 and 3 at `target`.
2768
  # * delete-destination: the old target is stage 1 and A's replacement is stage 2.
2769
  # * different-renames: retain the base source and put the merged conflict blob at both destinations.
2770
  # * modify-destination: the old destination and both replacements occupy stages 1, 2, and 3.
2771
  # * modify-source: the edit follows the rename and resolves cleanly at stage 0.
2772
  rm .git/index
2773
  git update-index --index-info <<EOF
2774
100644 blob $(git rev-parse main:.gitattributes)	.gitattributes
2775
100644 blob $(git rev-parse A:add-destination/target) 2	add-destination/target
2776
100644 blob $(git rev-parse B:add-destination/target) 3	add-destination/target
2777
100644 blob $(git rev-parse main:delete-destination/target) 1	delete-destination/target
2778
100644 blob $(git rev-parse A:delete-destination/target) 2	delete-destination/target
2779
100644 blob $(git rev-parse A:delete-source/renamed) 2	delete-source/renamed
2780
100644 blob $(git rev-parse main:different-renames/file) 1	different-renames/file
2781
100644 blob $different_merged_id 2	different-renames/ours
2782
100644 blob $different_merged_id 3	different-renames/theirs
2783
100644 blob $(git rev-parse B:directory-old/added)	directory-renamed/added
2784
100644 blob $(git rev-parse A:directory-renamed/file)	directory-renamed/file
2785
100644 blob $(git rev-parse main:modify-destination/target) 1	modify-destination/target
2786
100644 blob $(git rev-parse A:modify-destination/target) 2	modify-destination/target
2787
100644 blob $(git rev-parse B:modify-destination/target) 3	modify-destination/target
2788
100644 blob $(git rev-parse B:modify-source/file)	modify-source/renamed
2789
EOF
2790
  make_conflict_index rename-change-matrix-A-B
2791
2792
  # The reversed index has the same shape, with stages 2 and 3 exchanged and directional
2793
  # content-conflict labels reversed.
2794
  rm .git/index
2795
  git update-index --index-info <<EOF
2796
100644 blob $(git rev-parse main:.gitattributes)	.gitattributes
2797
100644 blob $(git rev-parse B:add-destination/target) 2	add-destination/target
2798
100644 blob $(git rev-parse A:add-destination/target) 3	add-destination/target
2799
100644 blob $(git rev-parse main:delete-destination/target) 1	delete-destination/target
2800
100644 blob $(git rev-parse A:delete-destination/target) 3	delete-destination/target
2801
100644 blob $(git rev-parse A:delete-source/renamed) 3	delete-source/renamed
2802
100644 blob $(git rev-parse main:different-renames/file) 1	different-renames/file
2803
100644 blob $different_merged_reversed_id 3	different-renames/ours
2804
100644 blob $different_merged_reversed_id 2	different-renames/theirs
2805
100644 blob $(git rev-parse B:directory-old/added)	directory-renamed/added
2806
100644 blob $(git rev-parse A:directory-renamed/file)	directory-renamed/file
2807
100644 blob $(git rev-parse main:modify-destination/target) 1	modify-destination/target
2808
100644 blob $(git rev-parse B:modify-destination/target) 2	modify-destination/target
2809
100644 blob $(git rev-parse A:modify-destination/target) 3	modify-destination/target
2810
100644 blob $(git rev-parse B:modify-source/file)	modify-source/renamed
2811
EOF
2812
  make_conflict_index rename-change-matrix-A-B-reversed
2813
)
2814
2815
(cd same-rename-with-content
2816
  # The merged tree matches Git, but the conflict index differs. Git puts the base and the
2817
  # original A and B blobs at stages 1, 2, and 3 of `target`. gix instead keeps the base at
2818
  # its original path `file` and puts the already-merged conflict blob into both destination
2819
  # stages. The structured conflict still retains the original side entries.
2820
  rm .git/index
2821
  git update-index --index-info <<EOF
2822
100644 blob $(git rev-parse main:file) 1	file
2823
100644 blob $(git rev-parse expected:target) 2	target
2824
100644 blob $(git rev-parse expected:target) 3	target
2825
EOF
2826
  make_conflict_index same-rename-with-content-A-B
2827
2828
  rm .git/index
2829
  git update-index --index-info <<EOF
2830
100644 blob $(git rev-parse main:file) 1	file
2831
100644 blob $(git rev-parse expected-reversed:target) 2	target
2832
100644 blob $(git rev-parse expected-reversed:target) 3	target
2833
EOF
2834
  make_conflict_index same-rename-with-content-A-B-reversed
2835
)
2836
2837
(cd renames-to-same-destination
2838
  # Git leaves this unresolved as an add/add at `target`, with A at stage 2 and B at stage 3
2839
  # and neither original path in the index. The following trees instead record gix's explicit
2840
  # conflict-resolution modes.
2841
  #
2842
  # Ancestor resolution applies neither rename, so both original files remain in both directions.
2843
  rm .git/index
2844
  git update-index --index-info <<EOF
2845
100644 blob $(git rev-parse main:one)	one
2846
100644 blob $(git rev-parse main:two)	two
2847
EOF
2848
  make_resolve_tree ancestor A B
2849
  make_resolve_tree ancestor B A
2850
2851
  # "Ours" for A/B applies A's `one` -> `target` and leaves B's source `two` untouched,
2852
  # unlike Git's unresolved index described above.
2853
  rm .git/index
2854
  git update-index --index-info <<EOF
2855
100644 blob $(git rev-parse A:target)	target
2856
100644 blob $(git rev-parse main:two)	two
2857
EOF
2858
  make_resolve_tree ours A B
2859
2860
  # Reversing the merge makes B ours: apply `two` -> `target` and leave `one` untouched,
2861
  # again replacing Git's unresolved add/add with a resolved tree.
2862
  rm .git/index
2863
  git update-index --index-info <<EOF
2864
100644 blob $(git rev-parse main:one)	one
2865
100644 blob $(git rev-parse B:target)	target
2866
EOF
2867
  make_resolve_tree ours B A
2868
)
2869
2870
(cd identical-renames-to-same-destination
2871
  # Identical entries make the two renames compatible rather than a tree
2872
  # conflict, so conflict-resolution policy must not alter the clean result.
2873
  IFS= read -r -d '' merged_tree_id <A-B.merge-info
2874
  git read-tree "$merged_tree_id"
2875
  make_resolve_tree ancestor A B
2876
  make_resolve_tree ancestor B A
2877
  make_resolve_tree ours A B
2878
  make_resolve_tree ours B A
2879
)
2880
2881
(cd identical-renames-to-same-destination-with-mode-change
2882
  # Git leaves the two destination modes at stages 2 and 3. gix resolves the
2883
  # identical content and the one-sided executable change into a stage-0 entry.
2884
  git read-tree expected
2885
  make_conflict_index identical-renames-to-same-destination-with-mode-change-A-B
2886
  make_conflict_index identical-renames-to-same-destination-with-mode-change-A-B-reversed
2887
2888
  # Ancestor rejects both colliding renames.
2889
  git read-tree main
2890
  make_resolve_tree ancestor A B
2891
  make_resolve_tree ancestor B A
2892
2893
  # Ours applies the selected side's rename and retains the other source.
2894
  git read-tree A
2895
  make_resolve_tree ours A B
2896
  git read-tree B
2897
  make_resolve_tree ours B A
2898
)
2899
2900
(cd rename-rename-plus-content
2901
  rm .git/index
2902
  # both sides rename 'foo' into something else.
2903
  git update-index --index-info <<EOF
2904
100644 blob $(oid 8a1218a1024a212bb3db30becd860315f9f3ac52)	foo
2905
EOF
2906
  make_resolve_tree ancestor A B
2907
  make_resolve_tree ancestor B A
2908
2909
  rm .git/index
2910
  # 'bar' is the name in 'A', and there is a merge with the content from 'B'
2911
  # which we auto-resolve.
2912
  git update-index --index-info <<EOF
2913
100644 blob $(oid d0549c3d3c96a464289f3b820b7d96aedc58924b)	bar
2914
EOF
2915
  make_resolve_tree ours A B
2916
2917
  rm .git/index
2918
  # 'baz' is the name in 'B', and there is a merge with the content from 'A'
2919
  # which we auto-resolve.
2920
  git update-index --index-info <<EOF
2921
100644 blob $(oid b414108e81e5091fe0974a1858b4d0d22b107f70)	baz
2922
EOF
2923
  make_resolve_tree ours B A
2924
)
2925
2926
(cd rename-add-delete
2927
  rm .git/index
2928
  # 'foo' is deleted, 'bar' is added in 'A', but renamed to 'bar' in 'B'.
2929
  # Do nothing, *should* keep 'foo', and it's re-added later, which copies it.
2930
  # But instead we keep foo the first time, but then it 'turns' and we process a remaining
2931
  # addition that once again sees the rename from the other side, which is not a conflict
2932
  # and thus removes 'foo' after all, and merges 'bar'.
2933
  # It's not super-correct, but it's only an issue for virtual merge bases, which are kind of
2934
  # hidden anyway.
2935
  git update-index --index-info <<EOF
2936
100644 blob $(oid 0cde534c2fca6c92c07b3e7a696665e844b9b933)	bar
2937
EOF
2938
  make_resolve_tree ancestor A B
2939
  # B A isn't tested, as it is not an Err() conflict.
2940
2941
  # This case ends up being exactly the same as the ancestor, but with the merge-conflict
2942
  # auto-resolved to 'ours'.
2943
  # This time, it's expected to not have 'foo', but 'ours' in the clashing pair is a deletion. The rename side
2944
  # is dropped, but what's left is the rename/add pair once the algorithm turns around/flips.
2945
  rm .git/index
2946
  git update-index --index-info <<EOF
2947
100644 blob $(oid f286e5cdd97ac6895438ea4548638bb98ac9bd6b)	bar
2948
EOF
2949
  make_resolve_tree ours A B
2950
)
2951
2952
(cd rename-rename-delete-delete
2953
  rm .git/index
2954
  # 'A' deletes 'bar' and 'B' turns 'bar' into 'baz'. 'A' renames 'foo' into 'bar', and
2955
  # 'B' deletes 'foo'. 'ancestor' resolves to avoid any edits, leaving the state from 'main'.
2956
  git update-index --index-info <<EOF
2957
100644 blob $(oid 5716ca5987cbf97d6bb54920bea6adde242d87e6)	bar
2958
100644 blob $(oid 257cc5642cb1a054f08cc83f2d943e56fd3ebe99)	foo
2959
EOF
2960
  make_resolve_tree ancestor A B
2961
  # this works in reverse as well (this time).
2962
  make_resolve_tree ancestor B A
2963
2964
  rm .git/index
2965
  # As 'ours' is a deletion of 'foo', it goes through, but we also acknowledge 'theirs'
2966
  # as it gives better results, so end up with `baz`.
2967
  git update-index --index-info <<EOF
2968
100644 blob $(oid 257cc5642cb1a054f08cc83f2d943e56fd3ebe99)	baz
2969
EOF
2970
  make_resolve_tree ours A B
2971
2972
  rm .git/index
2973
  # Here we end up in exactly the same spot as if we'd do a normal merge,
2974
  # which ends `baz` in a conflict. However, with content-merges set to 'ours'
2975
  # it ends up like it should, giving a good result.
2976
  git update-index --index-info <<EOF
2977
  100644 blob $(oid 5716ca5987cbf97d6bb54920bea6adde242d87e6)	baz
2978
EOF
2979
  make_resolve_tree ours B A
2980
)
2981
2982
(cd super-1
2983
  # Each of the ancestor files are renamed in a conflicting way, and here
2984
  # with ancestor choice, nothing happens, making this equivalent to `main`
2985
  git checkout main
2986
  make_resolve_tree ancestor A B
2987
  make_resolve_tree ancestor B A
2988
2989
  rm .git/index
2990
  # We do indeed perform the renames like this, and the content is merges as well as possible,
2991
  # (here) configured to content-merge with 'ours' as well where needed.
2992
  git update-index --index-info <<EOF
2993
100644 blob $(oid 4b5599c7c2ed4390417d9699bec86144a386873d)	four
2994
100644 blob $(oid 64012489f118cb4011c8902b4a635f70dcb0c0ca)	six
2995
100644 blob $(oid e33f5e94470d3b5fa0220ff6a9cabb78a3f72fa3)	two
2996
EOF
2997
  make_resolve_tree ours A B
2998
2999
  rm .git/index
3000
  # The same, but from the other side.
3001
  git update-index --index-info <<EOF
3002
  100644 blob $(oid 64012489f118cb4011c8902b4a635f70dcb0c0ca)	four
3003
  100644 blob $(oid e33f5e94470d3b5fa0220ff6a9cabb78a3f72fa3)	six
3004
  100644 blob $(oid 4178ea6795c4c3e07b4e17e6a04aa49584b07ecd)	two
3005
EOF
3006
  make_resolve_tree ours B A
3007
)
3008
3009
(cd super-2
3010
  rm .git/index
3011
  # 'B' changes foo, and moves it into 'olddir/bar', but `A' deleted 'foo', and adds 'newdir/bar/file'
3012
  # after renaming 'olddir' to 'newdir'.
3013
  # As `B` only has a single change that gets dropped when it clashes with the deletion of 'foo',
3014
  # all other changes of 'A' can just be applied without any conflict whatsoever.
3015
  git update-index --index-info <<EOF
3016
100644 blob $(oid 8a1218a1024a212bb3db30becd860315f9f3ac52)	foo
3017
100644 blob $(oid 78981922613b2afb6025042ff6bd878ac1994e85)	newdir/a
3018
100644 blob $(oid 61780798228d17af2d34fce4cfbdf35556832472)	newdir/b
3019
100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	newdir/bar/file
3020
100644 blob $(oid f2ad6c76f0115a6ba5b00456a849810e7ec0af20)	newdir/c
3021
EOF
3022
  make_resolve_tree ancestor A B
3023
  make_resolve_tree ancestor B A
3024
3025
  rm .git/index
3026
  # Similar to the ancestor version, but now we choose 'ours', so the rename of 'foo' gets
3027
  # dropped and it just gets deleted. Everything else is then 'A'.
3028
  git update-index --index-info <<EOF
3029
100644 blob $(oid 78981922613b2afb6025042ff6bd878ac1994e85)	newdir/a
3030
100644 blob $(oid 61780798228d17af2d34fce4cfbdf35556832472)	newdir/b
3031
100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	newdir/bar/file
3032
100644 blob $(oid f2ad6c76f0115a6ba5b00456a849810e7ec0af20)	newdir/c
3033
EOF
3034
  make_resolve_tree ours A B
3035
3036
  rm .git/index
3037
  # 'B' changes 'foo' and moves it to 'olddir/bar', which gets tracked to be
3038
  # 'newdir/bar' and is taken verbatim. The clash that it finds it
3039
  # resolves in 'B's favor, leaving only 'newdir/bar'.
3040
  git update-index --index-info <<EOF
3041
100644 blob $(oid 78981922613b2afb6025042ff6bd878ac1994e85)	newdir/a
3042
100644 blob $(oid 61780798228d17af2d34fce4cfbdf35556832472)	newdir/b
3043
100644 blob $(oid b414108e81e5091fe0974a1858b4d0d22b107f70)	newdir/bar
3044
100644 blob $(oid f2ad6c76f0115a6ba5b00456a849810e7ec0af20)	newdir/c
3045
EOF
3046
  make_resolve_tree ours B A
3047
)
3048
3049
(cd conflicting-rename
3050
  rm .git/index
3051
  # 'A' renames 'a' to 'a-renamed', 'B' renames 'a' to 'a-different'.
3052
  # All these conflicts are dropped in favor of keeping the 'ancestor' *location*.
3053
  git update-index --index-info <<EOF
3054
100644 blob $(oid 44065282f89b9bd6439ed2e4674721383fd987eb)	a/sub/y.f
3055
100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a/sub/z
3056
100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a/w
3057
100644 blob $(oid 44065282f89b9bd6439ed2e4674721383fd987eb)	a/x.f
3058
EOF
3059
  make_resolve_tree ancestor A B
3060
  make_resolve_tree ancestor B A
3061
3062
  rm .git/index
3063
  # Much like the ancestor version, except that it applied the 'A' rename,
3064
  # along with its *merged* content.
3065
  git update-index --index-info <<EOF
3066
100644 blob $(oid b414108e81e5091fe0974a1858b4d0d22b107f70)	a-renamed/sub/y.f
3067
100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a-renamed/sub/z
3068
100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a-renamed/w
3069
100644 blob $(oid b414108e81e5091fe0974a1858b4d0d22b107f70)	a-renamed/x.f
3070
EOF
3071
  make_resolve_tree ours A B
3072
  rm .git/index
3073
  # Just like 'A' above, but with the 'B' rename chosen and all the merges.
3074
  git update-index --index-info <<EOF
3075
100644 blob $(oid b414108e81e5091fe0974a1858b4d0d22b107f70)	a-different/sub/y.f
3076
100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a-different/sub/z
3077
100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a-different/w
3078
100644 blob $(oid b414108e81e5091fe0974a1858b4d0d22b107f70)	a-different/x.f
3079
EOF
3080
  make_resolve_tree ours B A
3081
)
3082
3083
(cd conflicting-rename-2
3084
  rm .git/index
3085
  # Like 'conflicting-rename', but this one only renames a single sub-directory for very much the same effect.
3086
  # Thus, keeping the 'ancestor' version is the same as 'main', except for merged content.
3087
  git update-index --index-info <<EOF
3088
100644 blob $(oid 44065282f89b9bd6439ed2e4674721383fd987eb)	a/sub/y.f
3089
100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a/sub/z
3090
100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a/w
3091
100644 blob $(oid b414108e81e5091fe0974a1858b4d0d22b107f70)	a/x.f
3092
EOF
3093
  make_resolve_tree ancestor A B
3094
  make_resolve_tree ancestor B A
3095
3096
  rm .git/index
3097
  git update-index --index-info <<EOF
3098
100644 blob $(oid b414108e81e5091fe0974a1858b4d0d22b107f70)	a/sub-renamed/y.f
3099
100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a/sub-renamed/z
3100
100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a/w
3101
100644 blob $(oid b414108e81e5091fe0974a1858b4d0d22b107f70)	a/x.f
3102
EOF
3103
  make_resolve_tree ours A B
3104
  rm .git/index
3105
  git update-index --index-info <<EOF
3106
100644 blob $(oid b414108e81e5091fe0974a1858b4d0d22b107f70)	a/sub-different/y.f
3107
100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a/sub-different/z
3108
100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a/w
3109
100644 blob $(oid b414108e81e5091fe0974a1858b4d0d22b107f70)	a/x.f
3110
EOF
3111
  make_resolve_tree ours B A
3112
)
3113
3114
(cd conflicting-rename-complex
3115
  rm .git/index
3116
  # 'A" renames 'a' to 'a-renamed', but 'B' moves 'a/sub/' up one level, and replaces everything in its wake
3117
  # so its two files are the only ones left.
3118
  # As result, we actually have one unconflicting change which ends up creating the new directory 'a-renamed',
3119
  # but everything else is conflicting so it keeps the 'ancestor' version.
3120
  git update-index --index-info <<EOF
3121
100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a-renamed/z
3122
100644 blob $(oid 44065282f89b9bd6439ed2e4674721383fd987eb)	a/sub/y.f
3123
100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a/sub/z
3124
100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a/w
3125
100644 blob $(oid 44065282f89b9bd6439ed2e4674721383fd987eb)	a/x.f
3126
EOF
3127
  make_resolve_tree ancestor A B
3128
  make_resolve_tree ancestor B A
3129
3130
  rm .git/index
3131
  # 'Ours' keeps its own version of the files 'B' deleted along with its own rename destinations,
3132
  # where the cleanly mergeable 'y.f' content is still used. Of 'theirs', only the unconflicting
3133
  # composed rename destination 'a-renamed/z' remains.
3134
  git update-index --index-info <<EOF
3135
  100644 blob $(oid b414108e81e5091fe0974a1858b4d0d22b107f70)	a-renamed/sub/y.f
3136
  100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a-renamed/sub/z
3137
  100644 blob $(oid 8a1218a1024a212bb3db30becd860315f9f3ac52)	a-renamed/x.f
3138
  100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a-renamed/z
3139
  100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a-renamed/w
3140
EOF
3141
  make_resolve_tree ours A B
3142
3143
  rm .git/index
3144
  # It applies the merged result of the content, and interestingly also managed to reconcile the rename from 'A'.
3145
  # However, it also drops all of 'their' conflicting changes in favor of 'ours', a respectable result.
3146
  git update-index --index-info <<EOF
3147
  100644 blob $(oid b414108e81e5091fe0974a1858b4d0d22b107f70)	a-renamed/y.f
3148
  100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a-renamed/z
3149
EOF
3150
  make_resolve_tree ours B A
3151
)
3152
3153
(cd renamed-symlink-with-conflict
3154
  rm .git/index
3155
  # 'A' changes 'a/x.f' and renames the 'link', while 'B' also changes 'a/x.f' in a mergable fashion,
3156
  # while renaming 'link' to something else which is where the conflict comes from.
3157
  # Choosing the 'ancestor' means to not rename 'link' at all, while merging the file.
3158
  git update-index --index-info <<EOF
3159
100644 blob $(oid b414108e81e5091fe0974a1858b4d0d22b107f70)	a/x.f
3160
120000 blob $(oid e29fa63dae4ccf0788897a7025da868083178fdf)	link
3161
EOF
3162
  make_resolve_tree ancestor A B
3163
  make_resolve_tree ancestor B A
3164
3165
  rm .git/index
3166
  # Here we choose the name of 'link' in 'A'.
3167
  git update-index --index-info <<EOF
3168
100644 blob $(oid b414108e81e5091fe0974a1858b4d0d22b107f70)	a/x.f
3169
120000 blob $(oid e29fa63dae4ccf0788897a7025da868083178fdf)	link-renamed
3170
EOF
3171
  make_resolve_tree ours A B
3172
3173
  rm .git/index
3174
  # Here we choose the name of 'link' in 'B'.
3175
  git update-index --index-info <<EOF
3176
100644 blob $(oid b414108e81e5091fe0974a1858b4d0d22b107f70)	a/x.f
3177
120000 blob $(oid e29fa63dae4ccf0788897a7025da868083178fdf)	link-different
3178
EOF
3179
  make_resolve_tree ours B A
3180
)
3181
3182
(cd type-change-and-renamed
3183
  rm .git/index
3184
  # 'A' changes `link` to a file, while 'B' keeps the link, but renames it.
3185
  # 'ancestor' just keeps the original version of 'link'
3186
  git update-index --index-info <<EOF
3187
100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a/x.f
3188
120000 blob $(oid e29fa63dae4ccf0788897a7025da868083178fdf)	link
3189
EOF
3190
  make_resolve_tree ancestor A B
3191
  make_resolve_tree ancestor B A
3192
3193
  rm .git/index
3194
  # 'A' changes the type of 'link' to be a file, and that's what's used here.
3195
  git update-index --index-info <<EOF
3196
100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a/x.f
3197
100644 blob $(oid f89a08d1e226b9a319210641b63b07dcf0bd705f)	link
3198
EOF
3199
  make_resolve_tree ours A B
3200
3201
  rm .git/index
3202
  # 'B' renames the link, and that is picked up as well.
3203
  git update-index --index-info <<EOF
3204
100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a/x.f
3205
120000 blob $(oid e29fa63dae4ccf0788897a7025da868083178fdf)	link-renamed
3206
EOF
3207
  make_resolve_tree ours B A
3208
)
3209
3210
(cd change-and-delete
3211
  rm .git/index
3212
  # 'A' changes 'link' to be a file, and changes the file, while 'B' deletes everything,
3213
  # causing each file to be irreconcilable.
3214
  # 'ancestor' keeps everything as is.
3215
  git update-index --index-info <<EOF
3216
100644 blob $(oid 44065282f89b9bd6439ed2e4674721383fd987eb)	a/x.f
3217
120000 blob $(oid e29fa63dae4ccf0788897a7025da868083178fdf)	link
3218
EOF
3219
  make_resolve_tree ancestor A B
3220
  make_resolve_tree ancestor B A
3221
3222
  rm .git/index
3223
  # 'A' changes everything, and that's the change we keep.
3224
  git update-index --index-info <<EOF
3225
100644 blob $(oid b414108e81e5091fe0974a1858b4d0d22b107f70)	a/x.f
3226
100644 blob $(oid f89a08d1e226b9a319210641b63b07dcf0bd705f)	link
3227
EOF
3228
  make_resolve_tree ours A B
3229
3230
  rm .git/index
3231
  # 'B' deletes everything, which is what we keep.
3232
  make_resolve_tree ours B A
3233
)
3234
3235
(cd submodule-both-modify
3236
  rm .git/index
3237
  # There is only one submodule. 'A' and 'B' change it in a fast-forwardable manner,
3238
  # but we can't handle this at all yet, and thus have to consider it irreconcilable.
3239
  # The 'ancestor' resolution just keeps what was.
3240
  git update-index --index-info <<EOF
3241
160000 commit $(oid e835c0c403c8e494c0ca98f3d25d0b8464c18d38)	sub
3242
EOF
3243
  make_resolve_tree ancestor A B
3244
  make_resolve_tree ancestor B A
3245
3246
  rm .git/index
3247
  # Otherwise it's the state of 'A'.
3248
  git update-index --index-info <<EOF
3249
160000 commit $(oid 64466ebdff775ad618d9cc993cf52840e0af528c)	sub
3250
EOF
3251
  make_resolve_tree ours A B
3252
3253
  rm .git/index
3254
  # Otherwise it's the state of 'B'.
3255
  git update-index --index-info <<EOF
3256
160000 commit $(oid ea6eb701e03c2497915c25a851f3da8f8e362ca0)	sub
3257
EOF
3258
  make_resolve_tree ours B A
3259
)
3260
3261
(cd gitlink-replaced-by-files
3262
  # Resolving tree conflicts with the ancestor does not resolve the content
3263
  # conflict, so retain Git's directional conflict-marker trees.
3264
  IFS= read -r -d '' merged_tree_id <A-B.merge-info
3265
  git read-tree "$merged_tree_id"
3266
  make_resolve_tree ancestor A B
3267
  IFS= read -r -d '' merged_reversed_tree_id <A-B-reversed.merge-info
3268
  git read-tree "$merged_reversed_tree_id"
3269
  make_resolve_tree ancestor B A
3270
3271
  # ResolveWith::Ours also configures the blob merge to keep the current side.
3272
  git read-tree A
3273
  make_resolve_tree ours A B
3274
  git read-tree B
3275
  make_resolve_tree ours B A
3276
)
3277
3278
(cd multiple-merge-bases
3279
  rm .git/index
3280
  # 'A' modifies and 'B' deletes the single file in the tree.
3281
  # 'ancestor' keeps the original, which is already the result of the merge of
3282
  # the merge-bases.
3283
  git update-index --index-info <<EOF
3284
100644 blob $(oid 09c277aa66897c58157f57a374eacc63a407dcab)	content
3285
EOF
3286
  make_resolve_tree ancestor A B
3287
  make_resolve_tree ancestor B A
3288
3289
  rm .git/index
3290
  # 'A' keeps the modified version.
3291
  git update-index --index-info <<EOF
3292
100644 blob $(oid 0a6a0ba83635bc00e7c79a4b5b6e50381385c1af)	content
3293
EOF
3294
  make_resolve_tree ours A B
3295
3296
  rm .git/index
3297
  # 'B' applies the deletion to get an empty tree.
3298
  make_resolve_tree ours B A
3299
)
3300
3301
(cd non-tree-to-tree
3302
  rm .git/index
3303
  # 'A' changes the single file 'a', while 'B' replaces it with a directory structure 'a',
3304
  # without a rename though.
3305
  # We manage to pick the 'ancestor', just a single file, while discarding all follow-up changes.
3306
  git update-index --index-info <<EOF
3307
100644 blob $(oid 44065282f89b9bd6439ed2e4674721383fd987eb)	a
3308
EOF
3309
  make_resolve_tree ancestor A B
3310
  make_resolve_tree ancestor B A
3311
3312
  rm .git/index
3313
  # Picks 'A' which is just a single, modified (and mergable) file.
3314
  git update-index --index-info <<EOF
3315
100644 blob $(oid b414108e81e5091fe0974a1858b4d0d22b107f70)	a
3316
EOF
3317
  make_resolve_tree ours A B
3318
3319
  rm .git/index
3320
  # Picks 'B' which is a whole directory tree.
3321
  git update-index --index-info <<EOF
3322
100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a/d
3323
100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a/e
3324
100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a/sub/b
3325
100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a/sub/c
3326
EOF
3327
  make_resolve_tree ours B A
3328
)
3329
3330
(cd tree-to-non-tree
3331
  rm .git/index
3332
  # 'A' modifies a nested file 'a/sub/b', while 'B' replaces 'a/' with file 'a'.
3333
  # Ignore *their* changes for 'ancestor' resolution, and the modification,
3334
  # but apply all others which are deletions.
3335
  git update-index --index-info <<EOF
3336
100644 blob $(oid 44065282f89b9bd6439ed2e4674721383fd987eb)	a/sub/b
3337
EOF
3338
  make_resolve_tree ancestor A B
3339
  make_resolve_tree ancestor B A
3340
  # *ours* is the same as ancestor, we want to keep the tree and changes, but it only
3341
  # applies to the one modification that protects the change.
3342
  rm .git/index
3343
  git update-index --index-info <<EOF
3344
100644 blob $(oid b414108e81e5091fe0974a1858b4d0d22b107f70)	a/sub/b
3345
EOF
3346
  make_resolve_tree ours A B
3347
3348
  rm .git/index
3349
  # Now *ours* is the single file which replaces a tree.
3350
  git update-index --index-info <<EOF
3351
100644 blob $(oid fa49b077972391ad58037050f2a75f74e3671e92)	a
3352
EOF
3353
  make_resolve_tree ours B A
3354
)
3355
3356
(cd tree-to-non-tree-with-rename
3357
  rm .git/index
3358
  # 'A' modifies a nested file 'a/sub/b', while 'B' replaces 'a/' with file 'a'.
3359
  # I let it pass as it's an edge-case to some extent.
3360
  git update-index --index-info <<EOF
3361
100644 blob $(oid 44065282f89b9bd6439ed2e4674721383fd987eb)	a/sub/b
3362
EOF
3363
  make_resolve_tree ancestor A B
3364
  make_resolve_tree ancestor B A
3365
3366
  rm .git/index
3367
  # Thanks to the rename, this version keeps one additional file, 'a/e'
3368
  git update-index --index-info <<EOF
3369
  100644 blob $(oid b414108e81e5091fe0974a1858b4d0d22b107f70)	a/sub/b
3370
EOF
3371
  make_resolve_tree ours A B
3372
3373
  rm .git/index
3374
  # Now *ours* is the single file which replaces a tree.
3375
  git update-index --index-info <<EOF
3376
100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a
3377
EOF
3378
  make_resolve_tree ours B A
3379
)
3380
3381
(cd non-tree-to-tree-with-rename
3382
  # 'A' changes the single file 'a', while 'B' replaces it with a directory structure 'a'.
3383
  # The rename now sends this off-course, as it removes the previously 'protective' entry
3384
  # and thus makes all changes from 'B' succeed without us detecting any problem with that.
3385
  # Also, here we don't actually have irreconcilable tree-changes because of that.
3386
  rm .git/index
3387
  git update-index --index-info <<EOF
3388
100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a/d
3389
100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a/e
3390
100644 blob $(oid b414108e81e5091fe0974a1858b4d0d22b107f70)	a/sub/b
3391
100644 blob $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)	a/sub/c
3392
EOF
3393
  make_resolve_tree ancestor A B
3394
  make_resolve_tree ancestor B A
3395
3396
  # Thanks to the rename, this time there isn't even an irreconcilable conflict.
3397
  # This is deactivated as the test-suite can't handle this (one) special case.
3398
  # The state really is the one from the ancestors, as there are no irreconcilable
3399
  # tree changes, it's all the same. But the test expects to see changes when 'ours'
3400
  # is chosen so we can't easily run this here.
3401
  #  make_resolve_tree ours A B
3402
  #  make_resolve_tree ours B A
3403
)
3404
3405
(cd rename-within-rename
3406
  # 'A' and 'B' change all content in a mergable manner. 'A' renames 'a' to 'a-renamed',
3407
  # and 'B' renames 'a/sub' to 'a/sub-renamed'.
3408
  # Ideally, we get both together, but doing so added a lot of complexity so maybe give
3409
  # that another go and try to keep it simple.
3410
  # In ancestor mode, only those ancestors of conflicts are kept unchanged, so some renames
3411
  # go through.
3412
  rm .git/index
3413
  git update-index --index-info <<EOF
3414
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 0	a-renamed/w
3415
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 0	a-renamed/x.f
3416
100644 $(oid 44065282f89b9bd6439ed2e4674721383fd987eb) 0	a/sub/y.f
3417
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 0	a/sub/z
3418
EOF
3419
  make_resolve_tree ancestor A B
3420
  make_resolve_tree ancestor B A
3421
3422
  # *ours* is `a-renamed` everything, with merges.
3423
  rm .git/index
3424
  git update-index --index-info <<EOF
3425
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 0	a-renamed/sub/y.f
3426
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 0	a-renamed/sub/z
3427
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 0	a-renamed/w
3428
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 0	a-renamed/x.f
3429
EOF
3430
  make_resolve_tree ours A B
3431
3432
  # Now ours is the renamed sub-directory, with merges. It can bring everything together even.
3433
  rm .git/index
3434
  git update-index --index-info <<EOF
3435
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 0	a-renamed/sub-renamed/y.f
3436
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 0	a-renamed/sub-renamed/z
3437
100644 $(oid e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) 0	a-renamed/w
3438
100644 $(oid b414108e81e5091fe0974a1858b4d0d22b107f70) 0	a-renamed/x.f
3439
EOF
3440
  make_resolve_tree ours B A
3441
)