1#!/bin/sh
2#
3# Copyright (c) 2005 Junio C Hamano
4#
5
6test_description='Two way merge with read-tree --emu23 $H $M
7
8This test tries two-way merge (aka fast forward with carry forward).
9
10There is the head (called H) and another commit (called M), which is
11simply ahead of H. The index and the work tree contains a state that
12is derived from H, but may also have local changes. This test checks
13all the combinations described in the two-tree merge "carry forward"
14rules, found in <Documentation/git-rev-tree.txt>.
15
16In the test, these paths are used:
17 bozbar - in H, stays in M, modified from bozbar to gnusto
18 frotz - not in H added in M
19 nitfol - in H, stays in M unmodified
20 rezrov - in H, deleted in M
21 yomin - not in H nor M
22'
23. ./test-lib.sh
24
25read_tree_twoway () {
26 git-read-tree --emu23 "$1" "$2" &&
27 git-ls-files --stage &&
28 git-merge-index git-merge-one-file -a &&
29 git-ls-files --stage
30}
31
32_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
33_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
34compare_change () {
35 sed -n >current \
36 -e '/^--- /d; /^+++ /d; /^@@ /d;' \
37 -e 's/^\([-+][0-7][0-7][0-7][0-7][0-7][0-7]\) '"$_x40"' /\1 X /p' \
38 "$1"
39 diff -u expected current
40}
41
42check_cache_at () {
43 clean_if_empty=`git-diff-files "$1"`
44 case "$clean_if_empty" in
45 '') echo "$1: clean" ;;
46 ?*) echo "$1: dirty" ;;
47 esac
48 case "$2,$clean_if_empty" in
49 clean,) : ;;
50 clean,?*) false ;;
51 dirty,) false ;;
52 dirty,?*) : ;;
53 esac
54}
55
56check_stages () {
57 cat >expected_stages
58 git-ls-files --stage | sed -e "s/ $_x40 / X /" >current_stages
59 diff -u expected_stages current_stages
60}
61
62cat >bozbar-old <<\EOF
63This is a sample file used in two-way fast forward merge
64tests. Its second line ends with a magic word bozbar
65which will be modified by the merged head to gnusto.
66It has some extra lines so that external tools can
67successfully merge independent changes made to later
68lines (such as this one), avoiding line conflicts.
69EOF
70
71sed -e 's/bozbar/gnusto (earlier bozbar)/' bozbar-old >bozbar-new
72
73test_expect_success \
74 setup \
75 'echo frotz >frotz &&
76 echo nitfol >nitfol &&
77 cat bozbar-old >bozbar &&
78 echo rezrov >rezrov &&
79 echo yomin >yomin &&
80 git-update-index --add nitfol bozbar rezrov &&
81 treeH=`git-write-tree` &&
82 echo treeH $treeH &&
83 git-ls-tree $treeH &&
84
85 cat bozbar-new >bozbar &&
86 git-update-index --add frotz bozbar --force-remove rezrov &&
87 git-ls-files --stage >M.out &&
88 treeM=`git-write-tree` &&
89 echo treeM $treeM &&
90 git-ls-tree $treeM &&
91 git-diff-tree $treeH $treeM'
92
93# "read-tree -m H I+H M" but I is empty so this is "read-tree -m H H M".
94#
95# bozbar [O && A && B && O==A && O!=B (#14) ==> B] take M by read-tree
96# frotz [!O && !A && B (#2) ==> B] take M by read-tree
97# nitfol [O && A && B && O==A && O==B (#15) ==> B] take M by read-tree
98# rezrov [O && A && !B && O==A (#10) ==> no merge] removed by script
99#
100# Earlier one did not have #2ALT so taking M was done by the script,
101# which also updated the work tree and making frotz clean. With #2ALT,
102# this is resolved by read-tree itself and the path is left dirty
103# because we are not testing "read-tree -u --emu23".
104test_expect_success \
105 '1, 2, 3 - no carry forward' \
106 'rm -f .git/index &&
107 read_tree_twoway $treeH $treeM &&
108 git-ls-files --stage >1-3.out &&
109 diff -u M.out 1-3.out &&
110 check_cache_at bozbar dirty &&
111 check_cache_at frotz dirty && # same as pure 2-way again.
112 check_cache_at nitfol dirty'
113
114echo '+100644 X 0 yomin' >expected
115
116test_expect_success \
117 '4 - carry forward local addition.' \
118 'rm -f .git/index &&
119 git-read-tree $treeH &&
120 git-checkout-index -u -f -q -a &&
121 git-update-index --add yomin &&
122 read_tree_twoway $treeH $treeM &&
123 git-ls-files --stage >4.out || return 1
124 diff -u M.out 4.out >4diff.out
125 compare_change 4diff.out expected &&
126 check_cache_at yomin clean'
127
128# "read-tree -m H I+H M" where !H && !M; so (I+H) not being up-to-date
129# should not matter. Thanks to #3ALT, this is now possible.
130test_expect_success \
131 '5 - carry forward local addition.' \
132 'rm -f .git/index &&
133 git-read-tree $treeH &&
134 git-checkout-index -u -f -q -a &&
135 echo yomin >yomin &&
136 git-update-index --add yomin &&
137 echo yomin yomin >yomin &&
138 read_tree_twoway $treeH $treeM &&
139 git-ls-files --stage >5.out || return 1
140 diff -u M.out 5.out >5diff.out
141 compare_change 5diff.out expected &&
142 check_cache_at yomin dirty'
143
144# "read-tree -m H I+H M" where !H && M && (I+H) == M, so this should
145# succeed (even the entry is clean), now thanks to #5ALT.
146test_expect_success \
147 '6 - local addition already has the same.' \
148 'rm -f .git/index &&
149 git-read-tree $treeH &&
150 git-checkout-index -u -f -q -a &&
151 git-update-index --add frotz &&
152 read_tree_twoway $treeH $treeM &&
153 git-ls-files --stage >6.out &&
154 diff -u M.out 6.out &&
155 check_cache_at frotz clean'
156
157# Exactly the same pattern as above but with dirty cache. This also
158# should succeed, now thanks to #5ALT.
159test_expect_success \
160 '7 - local addition already has the same.' \
161 'rm -f .git/index &&
162 git-read-tree $treeH &&
163 git-checkout-index -u -f -q -a &&
164 echo frotz >frotz &&
165 git-update-index --add frotz &&
166 echo frotz frotz >frotz &&
167 read_tree_twoway $treeH $treeM &&
168 git-ls-files --stage >7.out &&
169 diff -u M.out 7.out &&
170 check_cache_at frotz dirty'
171
172test_expect_success \
173 '8 - conflicting addition.' \
174 'rm -f .git/index &&
175 git-read-tree $treeH &&
176 git-checkout-index -u -f -q -a &&
177 echo frotz frotz >frotz &&
178 git-update-index --add frotz &&
179 if read_tree_twoway $treeH $treeM; then false; else :; fi'
180
181test_expect_success \
182 '9 - conflicting addition.' \
183 'rm -f .git/index &&
184 git-read-tree $treeH &&
185 git-checkout-index -u -f -q -a &&
186 echo frotz frotz >frotz &&
187 git-update-index --add frotz &&
188 echo frotz >frotz &&
189 if read_tree_twoway $treeH $treeM; then false; else :; fi'
190
191test_expect_success \
192 '10 - path removed.' \
193 'rm -f .git/index &&
194 git-read-tree $treeH &&
195 git-checkout-index -u -f -q -a &&
196 echo rezrov >rezrov &&
197 git-update-index --add rezrov &&
198 read_tree_twoway $treeH $treeM &&
199 git-ls-files --stage >10.out &&
200 diff -u M.out 10.out'
201
202test_expect_success \
203 '11 - dirty path removed.' \
204 'rm -f .git/index &&
205 git-read-tree $treeH &&
206 git-checkout-index -u -f -q -a &&
207 echo rezrov >rezrov &&
208 git-update-index --add rezrov &&
209 echo rezrov rezrov >rezrov &&
210 if read_tree_twoway $treeH $treeM; then false; else :; fi'
211
212test_expect_success \
213 '12 - unmatching local changes being removed.' \
214 'rm -f .git/index &&
215 git-read-tree $treeH &&
216 git-checkout-index -u -f -q -a &&
217 echo rezrov rezrov >rezrov &&
218 git-update-index --add rezrov &&
219 if read_tree_twoway $treeH $treeM; then false; else :; fi'
220
221test_expect_success \
222 '13 - unmatching local changes being removed.' \
223 'rm -f .git/index &&
224 git-read-tree $treeH &&
225 git-checkout-index -u -f -q -a &&
226 echo rezrov rezrov >rezrov &&
227 git-update-index --add rezrov &&
228 echo rezrov >rezrov &&
229 if read_tree_twoway $treeH $treeM; then false; else :; fi'
230
231cat >expected <<EOF
232-100644 X 0 nitfol
233+100644 X 0 nitfol
234EOF
235
236test_expect_success \
237 '14 - unchanged in two heads.' \
238 'rm -f .git/index &&
239 git-read-tree $treeH &&
240 git-checkout-index -u -f -q -a &&
241 echo nitfol nitfol >nitfol &&
242 git-update-index --add nitfol &&
243 read_tree_twoway $treeH $treeM &&
244 git-ls-files --stage >14.out || return 1
245 diff -u M.out 14.out >14diff.out
246 compare_change 14diff.out expected &&
247 check_cache_at nitfol clean'
248
249test_expect_success \
250 '15 - unchanged in two heads.' \
251 'rm -f .git/index &&
252 git-read-tree $treeH &&
253 git-checkout-index -u -f -q -a &&
254 echo nitfol nitfol >nitfol &&
255 git-update-index --add nitfol &&
256 echo nitfol nitfol nitfol >nitfol &&
257 read_tree_twoway $treeH $treeM &&
258 git-ls-files --stage >15.out || return 1
259 diff -u M.out 15.out >15diff.out
260 compare_change 15diff.out expected &&
261 check_cache_at nitfol dirty'
262
263# This is different from straight 2-way merge in that it leaves
264# three stages of bozbar in the index file without failing, so
265# the user can run git-diff-stages to examine the situation.
266# With #2ALT, frotz is resolved internally.
267test_expect_success \
268 '16 - conflicting local change.' \
269 'rm -f .git/index &&
270 git-read-tree $treeH &&
271 git-checkout-index -u -f -q -a &&
272 echo bozbar bozbar >bozbar &&
273 git-update-index --add bozbar &&
274 git-read-tree --emu23 $treeH $treeM &&
275 check_stages' <<\EOF
276100644 X 1 bozbar
277100644 X 2 bozbar
278100644 X 3 bozbar
279100644 X 0 frotz
280100644 X 0 nitfol
281100644 X 1 rezrov
282100644 X 2 rezrov
283EOF
284
285test_expect_success \
286 '17 - conflicting local change.' \
287 'rm -f .git/index &&
288 git-read-tree $treeH &&
289 git-checkout-index -u -f -q -a &&
290 echo bozbar bozbar >bozbar &&
291 git-update-index --add bozbar &&
292 echo bozbar bozbar bozbar >bozbar &&
293 if read_tree_twoway $treeH $treeM; then false; else :; fi'
294
295test_expect_success \
296 '18 - local change already having a good result.' \
297 'rm -f .git/index &&
298 git-read-tree $treeH &&
299 git-checkout-index -u -f -q -a &&
300 cat bozbar-new >bozbar &&
301 git-update-index --add bozbar &&
302 read_tree_twoway $treeH $treeM &&
303 git-ls-files --stage >18.out &&
304 diff -u M.out 18.out &&
305 check_cache_at bozbar clean'
306
307test_expect_success \
308 '19 - local change already having a good result, further modified.' \
309 'rm -f .git/index &&
310 git-read-tree $treeH &&
311 git-checkout-index -u -f -q -a &&
312 cat bozbar-new >bozbar &&
313 git-update-index --add bozbar &&
314 echo gnusto gnusto >bozbar &&
315 read_tree_twoway $treeH $treeM &&
316 git-ls-files --stage >19.out &&
317 diff -u M.out 19.out &&
318 check_cache_at bozbar dirty'
319
320test_expect_success \
321 '20 - no local change, use new tree.' \
322 'rm -f .git/index &&
323 git-read-tree $treeH &&
324 git-checkout-index -u -f -q -a &&
325 cat bozbar-old >bozbar &&
326 git-update-index --add bozbar &&
327 read_tree_twoway $treeH $treeM &&
328 git-ls-files --stage >20.out &&
329 diff -u M.out 20.out &&
330 check_cache_at bozbar dirty'
331
332test_expect_success \
333 '21 - no local change, dirty cache.' \
334 'rm -f .git/index &&
335 git-read-tree $treeH &&
336 git-checkout-index -u -f -q -a &&
337 cat bozbar-old >bozbar &&
338 git-update-index --add bozbar &&
339 echo gnusto gnusto >bozbar &&
340 if read_tree_twoway $treeH $treeM; then false; else :; fi'
341
342echo '-100644 X 0 bozbar
343+100644 X 0 bozbar' >expected
344
345# This fails with straight two-way fast forward, but emu23
346# can merge them.
347test_expect_success \
348 '22 - local change cache updated.' \
349 'rm -f .git/index &&
350 git-read-tree $treeH &&
351 git-checkout-index -u -f -q -a &&
352 sed -e "s/such as/SUCH AS/" bozbar-old >bozbar &&
353 git-update-index --add bozbar &&
354 read_tree_twoway $treeH $treeM &&
355 git-ls-files --stage >22.out || return 1
356 diff -u M.out 22.out >22diff.out
357 compare_change 22diff.out &&
358 check_cache_at bozbar clean'
359
360# Also make sure we did not break DF vs DF/DF case.
361test_expect_success \
362 'DF vs DF/DF case setup.' \
363 'rm -f .git/index &&
364 echo DF >DF &&
365 git-update-index --add DF &&
366 treeDF=`git-write-tree` &&
367 echo treeDF $treeDF &&
368 git-ls-tree $treeDF &&
369 git-ls-files --stage >DF.out
370
371 rm -f DF &&
372 mkdir DF &&
373 echo DF/DF >DF/DF &&
374 git-update-index --add --remove DF DF/DF &&
375 treeDFDF=`git-write-tree` &&
376 echo treeDFDF $treeDFDF &&
377 git-ls-tree $treeDFDF &&
378 git-ls-files --stage >DFDF.out'
379
380test_expect_success \
381 'DF vs DF/DF case test (#1)' \
382 'rm -f .git/index &&
383 rm -fr DF &&
384 echo DF >DF &&
385 git-update-index --add DF &&
386 read_tree_twoway $treeDF $treeDFDF &&
387 git-ls-files --stage >DFDFcheck.out &&
388 diff -u DFDF.out DFDFcheck.out &&
389 check_cache_at DF/DF clean && # different from pure 2-way
390 :'
391
392# The other way around
393test_expect_success \
394 'DF vs DF/DF case test (#2)' \
395 'rm -f .git/index &&
396 rm -fr DF &&
397 mkdir DF &&
398 echo DF/DF >DF/DF &&
399 git-update-index --add DF/DF &&
400 read_tree_twoway $treeDFDF $treeDF &&
401 git-ls-files --stage >DFDFcheck.out &&
402 diff -u DF.out DFDFcheck.out &&
403 check_cache_at DF clean && # different from pure 2-way
404 :'
405
406# Emu23 can grok I having more than H. Make sure we did not
407# botch the conflict tests (fixed).
408test_expect_success \
409 'DF vs DF/DF case test (#3).' \
410 'rm -f .git/index &&
411 rm -fr DF &&
412 mkdir DF &&
413 echo DF/DF >DF/DF &&
414 git-update-index --add DF/DF &&
415 # This should fail because I and H have a conflict
416 # at DF.
417 if git-read-tree --emu23 $treeDF $treeDFDF
418 then false
419 else true
420 fi'
421
422test_done