forked from apenwarr/git-subtree
-
Notifications
You must be signed in to change notification settings - Fork 2
/
test.sh
executable file
·402 lines (325 loc) · 9.51 KB
/
test.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
#!/bin/bash
. shellopts.sh
set -e
create()
{
echo "$1" >"$1"
git add "$1"
}
check()
{
echo
echo "check:" "$@"
if "$@"; then
echo ok
return 0
else
echo FAILED
exit 1
fi
}
check_not()
{
echo
echo "check: NOT " "$@"
if "$@"; then
echo FAILED
exit 1
else
echo ok
return 0
fi
}
check_equal()
{
echo
echo "check a:" "{$1}"
echo " b:" "{$2}"
if [ "$1" = "$2" ]; then
return 0
else
echo FAILED
exit 1
fi
}
fixnl()
{
t=""
while read x; do
t="$t$x "
done
echo $t
}
multiline()
{
while read x; do
set -- $x
for d in "$@"; do
echo "$d"
done
done
}
undo()
{
git reset --hard HEAD~
}
last_commit_message()
{
git log --pretty=format:%s -1
}
rm -rf mainline subproj
mkdir mainline subproj
cd subproj
git init
create sub1
git commit -m 'sub1'
git branch sub1
git branch -m master subproj
check true
create sub2
git commit -m 'sub2'
git branch sub2
create sub3
git commit -m 'sub3'
git branch sub3
cd ../mainline
git init
create main4
git commit -m 'main4'
git branch -m master mainline
git branch subdir
git fetch ../subproj sub1
git branch sub1 FETCH_HEAD
# check if --message works for add
check_not git subtree merge --prefix=subdir sub1
check_not git subtree pull --prefix=subdir ../subproj sub1
git subtree add --prefix=subdir --message="Added subproject" sub1
check_equal "$(last_commit_message)" "Added subproject"
undo
# check if --message works as -m and --prefix as -P
git subtree add -P subdir -m "Added subproject using git subtree" sub1
check_equal "$(last_commit_message)" "Added subproject using git subtree"
undo
# check if --message works with squash too
git subtree add -P subdir -m "Added subproject with squash" --squash sub1
check_equal "$(last_commit_message)" "Added subproject with squash"
undo
git subtree add --prefix=subdir/ FETCH_HEAD
check_equal "$(last_commit_message)" "Add 'subdir/' from commit '$(git rev-parse sub1)'"
# this shouldn't actually do anything, since FETCH_HEAD is already a parent
git merge -m 'merge -s -ours' -s ours FETCH_HEAD
create subdir/main-sub5
git commit -m 'main-sub5'
create main6
git commit -m 'main6 boring'
create subdir/main-sub7
git commit -m 'main-sub7'
git fetch ../subproj sub2
git branch sub2 FETCH_HEAD
# check if --message works for merge
git subtree merge --prefix=subdir -m "Merged changes from subproject" sub2
check_equal "$(last_commit_message)" "Merged changes from subproject"
undo
# check if --message for merge works with squash too
git subtree merge --prefix subdir -m "Merged changes from subproject using squash" --squash sub2
check_equal "$(last_commit_message)" "Merged changes from subproject using squash"
undo
git subtree merge --prefix=subdir FETCH_HEAD
git branch pre-split
check_equal "$(last_commit_message)" "Merge commit '$(git rev-parse sub2)' into mainline"
# Check that prefix argument is required for split (exits with warning and exit status = 1)
! result=$(git subtree split 2>&1)
check_equal "You must provide the --prefix option." "$result"
# Check that the <prefix> exists for a split.
! result=$(git subtree split --prefix=non-existent-directory 2>&1)
check_equal "'non-existent-directory' does not exist; use 'git subtree add'" \
"$result"
# check if --message works for split+rejoin
spl1=$(git subtree split --annotate='*' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)
echo "spl1={$spl1}"
git branch spl1 "$spl1"
check_equal "$(last_commit_message)" "Split & rejoin"
undo
# check split with --branch
git subtree split --annotate='*' --prefix subdir --onto FETCH_HEAD --branch splitbr1
check_equal "$(git rev-parse splitbr1)" "$spl1"
# check split with --branch for an existing branch
git branch splitbr2 sub1
git subtree split --annotate='*' --prefix subdir --onto FETCH_HEAD --branch splitbr2
check_equal "$(git rev-parse splitbr2)" "$spl1"
# check split with --branch for an incompatible branch
result=$(git subtree split --prefix subdir --onto FETCH_HEAD --branch subdir || echo "caught error")
check_equal "$result" "caught error"
git subtree split --annotate='*' --prefix subdir --onto FETCH_HEAD --rejoin
check_equal "$(last_commit_message)" "Split 'subdir/' into commit '$spl1'"
create subdir/main-sub8
git commit -m 'main-sub8'
cd ../subproj
git fetch ../mainline spl1
git branch spl1 FETCH_HEAD
git merge FETCH_HEAD
create sub9
git commit -m 'sub9'
cd ../mainline
split2=$(git subtree split --annotate='*' --prefix subdir/ --rejoin)
git branch split2 "$split2"
create subdir/main-sub10
git commit -m 'main-sub10'
spl3=$(git subtree split --annotate='*' --prefix subdir --rejoin)
git branch spl3 "$spl3"
cd ../subproj
git fetch ../mainline spl3
git branch spl3 FETCH_HEAD
git merge FETCH_HEAD
git branch subproj-merge-spl3
chkm="main4 main6"
chkms="main-sub10 main-sub5 main-sub7 main-sub8"
chkms_sub=$(echo $chkms | multiline | sed 's,^,subdir/,' | fixnl)
chks="sub1 sub2 sub3 sub9"
chks_sub=$(echo $chks | multiline | sed 's,^,subdir/,' | fixnl)
# make sure exactly the right set of files ends up in the subproj
subfiles=$(git ls-files | fixnl)
check_equal "$subfiles" "$chkms $chks"
# make sure the subproj history *only* contains commits that affect the subdir.
allchanges=$(git log --name-only --pretty=format:'' | sort | fixnl)
check_equal "$allchanges" "$chkms $chks"
cd ../mainline
git fetch ../subproj subproj-merge-spl3
git branch subproj-merge-spl3 FETCH_HEAD
git subtree pull --prefix=subdir ../subproj subproj-merge-spl3
# make sure exactly the right set of files ends up in the mainline
mainfiles=$(git ls-files | fixnl)
check_equal "$mainfiles" "$chkm $chkms_sub $chks_sub"
# make sure each filename changed exactly once in the entire history.
# 'main-sub??' and '/subdir/main-sub??' both change, because those are the
# changes that were split into their own history. And 'subdir/sub??' never
# change, since they were *only* changed in the subtree branch.
allchanges=$(git log --name-only --pretty=format:'' | sort | fixnl)
check_equal "$allchanges" "$(echo $chkms $chkm $chks $chkms_sub | multiline | sort | fixnl)"
# make sure the --rejoin commits never make it into subproj
check_equal "$(git log --pretty=format:'%s' HEAD^2 | grep -i split)" ""
# make sure no 'git subtree' tagged commits make it into subproj. (They're
# meaningless to subproj since one side of the merge refers to the mainline)
check_equal "$(git log --pretty=format:'%s%n%b' HEAD^2 | grep 'git-subtree.*:')" ""
# check if split can find proper base without --onto
# prepare second pair of repositories
mkdir test2
cd test2
mkdir main
cd main
git init
create main1
git commit -m "main1"
cd ..
mkdir sub
cd sub
git init
create sub2
git commit -m "sub2"
cd ../main
git fetch ../sub master
git branch sub2 FETCH_HEAD
git subtree add --prefix subdir sub2
cd ../sub
create sub3
git commit -m "sub3"
cd ../main
git fetch ../sub master
git branch sub3 FETCH_HEAD
git subtree merge --prefix subdir sub3
create subdir/main-sub4
git commit -m "main-sub4"
git subtree split --prefix subdir --branch mainsub4
# at this point, the new commit's parent should be sub3
# if it's not, something went wrong (the "newparent" of "master~" commit should have been sub3,
# but it wasn't, because it's cache was not set to itself)
check_equal "$(git log --pretty=format:%P -1 mainsub4)" "$(git rev-parse sub3)"
mkdir subdir2
create subdir2/main-sub5
git commit -m "main-sub5"
git subtree split --prefix subdir2 --branch mainsub5
# also test that we still can split out an entirely new subtree
# if the parent of the first commit in the tree isn't empty,
# then the new subtree has accidently been attached to something
check_equal "$(git log --pretty=format:%P -1 mainsub5)" ""
# make sure no patch changes more than one file. The original set of commits
# changed only one file each. A multi-file change would imply that we pruned
# commits too aggressively.
joincommits()
{
commit=
all=
while read x y; do
echo "{$x}" >&2
if [ -z "$x" ]; then
continue
elif [ "$x" = "commit:" ]; then
if [ -n "$commit" ]; then
echo "$commit $all"
all=
fi
commit="$y"
else
all="$all $y"
fi
done
echo "$commit $all"
}
x=
git log --pretty=format:'commit: %H' | joincommits |
( while read commit a b; do
echo "Verifying commit $commit"
check_equal "$b" ""
x=1
done
check_equal "$x" 1
) || exit 1
# Return to mainline
cd ../..
# from-submodule
make_submodule()
{
cd ..
local prefix=$1
local submodule_path=$(mktemp -d submodule.XXX)
cd $submodule_path
local submodule_abs_path=$(pwd)
git init > /dev/null
create "sub-file"
git commit -m "sub-commit" > /dev/null
local submodule_sha=$(git rev-parse HEAD)
cd ../mainline
git submodule add $submodule_abs_path $prefix > /dev/null
git submodule update --init > /dev/null
git commit -m "Added $prefix." > /dev/null
echo $submodule_sha
}
# Unfortunately the following submodule tests were broken when I found them:
#
# subA_sha=$(make_submodule submodules/subA)
#
# git subtree from-submodule --prefix submodules/subA
#
# check_equal "$(last_commit_message)" "Add 'submodules/subA/' from commit '${subA_sha}'"
# # Submodule should be gone.
# check_equal "$(git submodule status)" ""
#
# rm -rf ../submodule.*
# Simple subtree add-and-immediately-push test:
echo
echo '*** TESTING add subtree and immediately push'
echo
cd .. # return to test dir top level
rm -rf mainline2
git init mainline2
cd mainline2
date > mainline2.txt && git add . && git commit -m "initial mainline2 commit"
git subtree add --prefix=subproj ../subproj subproj
git subtree push --prefix=subproj ../subproj subproj
result=$?
cd ..
check_equal 0 "$result"
echo
echo '*** PASSED add subtree and immediately push'
echo
echo '*** ALL TESTS PASSED ***'