1#!/bin/sh
2#
3# Copyright (c) 2005 Junio C Hamano
4#
5
6test_description='Two way merge with read-tree -m $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 read-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 or M
22'
23
24TEST_PASSES_SANITIZE_LEAK=true
25. ./test-lib.sh
26. "$TEST_DIRECTORY"/lib-read-tree.sh
27
28read_tree_twoway () {
29    git read-tree -m "$1" "$2" && git ls-files --stage
30}
31
32compare_change () {
33	sed -n >current \
34	    -e '/^--- /d; /^+++ /d; /^@@ /d;' \
35	    -e 's/^\([-+][0-7][0-7][0-7][0-7][0-7][0-7]\) '"$OID_REGEX"' /\1 X /p' \
36	    "$1"
37	test_cmp expected current
38}
39
40check_cache_at () {
41	clean_if_empty=$(git diff-files -- "$1")
42	case "$clean_if_empty" in
43	'')  echo "$1: clean" ;;
44	?*)  echo "$1: dirty" ;;
45	esac
46	case "$2,$clean_if_empty" in
47	clean,)		:     ;;
48	clean,?*)	false ;;
49	dirty,)		false ;;
50	dirty,?*)	:     ;;
51	esac
52}
53
54cat >bozbar-old <<\EOF
55This is a sample file used in two-way fast-forward merge
56tests.  Its second line ends with a magic word bozbar
57which will be modified by the merged head to gnusto.
58It has some extra lines so that external tools can
59successfully merge independent changes made to later
60lines (such as this one), avoiding line conflicts.
61EOF
62
63sed -e 's/bozbar/gnusto (earlier bozbar)/' bozbar-old >bozbar-new
64
65test_expect_success 'setup' '
66	echo frotz >frotz &&
67	echo nitfol >nitfol &&
68	cat bozbar-old >bozbar &&
69	echo rezrov >rezrov &&
70	echo yomin >yomin &&
71	git update-index --add nitfol bozbar rezrov &&
72	treeH=$(git write-tree) &&
73	echo treeH $treeH &&
74	git ls-tree $treeH &&
75
76	cat bozbar-new >bozbar &&
77	git update-index --add frotz bozbar --force-remove rezrov &&
78	git ls-files --stage >M.out &&
79	treeM=$(git write-tree) &&
80	echo treeM $treeM &&
81	git ls-tree $treeM &&
82	git diff-tree $treeH $treeM
83'
84
85test_expect_success '1, 2, 3 - no carry forward' '
86	rm -f .git/index &&
87	read_tree_twoway $treeH $treeM &&
88	git ls-files --stage >1-3.out &&
89	test_cmp M.out 1-3.out &&
90	check_cache_at bozbar dirty &&
91	check_cache_at frotz dirty &&
92	check_cache_at nitfol dirty
93'
94echo '+100644 X 0	yomin' >expected
95
96test_expect_success '4 - carry forward local addition.' '
97	rm -f .git/index &&
98	read_tree_must_succeed $treeH &&
99	git checkout-index -u -f -q -a &&
100	git update-index --add yomin &&
101	read_tree_twoway $treeH $treeM &&
102	git ls-files --stage >4.out &&
103	test_must_fail git diff --no-index M.out 4.out >4diff.out &&
104	compare_change 4diff.out expected &&
105	check_cache_at yomin clean
106'
107
108test_expect_success '5 - carry forward local addition.' '
109	rm -f .git/index &&
110	read_tree_must_succeed $treeH &&
111	git checkout-index -u -f -q -a &&
112	echo yomin >yomin &&
113	git update-index --add yomin &&
114	echo yomin yomin >yomin &&
115	read_tree_twoway $treeH $treeM &&
116	git ls-files --stage >5.out &&
117	test_must_fail git diff --no-index M.out 5.out >5diff.out &&
118	compare_change 5diff.out expected &&
119	check_cache_at yomin dirty
120'
121
122test_expect_success '6 - local addition already has the same.' '
123	rm -f .git/index &&
124	read_tree_must_succeed $treeH &&
125	git checkout-index -u -f -q -a &&
126	git update-index --add frotz &&
127	read_tree_twoway $treeH $treeM &&
128	git ls-files --stage >6.out &&
129	test_cmp M.out 6.out &&
130	check_cache_at frotz clean
131'
132
133test_expect_success '7 - local addition already has the same.' '
134	rm -f .git/index &&
135	read_tree_must_succeed $treeH &&
136	git checkout-index -u -f -q -a &&
137	echo frotz >frotz &&
138	git update-index --add frotz &&
139	echo frotz frotz >frotz &&
140	read_tree_twoway $treeH $treeM &&
141	git ls-files --stage >7.out &&
142	test_cmp M.out 7.out &&
143	check_cache_at frotz dirty
144'
145
146test_expect_success '8 - conflicting addition.' '
147	rm -f .git/index &&
148	read_tree_must_succeed $treeH &&
149	git checkout-index -u -f -q -a &&
150	echo frotz frotz >frotz &&
151	git update-index --add frotz &&
152	if read_tree_twoway $treeH $treeM; then false; else :; fi
153'
154
155test_expect_success '9 - conflicting addition.' '
156	rm -f .git/index &&
157	read_tree_must_succeed $treeH &&
158	git checkout-index -u -f -q -a &&
159	echo frotz frotz >frotz &&
160	git update-index --add frotz &&
161	echo frotz >frotz &&
162	if read_tree_twoway $treeH $treeM; then false; else :; fi
163'
164
165test_expect_success '10 - path removed.' '
166	rm -f .git/index &&
167	read_tree_must_succeed $treeH &&
168	git checkout-index -u -f -q -a &&
169	echo rezrov >rezrov &&
170	git update-index --add rezrov &&
171	read_tree_twoway $treeH $treeM &&
172	git ls-files --stage >10.out &&
173	test_cmp M.out 10.out
174'
175
176test_expect_success '11 - dirty path removed.' '
177	rm -f .git/index &&
178	read_tree_must_succeed $treeH &&
179	git checkout-index -u -f -q -a &&
180	echo rezrov >rezrov &&
181	git update-index --add rezrov &&
182	echo rezrov rezrov >rezrov &&
183	if read_tree_twoway $treeH $treeM; then false; else :; fi
184'
185
186test_expect_success '12 - unmatching local changes being removed.' '
187	rm -f .git/index &&
188	read_tree_must_succeed $treeH &&
189	git checkout-index -u -f -q -a &&
190	echo rezrov rezrov >rezrov &&
191	git update-index --add rezrov &&
192	if read_tree_twoway $treeH $treeM; then false; else :; fi
193'
194
195test_expect_success '13 - unmatching local changes being removed.' '
196	rm -f .git/index &&
197	read_tree_must_succeed $treeH &&
198	git checkout-index -u -f -q -a &&
199	echo rezrov rezrov >rezrov &&
200	git update-index --add rezrov &&
201	echo rezrov >rezrov &&
202	if read_tree_twoway $treeH $treeM; then false; else :; fi
203'
204
205cat >expected <<EOF
206-100644 X 0	nitfol
207+100644 X 0	nitfol
208EOF
209
210test_expect_success '14 - unchanged in two heads.' '
211	rm -f .git/index &&
212	read_tree_must_succeed $treeH &&
213	git checkout-index -u -f -q -a &&
214	echo nitfol nitfol >nitfol &&
215	git update-index --add nitfol &&
216	read_tree_twoway $treeH $treeM &&
217	git ls-files --stage >14.out &&
218	test_must_fail git diff --no-index M.out 14.out >14diff.out &&
219	compare_change 14diff.out expected &&
220	check_cache_at nitfol clean
221'
222
223test_expect_success '15 - unchanged in two heads.' '
224	rm -f .git/index &&
225	read_tree_must_succeed $treeH &&
226	git checkout-index -u -f -q -a &&
227	echo nitfol nitfol >nitfol &&
228	git update-index --add nitfol &&
229	echo nitfol nitfol nitfol >nitfol &&
230	read_tree_twoway $treeH $treeM &&
231	git ls-files --stage >15.out &&
232	test_must_fail git diff --no-index M.out 15.out >15diff.out &&
233	compare_change 15diff.out expected &&
234	check_cache_at nitfol dirty
235'
236
237test_expect_success '16 - conflicting local change.' '
238	rm -f .git/index &&
239	read_tree_must_succeed $treeH &&
240	git checkout-index -u -f -q -a &&
241	echo bozbar bozbar >bozbar &&
242	git update-index --add bozbar &&
243	if read_tree_twoway $treeH $treeM; then false; else :; fi
244'
245
246test_expect_success '17 - conflicting local change.' '
247	rm -f .git/index &&
248	read_tree_must_succeed $treeH &&
249	git checkout-index -u -f -q -a &&
250	echo bozbar bozbar >bozbar &&
251	git update-index --add bozbar &&
252	echo bozbar bozbar bozbar >bozbar &&
253	if read_tree_twoway $treeH $treeM; then false; else :; fi
254'
255
256test_expect_success '18 - local change already having a good result.' '
257	rm -f .git/index &&
258	read_tree_must_succeed $treeH &&
259	git checkout-index -u -f -q -a &&
260	cat bozbar-new >bozbar &&
261	git update-index --add bozbar &&
262	read_tree_twoway $treeH $treeM &&
263	git ls-files --stage >18.out &&
264	test_cmp M.out 18.out &&
265	check_cache_at bozbar clean
266'
267
268test_expect_success '19 - local change already having a good result, further modified.' '
269	rm -f .git/index &&
270	read_tree_must_succeed $treeH &&
271	git checkout-index -u -f -q -a &&
272	cat bozbar-new >bozbar &&
273	git update-index --add bozbar &&
274	echo gnusto gnusto >bozbar &&
275	read_tree_twoway $treeH $treeM &&
276	git ls-files --stage >19.out &&
277	test_cmp M.out 19.out &&
278	check_cache_at bozbar dirty
279'
280
281test_expect_success '20 - no local change, use new tree.' '
282	rm -f .git/index &&
283	read_tree_must_succeed $treeH &&
284	git checkout-index -u -f -q -a &&
285	cat bozbar-old >bozbar &&
286	git update-index --add bozbar &&
287	read_tree_twoway $treeH $treeM &&
288	git ls-files --stage >20.out &&
289	test_cmp M.out 20.out &&
290	check_cache_at bozbar dirty
291'
292
293test_expect_success '21 - no local change, dirty cache.' '
294	rm -f .git/index &&
295	read_tree_must_succeed $treeH &&
296	git checkout-index -u -f -q -a &&
297	cat bozbar-old >bozbar &&
298	git update-index --add bozbar &&
299	echo gnusto gnusto >bozbar &&
300	if read_tree_twoway $treeH $treeM; then false; else :; fi
301'
302
303# This fails with straight two-way fast-forward.
304test_expect_success '22 - local change cache updated.' '
305	rm -f .git/index &&
306	read_tree_must_succeed $treeH &&
307	git checkout-index -u -f -q -a &&
308	sed -e "s/such as/SUCH AS/" bozbar-old >bozbar &&
309	git update-index --add bozbar &&
310	if read_tree_twoway $treeH $treeM; then false; else :; fi
311'
312
313# Also make sure we did not break DF vs DF/DF case.
314test_expect_success 'DF vs DF/DF case setup.' '
315	rm -f .git/index &&
316	echo DF >DF &&
317	git update-index --add DF &&
318	treeDF=$(git write-tree) &&
319	echo treeDF $treeDF &&
320	git ls-tree $treeDF &&
321
322	rm -f DF &&
323	mkdir DF &&
324	echo DF/DF >DF/DF &&
325	git update-index --add --remove DF DF/DF &&
326	treeDFDF=$(git write-tree) &&
327	echo treeDFDF $treeDFDF &&
328	git ls-tree $treeDFDF &&
329	git ls-files --stage >DFDF.out
330'
331
332test_expect_success 'DF vs DF/DF case test.' '
333	rm -f .git/index &&
334	rm -fr DF &&
335	echo DF >DF &&
336	git update-index --add DF &&
337	read_tree_twoway $treeDF $treeDFDF &&
338	git ls-files --stage >DFDFcheck.out &&
339	test_cmp DFDF.out DFDFcheck.out &&
340	check_cache_at DF/DF dirty &&
341	:
342'
343
344test_expect_success 'a/b (untracked) vs a case setup.' '
345	rm -f .git/index &&
346	: >a &&
347	git update-index --add a &&
348	treeM=$(git write-tree) &&
349	echo treeM $treeM &&
350	git ls-tree $treeM &&
351	git ls-files --stage >treeM.out &&
352
353	rm -f a &&
354	git update-index --remove a &&
355	mkdir a &&
356	: >a/b &&
357	treeH=$(git write-tree) &&
358	echo treeH $treeH &&
359	git ls-tree $treeH
360'
361
362test_expect_success 'a/b (untracked) vs a, plus c/d case test.' '
363	read_tree_u_must_fail -u -m "$treeH" "$treeM" &&
364	git ls-files --stage &&
365	test -f a/b
366'
367
368test_expect_success 'read-tree supports the super-prefix' '
369	cat <<-EOF >expect &&
370		error: Updating '\''fictional/a'\'' would lose untracked files in it
371	EOF
372	test_must_fail git --super-prefix fictional/ read-tree -u -m "$treeH" "$treeM" 2>actual &&
373	test_cmp expect actual
374'
375
376test_expect_success 'a/b vs a, plus c/d case setup.' '
377	rm -f .git/index &&
378	rm -fr a &&
379	: >a &&
380	mkdir c &&
381	: >c/d &&
382	git update-index --add a c/d &&
383	treeM=$(git write-tree) &&
384	echo treeM $treeM &&
385	git ls-tree $treeM &&
386	git ls-files --stage >treeM.out &&
387
388	rm -f a &&
389	mkdir a &&
390	: >a/b &&
391	git update-index --add --remove a a/b &&
392	treeH=$(git write-tree) &&
393	echo treeH $treeH &&
394	git ls-tree $treeH
395'
396
397test_expect_success 'a/b vs a, plus c/d case test.' '
398	read_tree_u_must_succeed -u -m "$treeH" "$treeM" &&
399	git ls-files --stage | tee >treeMcheck.out &&
400	test_cmp treeM.out treeMcheck.out
401'
402
403test_expect_success '-m references the correct modified tree' '
404	echo >file-a &&
405	echo >file-b &&
406	git add file-a file-b &&
407	git commit -a -m "test for correct modified tree" &&
408	git branch initial-mod &&
409	echo b >file-b &&
410	git commit -a -m "B" &&
411	echo a >file-a &&
412	git add file-a &&
413	git ls-tree $(git write-tree) file-a >expect &&
414	read_tree_must_succeed -m HEAD initial-mod &&
415	git ls-tree $(git write-tree) file-a >actual &&
416	test_cmp expect actual
417'
418
419test_done
420