@matheusbsbs / matheus-bagliano / commits / ff7f089ed1
Fork 0

mergetool: Teach about submodules

When the index has conflicted submodules, mergetool used to mildly clobber the module, renaming it to mymodule.BACKUP.nnnn, then failing to copy it non-recursively. Recognize submodules and offer a resolution instead: Submodule merge conflict for 'Shared': {local}: submodule commit ad9f12e3e6205381bf2163a793d1e596a9e211d0 {remote}: submodule commit f5893fb70ec5646efcd9aa643c5136753ac89253 Use (l)ocal or (r)emote, or (a)bort? Selecting a commit will stage it, but not update the submodule (as git does had there been no conflict). Type changes are also supported, should the path be a submodule on one side, and a file, symlink, directory, or deleted on the other. Signed-off-by: Jonathon Mah <me@JonathonMah.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jonathon Mah committed Apr 13, 2011 at 03:00 UTC ff7f089ed1064b6a10e958bf405f828226262424
2 files changed +371 -9
git-mergetool.sh
+84 -6
@@ -21,6 +21,10 @@ is_symlink () {
21 test "$1" = 120000
22 }
23
24 +is_submodule () {
25 + test "$1" = 160000
26 +}
27 +
28 local_present () {
29 test -n "$local_mode"
30 }
@@ -35,7 +39,8 @@ base_present () {
39
40 cleanup_temp_files () {
41 if test "$1" = --save-backup ; then
38 - mv -- "$BACKUP" "$MERGED.orig"
42 + rm -rf -- "$MERGED.orig"
43 + test -e "$BACKUP" && mv -- "$BACKUP" "$MERGED.orig"
44 rm -f -- "$LOCAL" "$REMOTE" "$BASE"
45 else
46 rm -f -- "$LOCAL" "$REMOTE" "$BASE" "$BACKUP"
@@ -52,11 +57,13 @@ describe_file () {
57 echo "deleted"
58 elif is_symlink "$mode" ; then
59 echo "a symbolic link -> '$(cat "$file")'"
60 + elif is_submodule "$mode" ; then
61 + echo "submodule commit $file"
62 else
63 if base_present; then
57 - echo "modified"
64 + echo "modified file"
65 else
59 - echo "created"
66 + echo "created file"
67 fi
68 fi
69 }
@@ -112,6 +119,67 @@ resolve_deleted_merge () {
119 done
120 }
121
122 +resolve_submodule_merge () {
123 + while true; do
124 + printf "Use (l)ocal or (r)emote, or (a)bort? "
125 + read ans
126 + case "$ans" in
127 + [lL]*)
128 + if ! local_present; then
129 + if test -n "$(git ls-tree HEAD -- "$MERGED")"; then
130 + # Local isn't present, but it's a subdirectory
131 + git ls-tree --full-name -r HEAD -- "$MERGED" | git update-index --index-info || exit $?
132 + else
133 + test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
134 + git update-index --force-remove "$MERGED"
135 + cleanup_temp_files --save-backup
136 + fi
137 + elif is_submodule "$local_mode"; then
138 + stage_submodule "$MERGED" "$local_sha1"
139 + else
140 + git checkout-index -f --stage=2 -- "$MERGED"
141 + git add -- "$MERGED"
142 + fi
143 + return 0
144 + ;;
145 + [rR]*)
146 + if ! remote_present; then
147 + if test -n "$(git ls-tree MERGE_HEAD -- "$MERGED")"; then
148 + # Remote isn't present, but it's a subdirectory
149 + git ls-tree --full-name -r MERGE_HEAD -- "$MERGED" | git update-index --index-info || exit $?
150 + else
151 + test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
152 + git update-index --force-remove "$MERGED"
153 + fi
154 + elif is_submodule "$remote_mode"; then
155 + ! is_submodule "$local_mode" && test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
156 + stage_submodule "$MERGED" "$remote_sha1"
157 + else
158 + test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
159 + git checkout-index -f --stage=3 -- "$MERGED"
160 + git add -- "$MERGED"
161 + fi
162 + cleanup_temp_files --save-backup
163 + return 0
164 + ;;
165 + [aA]*)
166 + return 1
167 + ;;
168 + esac
169 + done
170 +}
171 +
172 +stage_submodule () {
173 + path="$1"
174 + submodule_sha1="$2"
175 + mkdir -p "$path" || die "fatal: unable to create directory for module at $path"
176 + # Find $path relative to work tree
177 + work_tree_root=$(cd_to_toplevel && pwd)
178 + work_rel_path=$(cd "$path" && GIT_WORK_TREE="${work_tree_root}" git rev-parse --show-prefix)
179 + test -n "$work_rel_path" || die "fatal: unable to get path of module $path relative to work tree"
180 + git update-index --add --replace --cacheinfo 160000 "$submodule_sha1" "${work_rel_path%/}" || die
181 +}
182 +
183 checkout_staged_file () {
184 tmpfile=$(expr "$(git checkout-index --temp --stage="$1" "$2")" : '\([^ ]*\) ')
185
@@ -139,13 +207,23 @@ merge_file () {
207 REMOTE="./$MERGED.REMOTE.$ext"
208 BASE="./$MERGED.BASE.$ext"
209
142 - mv -- "$MERGED" "$BACKUP"
143 - cp -- "$BACKUP" "$MERGED"
144 -
210 base_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==1) print $1;}')
211 local_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $1;}')
212 remote_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $1;}')
213
214 + if is_submodule "$local_mode" || is_submodule "$remote_mode"; then
215 + echo "Submodule merge conflict for '$MERGED':"
216 + local_sha1=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $2;}')
217 + remote_sha1=$(git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $2;}')
218 + describe_file "$local_mode" "local" "$local_sha1"
219 + describe_file "$remote_mode" "remote" "$remote_sha1"
220 + resolve_submodule_merge
221 + return
222 + fi
223 +
224 + mv -- "$MERGED" "$BACKUP"
225 + cp -- "$BACKUP" "$MERGED"
226 +
227 base_present && checkout_staged_file 1 "$MERGED" "$BASE"
228 local_present && checkout_staged_file 2 "$MERGED" "$LOCAL"
229 remote_present && checkout_staged_file 3 "$MERGED" "$REMOTE"
t/t7610-mergetool.sh
+287 -3
@@ -22,26 +22,50 @@ test_expect_success 'setup' '
22 echo master file14 >file14 &&
23 mkdir subdir &&
24 echo master sub >subdir/file3 &&
25 - git add file1 file1[1-4] subdir/file3 &&
25 + test_create_repo submod &&
26 + (
27 + cd submod &&
28 + : >foo &&
29 + git add foo &&
30 + git commit -m "Add foo"
31 + ) &&
32 + git submodule add git://example.com/submod submod &&
33 + git add file1 file1[1-4] subdir/file3 .gitmodules submod &&
34 git commit -m "add initial versions" &&
35
36 git checkout -b branch1 master &&
37 + git submodule update -N &&
38 echo branch1 change >file1 &&
39 echo branch1 newfile >file2 &&
40 echo branch1 change file11 >file11 &&
41 echo branch1 change file13 >file13 &&
42 echo branch1 sub >subdir/file3 &&
34 - git add file1 file11 file13 file2 subdir/file3 &&
43 + (
44 + cd submod &&
45 + echo branch1 submodule >bar &&
46 + git add bar &&
47 + git commit -m "Add bar on branch1" &&
48 + git checkout -b submod-branch1
49 + ) &&
50 + git add file1 file11 file13 file2 subdir/file3 submod &&
51 git rm file12 &&
52 git commit -m "branch1 changes" &&
53
54 git checkout master &&
55 + git submodule update -N &&
56 echo master updated >file1 &&
57 echo master new >file2 &&
58 echo master updated file12 >file12 &&
59 echo master updated file14 >file14 &&
60 echo master new sub >subdir/file3 &&
44 - git add file1 file12 file14 file2 subdir/file3 &&
61 + (
62 + cd submod &&
63 + echo master submodule >bar &&
64 + git add bar &&
65 + git commit -m "Add bar on master" &&
66 + git checkout -b submod-master
67 + ) &&
68 + git add file1 file12 file14 file2 subdir/file3 submod &&
69 git rm file11 &&
70 git commit -m "master updates" &&
71
@@ -52,15 +76,18 @@ test_expect_success 'setup' '
76
77 test_expect_success 'custom mergetool' '
78 git checkout -b test1 branch1 &&
79 + git submodule update -N &&
80 test_must_fail git merge master >/dev/null 2>&1 &&
81 ( yes "" | git mergetool file1 >/dev/null 2>&1 ) &&
82 ( yes "" | git mergetool file2 >/dev/null 2>&1 ) &&
83 ( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&
84 ( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
85 ( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
86 + ( yes "l" | git mergetool submod >/dev/null 2>&1 ) &&
87 test "$(cat file1)" = "master updated" &&
88 test "$(cat file2)" = "master new" &&
89 test "$(cat subdir/file3)" = "master new sub" &&
90 + test "$(cat submod/bar)" = "branch1 submodule" &&
91 git commit -m "branch1 resolved with mergetool"
92 '
93
@@ -73,9 +100,12 @@ test_expect_success 'mergetool crlf' '
100 ( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&
101 ( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
102 ( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
103 + ( yes "r" | git mergetool submod >/dev/null 2>&1 ) &&
104 test "$(printf x | cat file1 -)" = "$(printf "master updated\r\nx")" &&
105 test "$(printf x | cat file2 -)" = "$(printf "master new\r\nx")" &&
106 test "$(printf x | cat subdir/file3 -)" = "$(printf "master new sub\r\nx")" &&
107 + git submodule update -N &&
108 + test "$(cat submod/bar)" = "master submodule" &&
109 git commit -m "branch1 resolved with mergetool - autocrlf" &&
110 git config core.autocrlf false &&
111 git reset --hard
@@ -83,6 +113,7 @@ test_expect_success 'mergetool crlf' '
113
114 test_expect_success 'mergetool in subdir' '
115 git checkout -b test3 branch1 &&
116 + git submodule update -N &&
117 (
118 cd subdir &&
119 test_must_fail git merge master >/dev/null 2>&1 &&
@@ -98,18 +129,22 @@ test_expect_success 'mergetool on file in parent dir' '
129 ( yes "" | git mergetool ../file2 >/dev/null 2>&1 ) &&
130 ( yes "d" | git mergetool ../file11 >/dev/null 2>&1 ) &&
131 ( yes "d" | git mergetool ../file12 >/dev/null 2>&1 ) &&
132 + ( yes "l" | git mergetool ../submod >/dev/null 2>&1 ) &&
133 test "$(cat ../file1)" = "master updated" &&
134 test "$(cat ../file2)" = "master new" &&
135 + test "$(cat ../submod/bar)" = "branch1 submodule" &&
136 git commit -m "branch1 resolved with mergetool - subdir"
137 )
138 '
139
140 test_expect_success 'mergetool skips autoresolved' '
141 git checkout -b test4 branch1 &&
142 + git submodule update -N &&
143 test_must_fail git merge master &&
144 test -n "$(git ls-files -u)" &&
145 ( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
146 ( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
147 + ( yes "l" | git mergetool submod >/dev/null 2>&1 ) &&
148 output="$(git mergetool --no-prompt)" &&
149 test "$output" = "No files need merging" &&
150 git reset --hard
@@ -120,10 +155,13 @@ test_expect_success 'mergetool merges all from subdir' '
155 cd subdir &&
156 git config rerere.enabled false &&
157 test_must_fail git merge master &&
158 + ( yes "r" | git mergetool ../submod ) &&
159 ( yes "d" "d" | git mergetool --no-prompt ) &&
160 test "$(cat ../file1)" = "master updated" &&
161 test "$(cat ../file2)" = "master new" &&
162 test "$(cat file3)" = "master new sub" &&
163 + ( cd .. && git submodule update -N ) &&
164 + test "$(cat ../submod/bar)" = "master submodule" &&
165 git commit -m "branch2 resolved by mergetool from subdir"
166 )
167 '
@@ -132,11 +170,257 @@ test_expect_success 'mergetool skips resolved paths when rerere is active' '
170 git config rerere.enabled true &&
171 rm -rf .git/rr-cache &&
172 git checkout -b test5 branch1
173 + git submodule update -N &&
174 test_must_fail git merge master >/dev/null 2>&1 &&
175 + ( yes "l" | git mergetool --no-prompt submod >/dev/null 2>&1 ) &&
176 ( yes "d" "d" | git mergetool --no-prompt >/dev/null 2>&1 ) &&
177 + git submodule update -N &&
178 output="$(yes "n" | git mergetool --no-prompt)" &&
179 test "$output" = "No files need merging" &&
180 git reset --hard
181 '
182
183 +test_expect_success 'deleted vs modified submodule' '
184 + git checkout -b test6 branch1 &&
185 + git submodule update -N &&
186 + mv submod submod-movedaside &&
187 + git rm submod &&
188 + git commit -m "Submodule deleted from branch" &&
189 + git checkout -b test6.a test6 &&
190 + test_must_fail git merge master &&
191 + test -n "$(git ls-files -u)" &&
192 + ( yes "" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) &&
193 + ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
194 + ( yes "r" | git mergetool submod ) &&
195 + rmdir submod && mv submod-movedaside submod &&
196 + test "$(cat submod/bar)" = "branch1 submodule" &&
197 + git submodule update -N &&
198 + test "$(cat submod/bar)" = "master submodule" &&
199 + output="$(git mergetool --no-prompt)" &&
200 + test "$output" = "No files need merging" &&
201 + git commit -m "Merge resolved by keeping module" &&
202 +
203 + mv submod submod-movedaside &&
204 + git checkout -b test6.b test6 &&
205 + git submodule update -N &&
206 + test_must_fail git merge master &&
207 + test -n "$(git ls-files -u)" &&
208 + ( yes "" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) &&
209 + ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
210 + ( yes "l" | git mergetool submod ) &&
211 + test ! -e submod &&
212 + output="$(git mergetool --no-prompt)" &&
213 + test "$output" = "No files need merging" &&
214 + git commit -m "Merge resolved by deleting module" &&
215 +
216 + mv submod-movedaside submod &&
217 + git checkout -b test6.c master &&
218 + git submodule update -N &&
219 + test_must_fail git merge test6 &&
220 + test -n "$(git ls-files -u)" &&
221 + ( yes "" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) &&
222 + ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
223 + ( yes "r" | git mergetool submod ) &&
224 + test ! -e submod &&
225 + test -d submod.orig &&
226 + git submodule update -N &&
227 + output="$(git mergetool --no-prompt)" &&
228 + test "$output" = "No files need merging" &&
229 + git commit -m "Merge resolved by deleting module" &&
230 + mv submod.orig submod &&
231 +
232 + git checkout -b test6.d master &&
233 + git submodule update -N &&
234 + test_must_fail git merge test6 &&
235 + test -n "$(git ls-files -u)" &&
236 + ( yes "" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) &&
237 + ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
238 + ( yes "l" | git mergetool submod ) &&
239 + test "$(cat submod/bar)" = "master submodule" &&
240 + git submodule update -N &&
241 + test "$(cat submod/bar)" = "master submodule" &&
242 + output="$(git mergetool --no-prompt)" &&
243 + test "$output" = "No files need merging" &&
244 + git commit -m "Merge resolved by keeping module" &&
245 + git reset --hard HEAD
246 +'
247 +
248 +test_expect_success 'file vs modified submodule' '
249 + git checkout -b test7 branch1 &&
250 + git submodule update -N &&
251 + mv submod submod-movedaside &&
252 + git rm submod &&
253 + echo not a submodule >submod &&
254 + git add submod &&
255 + git commit -m "Submodule path becomes file" &&
256 + git checkout -b test7.a branch1 &&
257 + test_must_fail git merge master &&
258 + test -n "$(git ls-files -u)" &&
259 + ( yes "" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) &&
260 + ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
261 + ( yes "r" | git mergetool submod ) &&
262 + rmdir submod && mv submod-movedaside submod &&
263 + test "$(cat submod/bar)" = "branch1 submodule" &&
264 + git submodule update -N &&
265 + test "$(cat submod/bar)" = "master submodule" &&
266 + output="$(git mergetool --no-prompt)" &&
267 + test "$output" = "No files need merging" &&
268 + git commit -m "Merge resolved by keeping module" &&
269 +
270 + mv submod submod-movedaside &&
271 + git checkout -b test7.b test7 &&
272 + test_must_fail git merge master &&
273 + test -n "$(git ls-files -u)" &&
274 + ( yes "" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) &&
275 + ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
276 + ( yes "l" | git mergetool submod ) &&
277 + git submodule update -N &&
278 + test "$(cat submod)" = "not a submodule" &&
279 + output="$(git mergetool --no-prompt)" &&
280 + test "$output" = "No files need merging" &&
281 + git commit -m "Merge resolved by keeping file" &&
282 +
283 + git checkout -b test7.c master &&
284 + rmdir submod && mv submod-movedaside submod &&
285 + test ! -e submod.orig &&
286 + git submodule update -N &&
287 + test_must_fail git merge test7 &&
288 + test -n "$(git ls-files -u)" &&
289 + ( yes "" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) &&
290 + ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
291 + ( yes "r" | git mergetool submod ) &&
292 + test -d submod.orig &&
293 + git submodule update -N &&
294 + test "$(cat submod)" = "not a submodule" &&
295 + output="$(git mergetool --no-prompt)" &&
296 + test "$output" = "No files need merging" &&
297 + git commit -m "Merge resolved by keeping file" &&
298 +
299 + git checkout -b test7.d master &&
300 + rmdir submod && mv submod.orig submod &&
301 + git submodule update -N &&
302 + test_must_fail git merge test7 &&
303 + test -n "$(git ls-files -u)" &&
304 + ( yes "" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) &&
305 + ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
306 + ( yes "l" | git mergetool submod ) &&
307 + test "$(cat submod/bar)" = "master submodule" &&
308 + git submodule update -N &&
309 + test "$(cat submod/bar)" = "master submodule" &&
310 + output="$(git mergetool --no-prompt)" &&
311 + test "$output" = "No files need merging" &&
312 + git commit -m "Merge resolved by keeping module"
313 +'
314 +
315 +test_expect_success 'submodule in subdirectory' '
316 + git checkout -b test10 branch1 &&
317 + git submodule update -N &&
318 + (
319 + cd subdir &&
320 + test_create_repo subdir_module &&
321 + (
322 + cd subdir_module &&
323 + : >file15 &&
324 + git add file15 &&
325 + git commit -m "add initial versions"
326 + )
327 + ) &&
328 + git submodule add git://example.com/subsubmodule subdir/subdir_module &&
329 + git add subdir/subdir_module &&
330 + git commit -m "add submodule in subdirectory" &&
331 +
332 + git checkout -b test10.a test10 &&
333 + git submodule update -N &&
334 + (
335 + cd subdir/subdir_module &&
336 + git checkout -b super10.a &&
337 + echo test10.a >file15 &&
338 + git add file15 &&
339 + git commit -m "on branch 10.a"
340 + ) &&
341 + git add subdir/subdir_module &&
342 + git commit -m "change submodule in subdirectory on test10.a" &&
343 +
344 + git checkout -b test10.b test10 &&
345 + git submodule update -N &&
346 + (
347 + cd subdir/subdir_module &&
348 + git checkout -b super10.b &&
349 + echo test10.b >file15 &&
350 + git add file15 &&
351 + git commit -m "on branch 10.b"
352 + ) &&
353 + git add subdir/subdir_module &&
354 + git commit -m "change submodule in subdirectory on test10.b" &&
355 +
356 + test_must_fail git merge test10.a >/dev/null 2>&1 &&
357 + (
358 + cd subdir &&
359 + ( yes "l" | git mergetool subdir_module )
360 + ) &&
361 + test "$(cat subdir/subdir_module/file15)" = "test10.b" &&
362 + git submodule update -N &&
363 + test "$(cat subdir/subdir_module/file15)" = "test10.b" &&
364 + git reset --hard &&
365 + git submodule update -N &&
366 +
367 + test_must_fail git merge test10.a >/dev/null 2>&1 &&
368 + ( yes "r" | git mergetool subdir/subdir_module ) &&
369 + test "$(cat subdir/subdir_module/file15)" = "test10.b" &&
370 + git submodule update -N &&
371 + test "$(cat subdir/subdir_module/file15)" = "test10.a" &&
372 + git commit -m "branch1 resolved with mergetool" &&
373 + rm -rf subdir/subdir_module
374 +'
375 +
376 +test_expect_success 'directory vs modified submodule' '
377 + git checkout -b test11 branch1 &&
378 + mv submod submod-movedaside &&
379 + git rm submod &&
380 + mkdir submod &&
381 + echo not a submodule >submod/file16 &&
382 + git add submod/file16 &&
383 + git commit -m "Submodule path becomes directory" &&
384 +
385 + test_must_fail git merge master &&
386 + test -n "$(git ls-files -u)" &&
387 + ( yes "l" | git mergetool submod ) &&
388 + test "$(cat submod/file16)" = "not a submodule" &&
389 + rm -rf submod.orig &&
390 +
391 + git reset --hard &&
392 + test_must_fail git merge master &&
393 + test -n "$(git ls-files -u)" &&
394 + test ! -e submod.orig &&
395 + ( yes "r" | git mergetool submod ) &&
396 + test -d submod.orig &&
397 + test "$(cat submod.orig/file16)" = "not a submodule" &&
398 + rm -r submod.orig &&
399 + mv submod-movedaside/.git submod &&
400 + ( cd submod && git clean -f && git reset --hard ) &&
401 + git submodule update -N &&
402 + test "$(cat submod/bar)" = "master submodule" &&
403 + git reset --hard && rm -rf submod-movedaside &&
404 +
405 + git checkout -b test11.c master &&
406 + git submodule update -N &&
407 + test_must_fail git merge test11 &&
408 + test -n "$(git ls-files -u)" &&
409 + ( yes "l" | git mergetool submod ) &&
410 + git submodule update -N &&
411 + test "$(cat submod/bar)" = "master submodule" &&
412 +
413 + git reset --hard &&
414 + git submodule update -N &&
415 + test_must_fail git merge test11 &&
416 + test -n "$(git ls-files -u)" &&
417 + test ! -e submod.orig &&
418 + ( yes "r" | git mergetool submod ) &&
419 + test "$(cat submod/file16)" = "not a submodule" &&
420 +
421 + git reset --hard master &&
422 + ( cd submod && git clean -f && git reset --hard ) &&
423 + git submodule update -N
424 +'
425 +
426 test_done