]>
Commit | Line | Data |
---|---|---|
6912ea95 | 1 | #!/bin/sh |
0ca71b37 AP |
2 | # |
3 | # git-subtree.sh: split/join git repositories in subdirectories of this one | |
4 | # | |
b77172f8 | 5 | # Copyright (C) 2009 Avery Pennarun <apenwarr@gmail.com> |
0ca71b37 | 6 | # |
5a356977 | 7 | |
f7ee88f1 JS |
8 | if test -z "$GIT_EXEC_PATH" || ! test -f "$GIT_EXEC_PATH/git-sh-setup" || { |
9 | test "${PATH#"${GIT_EXEC_PATH}:"}" = "$PATH" && | |
10 | test ! "$GIT_EXEC_PATH" -ef "${PATH%%:*}" 2>/dev/null | |
11 | } | |
22d55074 | 12 | then |
77f37de3 | 13 | basename=${0##*[/\\]} |
22d55074 LS |
14 | echo >&2 'It looks like either your git installation or your' |
15 | echo >&2 'git-subtree installation is broken.' | |
16 | echo >&2 | |
17 | echo >&2 "Tips:" | |
18 | echo >&2 " - If \`git --exec-path\` does not print the correct path to" | |
19 | echo >&2 " your git install directory, then set the GIT_EXEC_PATH" | |
20 | echo >&2 " environment variable to the correct directory." | |
77f37de3 | 21 | echo >&2 " - Make sure that your \`$basename\` file is either in your" |
22d55074 | 22 | echo >&2 " PATH or in your git exec path (\`$(git --exec-path)\`)." |
77f37de3 JS |
23 | echo >&2 " - You should run git-subtree as \`git ${basename#git-}\`," |
24 | echo >&2 " not as \`$basename\`." >&2 | |
22d55074 LS |
25 | exit 126 |
26 | fi | |
27 | ||
0ca71b37 | 28 | OPTS_SPEC="\ |
f4f29557 | 29 | git subtree add --prefix=<prefix> <commit> |
1c3e0f00 | 30 | git subtree add --prefix=<prefix> <repository> <ref> |
13648af5 | 31 | git subtree merge --prefix=<prefix> <commit> |
6468784d | 32 | git subtree split --prefix=<prefix> [<commit>] |
1c3e0f00 | 33 | git subtree pull --prefix=<prefix> <repository> <ref> |
49470cd4 | 34 | git subtree push --prefix=<prefix> <repository> <refspec> |
0ca71b37 | 35 | -- |
96db2c04 AP |
36 | h,help show the help |
37 | q quiet | |
942dce55 | 38 | d show debug messages |
6e25f79f | 39 | P,prefix= the name of the subdir to split out |
94389e7c | 40 | options for 'split' (also: 'push') |
d0eb1b14 | 41 | annotate= add a prefix to commit message of new commits |
43a39512 AP |
42 | b,branch= create a new branch from the split subtree |
43 | ignore-joins ignore prior --rejoin commits | |
96db2c04 AP |
44 | onto= try connecting new tree to an existing one |
45 | rejoin merge the new branch back into HEAD | |
94389e7c | 46 | options for 'add' and 'merge' (also: 'pull', 'split --rejoin', and 'push --rejoin') |
8e79043c | 47 | squash merge subtree changes as a single commit |
cb655144 | 48 | m,message= use the given message as the commit message for the merge commit |
0ca71b37 | 49 | " |
9c632ea2 | 50 | |
e9525a8a LS |
51 | indent=0 |
52 | ||
5b893f7d ÆAB |
53 | # Usage: say [MSG...] |
54 | say () { | |
55 | if test -z "$arg_quiet" | |
56 | then | |
57 | printf '%s\n' "$*" | |
58 | fi | |
59 | } | |
60 | ||
5cdae0f6 | 61 | # Usage: debug [MSG...] |
d7fd792e | 62 | debug () { |
e2b11e42 | 63 | if test -n "$arg_debug" |
6ae6a233 | 64 | then |
e9525a8a | 65 | printf "%$(($indent * 2))s%s\n" '' "$*" >&2 |
942dce55 AP |
66 | fi |
67 | } | |
68 | ||
5cdae0f6 | 69 | # Usage: progress [MSG...] |
d7fd792e | 70 | progress () { |
5b893f7d | 71 | if test -z "$arg_quiet" |
6ae6a233 | 72 | then |
534ff90d LS |
73 | if test -z "$arg_debug" |
74 | then | |
75 | # Debug mode is off. | |
76 | # | |
77 | # Print one progress line that we keep updating (use | |
78 | # "\r" to return to the beginning of the line, rather | |
79 | # than "\n" to start a new line). This only really | |
80 | # works when stderr is a terminal. | |
81 | printf "%s\r" "$*" >&2 | |
82 | else | |
83 | # Debug mode is on. The `debug` function is regularly | |
84 | # printing to stderr. | |
85 | # | |
86 | # Don't do the one-line-with-"\r" thing, because on a | |
87 | # terminal the debug output would overwrite and hide the | |
88 | # progress output. Add a "progress:" prefix to make the | |
89 | # progress output and the debug output easy to | |
90 | # distinguish. This ensures maximum readability whether | |
91 | # stderr is a terminal or a file. | |
92 | printf "progress: %s\n" "$*" >&2 | |
93 | fi | |
0ca71b37 AP |
94 | fi |
95 | } | |
96 | ||
5cdae0f6 | 97 | # Usage: assert CMD... |
d7fd792e | 98 | assert () { |
6ae6a233 DA |
99 | if ! "$@" |
100 | then | |
a94f9110 | 101 | die "assertion failed: $*" |
2573354e AP |
102 | fi |
103 | } | |
104 | ||
5a356977 LS |
105 | main () { |
106 | if test $# -eq 0 | |
107 | then | |
108 | set -- -h | |
109 | fi | |
9a3e3ca2 LS |
110 | set_args="$(echo "$OPTS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?)" |
111 | eval "$set_args" | |
5a356977 LS |
112 | . git-sh-setup |
113 | require_work_tree | |
6ae6a233 | 114 | |
9a3e3ca2 LS |
115 | # First figure out the command and whether we use --rejoin, so |
116 | # that we can provide more helpful validation when we do the | |
117 | # "real" flag parsing. | |
118 | arg_split_rejoin= | |
119 | allow_split= | |
120 | allow_addmerge= | |
121 | while test $# -gt 0 | |
122 | do | |
123 | opt="$1" | |
124 | shift | |
125 | case "$opt" in | |
126 | --annotate|-b|-P|-m|--onto) | |
127 | shift | |
128 | ;; | |
129 | --rejoin) | |
130 | arg_split_rejoin=1 | |
131 | ;; | |
132 | --no-rejoin) | |
133 | arg_split_rejoin= | |
134 | ;; | |
135 | --) | |
136 | break | |
137 | ;; | |
138 | esac | |
139 | done | |
140 | arg_command=$1 | |
141 | case "$arg_command" in | |
142 | add|merge|pull) | |
143 | allow_addmerge=1 | |
144 | ;; | |
145 | split|push) | |
146 | allow_split=1 | |
147 | allow_addmerge=$arg_split_rejoin | |
148 | ;; | |
149 | *) | |
150 | die "Unknown command '$arg_command'" | |
151 | ;; | |
152 | esac | |
153 | # Reset the arguments array for "real" flag parsing. | |
154 | eval "$set_args" | |
155 | ||
156 | # Begin "real" flag parsing. | |
5b893f7d | 157 | arg_quiet= |
9a3e3ca2 LS |
158 | arg_debug= |
159 | arg_prefix= | |
160 | arg_split_branch= | |
161 | arg_split_onto= | |
162 | arg_split_ignore_joins= | |
163 | arg_split_annotate= | |
164 | arg_addmerge_squash= | |
165 | arg_addmerge_message= | |
5a356977 LS |
166 | while test $# -gt 0 |
167 | do | |
168 | opt="$1" | |
6ae6a233 | 169 | shift |
5a356977 LS |
170 | |
171 | case "$opt" in | |
172 | -q) | |
5b893f7d | 173 | arg_quiet=1 |
5a356977 LS |
174 | ;; |
175 | -d) | |
e2b11e42 | 176 | arg_debug=1 |
5a356977 LS |
177 | ;; |
178 | --annotate) | |
9a3e3ca2 | 179 | test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'." |
e2b11e42 | 180 | arg_split_annotate="$1" |
5a356977 LS |
181 | shift |
182 | ;; | |
183 | --no-annotate) | |
9a3e3ca2 | 184 | test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'." |
e2b11e42 | 185 | arg_split_annotate= |
5a356977 LS |
186 | ;; |
187 | -b) | |
9a3e3ca2 | 188 | test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'." |
e2b11e42 | 189 | arg_split_branch="$1" |
5a356977 LS |
190 | shift |
191 | ;; | |
192 | -P) | |
e2b11e42 | 193 | arg_prefix="${1%/}" |
5a356977 LS |
194 | shift |
195 | ;; | |
196 | -m) | |
9a3e3ca2 | 197 | test -n "$allow_addmerge" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'." |
e2b11e42 | 198 | arg_addmerge_message="$1" |
5a356977 LS |
199 | shift |
200 | ;; | |
201 | --no-prefix) | |
e2b11e42 | 202 | arg_prefix= |
5a356977 LS |
203 | ;; |
204 | --onto) | |
9a3e3ca2 | 205 | test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'." |
e2b11e42 | 206 | arg_split_onto="$1" |
5a356977 LS |
207 | shift |
208 | ;; | |
209 | --no-onto) | |
9a3e3ca2 | 210 | test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'." |
e2b11e42 | 211 | arg_split_onto= |
5a356977 LS |
212 | ;; |
213 | --rejoin) | |
9a3e3ca2 | 214 | test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'." |
5a356977 LS |
215 | ;; |
216 | --no-rejoin) | |
9a3e3ca2 | 217 | test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'." |
5a356977 LS |
218 | ;; |
219 | --ignore-joins) | |
9a3e3ca2 | 220 | test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'." |
e2b11e42 | 221 | arg_split_ignore_joins=1 |
5a356977 LS |
222 | ;; |
223 | --no-ignore-joins) | |
9a3e3ca2 | 224 | test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'." |
e2b11e42 | 225 | arg_split_ignore_joins= |
5a356977 LS |
226 | ;; |
227 | --squash) | |
9a3e3ca2 | 228 | test -n "$allow_addmerge" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'." |
e2b11e42 | 229 | arg_addmerge_squash=1 |
5a356977 LS |
230 | ;; |
231 | --no-squash) | |
9a3e3ca2 | 232 | test -n "$allow_addmerge" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'." |
e2b11e42 | 233 | arg_addmerge_squash= |
5a356977 LS |
234 | ;; |
235 | --) | |
236 | break | |
237 | ;; | |
238 | *) | |
239 | die "Unexpected option: $opt" | |
240 | ;; | |
241 | esac | |
242 | done | |
5a356977 LS |
243 | shift |
244 | ||
e2b11e42 | 245 | if test -z "$arg_prefix" |
5a356977 LS |
246 | then |
247 | die "You must provide the --prefix option." | |
248 | fi | |
249 | ||
e2b11e42 | 250 | case "$arg_command" in |
5a356977 | 251 | add) |
e2b11e42 LS |
252 | test -e "$arg_prefix" && |
253 | die "prefix '$arg_prefix' already exists." | |
6ae6a233 DA |
254 | ;; |
255 | *) | |
e2b11e42 LS |
256 | test -e "$arg_prefix" || |
257 | die "'$arg_prefix' does not exist; use 'git subtree add'" | |
6ae6a233 | 258 | ;; |
0ca71b37 | 259 | esac |
5a356977 | 260 | |
e2b11e42 | 261 | dir="$(dirname "$arg_prefix/.")" |
5a356977 | 262 | |
e2b11e42 | 263 | debug "command: {$arg_command}" |
5b893f7d | 264 | debug "quiet: {$arg_quiet}" |
5a356977 LS |
265 | debug "dir: {$dir}" |
266 | debug "opts: {$*}" | |
267 | debug | |
268 | ||
e2b11e42 | 269 | "cmd_$arg_command" "$@" |
5a356977 | 270 | } |
0ca71b37 | 271 | |
5cdae0f6 | 272 | # Usage: cache_setup |
d7fd792e | 273 | cache_setup () { |
5cdae0f6 | 274 | assert test $# = 0 |
2573354e | 275 | cachedir="$GIT_DIR/subtree-cache/$$" |
6ae6a233 DA |
276 | rm -rf "$cachedir" || |
277 | die "Can't delete old cachedir: $cachedir" | |
278 | mkdir -p "$cachedir" || | |
279 | die "Can't create new cachedir: $cachedir" | |
280 | mkdir -p "$cachedir/notree" || | |
281 | die "Can't create new cachedir: $cachedir/notree" | |
0ca71b37 | 282 | debug "Using cachedir: $cachedir" >&2 |
0ca71b37 AP |
283 | } |
284 | ||
5cdae0f6 | 285 | # Usage: cache_get [REVS...] |
d7fd792e | 286 | cache_get () { |
6ae6a233 DA |
287 | for oldrev in "$@" |
288 | do | |
289 | if test -r "$cachedir/$oldrev" | |
290 | then | |
0ca71b37 AP |
291 | read newrev <"$cachedir/$oldrev" |
292 | echo $newrev | |
293 | fi | |
294 | done | |
295 | } | |
296 | ||
5cdae0f6 | 297 | # Usage: cache_miss [REVS...] |
d7fd792e | 298 | cache_miss () { |
6ae6a233 DA |
299 | for oldrev in "$@" |
300 | do | |
301 | if ! test -r "$cachedir/$oldrev" | |
302 | then | |
915b9894 JG |
303 | echo $oldrev |
304 | fi | |
305 | done | |
306 | } | |
307 | ||
3ce8888f | 308 | # Usage: check_parents [REVS...] |
d7fd792e | 309 | check_parents () { |
3ce8888f | 310 | missed=$(cache_miss "$@") || exit $? |
e9525a8a | 311 | local indent=$(($indent + 1)) |
6ae6a233 DA |
312 | for miss in $missed |
313 | do | |
314 | if ! test -r "$cachedir/notree/$miss" | |
315 | then | |
e9525a8a LS |
316 | debug "incorrect order: $miss" |
317 | process_split_commit "$miss" "" | |
915b9894 JG |
318 | fi |
319 | done | |
320 | } | |
321 | ||
5cdae0f6 | 322 | # Usage: set_notree REV |
d7fd792e | 323 | set_notree () { |
5cdae0f6 | 324 | assert test $# = 1 |
915b9894 JG |
325 | echo "1" > "$cachedir/notree/$1" |
326 | } | |
327 | ||
5cdae0f6 | 328 | # Usage: cache_set OLDREV NEWREV |
d7fd792e | 329 | cache_set () { |
5cdae0f6 | 330 | assert test $# = 2 |
0ca71b37 AP |
331 | oldrev="$1" |
332 | newrev="$2" | |
6ae6a233 DA |
333 | if test "$oldrev" != "latest_old" && |
334 | test "$oldrev" != "latest_new" && | |
335 | test -e "$cachedir/$oldrev" | |
336 | then | |
0ca71b37 AP |
337 | die "cache for $oldrev already exists!" |
338 | fi | |
339 | echo "$newrev" >"$cachedir/$oldrev" | |
340 | } | |
341 | ||
5cdae0f6 | 342 | # Usage: rev_exists REV |
d7fd792e | 343 | rev_exists () { |
5cdae0f6 | 344 | assert test $# = 1 |
6ae6a233 DA |
345 | if git rev-parse "$1" >/dev/null 2>&1 |
346 | then | |
43a39512 AP |
347 | return 0 |
348 | else | |
349 | return 1 | |
350 | fi | |
351 | } | |
352 | ||
5cdae0f6 LS |
353 | # Usage: try_remove_previous REV |
354 | # | |
355 | # If a commit doesn't have a parent, this might not work. But we only want | |
b9de5353 AP |
356 | # to remove the parent from the rev-list, and since it doesn't exist, it won't |
357 | # be there anyway, so do nothing in that case. | |
d7fd792e | 358 | try_remove_previous () { |
5cdae0f6 | 359 | assert test $# = 1 |
6ae6a233 DA |
360 | if rev_exists "$1^" |
361 | then | |
b9de5353 AP |
362 | echo "^$1^" |
363 | fi | |
364 | } | |
365 | ||
5cdae0f6 | 366 | # Usage: find_latest_squash DIR |
d7fd792e | 367 | find_latest_squash () { |
5cdae0f6 | 368 | assert test $# = 1 |
d713e2d8 | 369 | debug "Looking for latest squash ($dir)..." |
e9525a8a LS |
370 | local indent=$(($indent + 1)) |
371 | ||
1cc2cfff | 372 | dir="$1" |
d713e2d8 AP |
373 | sq= |
374 | main= | |
375 | sub= | |
6f2012cd | 376 | git log --grep="^git-subtree-dir: $dir/*\$" \ |
8841b522 | 377 | --no-show-signature --pretty=format:'START %H%n%s%n%n%b%nEND%n' HEAD | |
6ae6a233 DA |
378 | while read a b junk |
379 | do | |
d713e2d8 AP |
380 | debug "$a $b $junk" |
381 | debug "{{$sq/$main/$sub}}" | |
1cc2cfff | 382 | case "$a" in |
6ae6a233 DA |
383 | START) |
384 | sq="$b" | |
385 | ;; | |
386 | git-subtree-mainline:) | |
387 | main="$b" | |
388 | ;; | |
389 | git-subtree-split:) | |
bbffb023 | 390 | sub="$(git rev-parse "$b^{commit}")" || |
6ae6a233 DA |
391 | die "could not rev-parse split hash $b from commit $sq" |
392 | ;; | |
393 | END) | |
394 | if test -n "$sub" | |
395 | then | |
396 | if test -n "$main" | |
397 | then | |
398 | # a rejoin commit? | |
399 | # Pretend its sub was a squash. | |
cb655144 LS |
400 | sq=$(git rev-parse --verify "$sq^2") || |
401 | die | |
1cc2cfff | 402 | fi |
6ae6a233 DA |
403 | debug "Squash found: $sq $sub" |
404 | echo "$sq" "$sub" | |
405 | break | |
406 | fi | |
407 | sq= | |
408 | main= | |
409 | sub= | |
410 | ;; | |
1cc2cfff | 411 | esac |
d2f0f819 | 412 | done || exit $? |
1cc2cfff AP |
413 | } |
414 | ||
5cdae0f6 | 415 | # Usage: find_existing_splits DIR REV |
d7fd792e | 416 | find_existing_splits () { |
5cdae0f6 | 417 | assert test $# = 2 |
8b4a77f2 | 418 | debug "Looking for prior splits..." |
e9525a8a LS |
419 | local indent=$(($indent + 1)) |
420 | ||
8b4a77f2 | 421 | dir="$1" |
5cdae0f6 | 422 | rev="$2" |
d713e2d8 AP |
423 | main= |
424 | sub= | |
dd21d43b | 425 | local grep_format="^git-subtree-dir: $dir/*\$" |
e2b11e42 | 426 | if test -n "$arg_split_ignore_joins" |
dd21d43b SR |
427 | then |
428 | grep_format="^Add '$dir/' from commit '" | |
429 | fi | |
430 | git log --grep="$grep_format" \ | |
5cdae0f6 | 431 | --no-show-signature --pretty=format:'START %H%n%s%n%n%b%nEND%n' "$rev" | |
6ae6a233 DA |
432 | while read a b junk |
433 | do | |
8b4a77f2 | 434 | case "$a" in |
6ae6a233 DA |
435 | START) |
436 | sq="$b" | |
437 | ;; | |
438 | git-subtree-mainline:) | |
439 | main="$b" | |
440 | ;; | |
441 | git-subtree-split:) | |
bbffb023 | 442 | sub="$(git rev-parse "$b^{commit}")" || |
6ae6a233 DA |
443 | die "could not rev-parse split hash $b from commit $sq" |
444 | ;; | |
445 | END) | |
e9525a8a | 446 | debug "Main is: '$main'" |
6ae6a233 DA |
447 | if test -z "$main" -a -n "$sub" |
448 | then | |
449 | # squash commits refer to a subtree | |
450 | debug " Squash: $sq from $sub" | |
451 | cache_set "$sq" "$sub" | |
452 | fi | |
453 | if test -n "$main" -a -n "$sub" | |
454 | then | |
455 | debug " Prior: $main -> $sub" | |
456 | cache_set $main $sub | |
457 | cache_set $sub $sub | |
458 | try_remove_previous "$main" | |
459 | try_remove_previous "$sub" | |
460 | fi | |
461 | main= | |
462 | sub= | |
463 | ;; | |
8b4a77f2 | 464 | esac |
d2f0f819 | 465 | done || exit $? |
8b4a77f2 AP |
466 | } |
467 | ||
5cdae0f6 | 468 | # Usage: copy_commit REV TREE FLAGS_STR |
d7fd792e | 469 | copy_commit () { |
5cdae0f6 | 470 | assert test $# = 3 |
f96bc790 | 471 | # We're going to set some environment vars here, so |
fd9500ee | 472 | # do it in a subshell to get rid of them safely later |
a64f3a72 | 473 | debug copy_commit "{$1}" "{$2}" "{$3}" |
8841b522 | 474 | git log -1 --no-show-signature --pretty=format:'%an%n%ae%n%aD%n%cn%n%ce%n%cD%n%B' "$1" | |
fd9500ee AP |
475 | ( |
476 | read GIT_AUTHOR_NAME | |
477 | read GIT_AUTHOR_EMAIL | |
478 | read GIT_AUTHOR_DATE | |
479 | read GIT_COMMITTER_NAME | |
480 | read GIT_COMMITTER_EMAIL | |
481 | read GIT_COMMITTER_DATE | |
b77172f8 AP |
482 | export GIT_AUTHOR_NAME \ |
483 | GIT_AUTHOR_EMAIL \ | |
484 | GIT_AUTHOR_DATE \ | |
485 | GIT_COMMITTER_NAME \ | |
486 | GIT_COMMITTER_EMAIL \ | |
487 | GIT_COMMITTER_DATE | |
6ae6a233 | 488 | ( |
e2b11e42 | 489 | printf "%s" "$arg_split_annotate" |
6ae6a233 DA |
490 | cat |
491 | ) | | |
fd9500ee AP |
492 | git commit-tree "$2" $3 # reads the rest of stdin |
493 | ) || die "Can't copy commit $1" | |
494 | } | |
495 | ||
5cdae0f6 | 496 | # Usage: add_msg DIR LATEST_OLD LATEST_NEW |
d7fd792e | 497 | add_msg () { |
5cdae0f6 | 498 | assert test $# = 3 |
eb7b590c AP |
499 | dir="$1" |
500 | latest_old="$2" | |
501 | latest_new="$3" | |
e2b11e42 | 502 | if test -n "$arg_addmerge_message" |
6ae6a233 | 503 | then |
e2b11e42 | 504 | commit_message="$arg_addmerge_message" |
2da0969a JS |
505 | else |
506 | commit_message="Add '$dir/' from commit '$latest_new'" | |
507 | fi | |
cb655144 LS |
508 | if test -n "$arg_split_rejoin" |
509 | then | |
510 | # If this is from a --rejoin, then rejoin_msg has | |
511 | # already inserted the `git-subtree-xxx:` tags | |
512 | echo "$commit_message" | |
513 | return | |
514 | fi | |
eb7b590c | 515 | cat <<-EOF |
2da0969a | 516 | $commit_message |
6ae6a233 | 517 | |
eb7b590c AP |
518 | git-subtree-dir: $dir |
519 | git-subtree-mainline: $latest_old | |
520 | git-subtree-split: $latest_new | |
521 | EOF | |
522 | } | |
523 | ||
5cdae0f6 | 524 | # Usage: add_squashed_msg REV DIR |
d7fd792e | 525 | add_squashed_msg () { |
5cdae0f6 | 526 | assert test $# = 2 |
e2b11e42 | 527 | if test -n "$arg_addmerge_message" |
6ae6a233 | 528 | then |
e2b11e42 | 529 | echo "$arg_addmerge_message" |
2da0969a JS |
530 | else |
531 | echo "Merge commit '$1' as '$2'" | |
532 | fi | |
533 | } | |
534 | ||
5cdae0f6 | 535 | # Usage: rejoin_msg DIR LATEST_OLD LATEST_NEW |
d7fd792e | 536 | rejoin_msg () { |
5cdae0f6 | 537 | assert test $# = 3 |
b77172f8 AP |
538 | dir="$1" |
539 | latest_old="$2" | |
540 | latest_new="$3" | |
e2b11e42 | 541 | if test -n "$arg_addmerge_message" |
6ae6a233 | 542 | then |
e2b11e42 | 543 | commit_message="$arg_addmerge_message" |
2da0969a JS |
544 | else |
545 | commit_message="Split '$dir/' into commit '$latest_new'" | |
546 | fi | |
b77172f8 | 547 | cat <<-EOF |
12629161 | 548 | $commit_message |
6ae6a233 | 549 | |
b77172f8 | 550 | git-subtree-dir: $dir |
8b4a77f2 AP |
551 | git-subtree-mainline: $latest_old |
552 | git-subtree-split: $latest_new | |
b77172f8 AP |
553 | EOF |
554 | } | |
555 | ||
5cdae0f6 | 556 | # Usage: squash_msg DIR OLD_SUBTREE_COMMIT NEW_SUBTREE_COMMIT |
d7fd792e | 557 | squash_msg () { |
5cdae0f6 | 558 | assert test $# = 3 |
1cc2cfff AP |
559 | dir="$1" |
560 | oldsub="$2" | |
561 | newsub="$3" | |
1cc2cfff | 562 | newsub_short=$(git rev-parse --short "$newsub") |
6ae6a233 DA |
563 | |
564 | if test -n "$oldsub" | |
565 | then | |
d713e2d8 AP |
566 | oldsub_short=$(git rev-parse --short "$oldsub") |
567 | echo "Squashed '$dir/' changes from $oldsub_short..$newsub_short" | |
568 | echo | |
8841b522 SG |
569 | git log --no-show-signature --pretty=tformat:'%h %s' "$oldsub..$newsub" |
570 | git log --no-show-signature --pretty=tformat:'REVERT: %h %s' "$newsub..$oldsub" | |
d713e2d8 AP |
571 | else |
572 | echo "Squashed '$dir/' content from commit $newsub_short" | |
573 | fi | |
6ae6a233 | 574 | |
d713e2d8 AP |
575 | echo |
576 | echo "git-subtree-dir: $dir" | |
577 | echo "git-subtree-split: $newsub" | |
1cc2cfff AP |
578 | } |
579 | ||
5cdae0f6 | 580 | # Usage: toptree_for_commit COMMIT |
d7fd792e | 581 | toptree_for_commit () { |
5cdae0f6 | 582 | assert test $# = 1 |
210d0839 | 583 | commit="$1" |
8841b522 | 584 | git rev-parse --verify "$commit^{tree}" || exit $? |
210d0839 AP |
585 | } |
586 | ||
5cdae0f6 | 587 | # Usage: subtree_for_commit COMMIT DIR |
d7fd792e | 588 | subtree_for_commit () { |
5cdae0f6 | 589 | assert test $# = 2 |
210d0839 AP |
590 | commit="$1" |
591 | dir="$2" | |
592 | git ls-tree "$commit" -- "$dir" | | |
6ae6a233 DA |
593 | while read mode type tree name |
594 | do | |
595 | assert test "$name" = "$dir" | |
596 | assert test "$type" = "tree" -o "$type" = "commit" | |
597 | test "$type" = "commit" && continue # ignore submodules | |
768d6d10 AP |
598 | echo $tree |
599 | break | |
d2f0f819 | 600 | done || exit $? |
768d6d10 AP |
601 | } |
602 | ||
5cdae0f6 | 603 | # Usage: tree_changed TREE [PARENTS...] |
d7fd792e | 604 | tree_changed () { |
5cdae0f6 | 605 | assert test $# -gt 0 |
768d6d10 AP |
606 | tree=$1 |
607 | shift | |
6ae6a233 DA |
608 | if test $# -ne 1 |
609 | then | |
768d6d10 AP |
610 | return 0 # weird parents, consider it changed |
611 | else | |
d2f0f819 | 612 | ptree=$(toptree_for_commit $1) || exit $? |
6ae6a233 DA |
613 | if test "$ptree" != "$tree" |
614 | then | |
768d6d10 AP |
615 | return 0 # changed |
616 | else | |
617 | return 1 # not changed | |
618 | fi | |
619 | fi | |
620 | } | |
621 | ||
5cdae0f6 | 622 | # Usage: new_squash_commit OLD_SQUASHED_COMMIT OLD_NONSQUASHED_COMMIT NEW_NONSQUASHED_COMMIT |
d7fd792e | 623 | new_squash_commit () { |
5cdae0f6 | 624 | assert test $# = 3 |
1cc2cfff AP |
625 | old="$1" |
626 | oldsub="$2" | |
627 | newsub="$3" | |
628 | tree=$(toptree_for_commit $newsub) || exit $? | |
6ae6a233 DA |
629 | if test -n "$old" |
630 | then | |
631 | squash_msg "$dir" "$oldsub" "$newsub" | | |
632 | git commit-tree "$tree" -p "$old" || exit $? | |
d713e2d8 AP |
633 | else |
634 | squash_msg "$dir" "" "$newsub" | | |
6ae6a233 | 635 | git commit-tree "$tree" || exit $? |
d713e2d8 | 636 | fi |
1cc2cfff AP |
637 | } |
638 | ||
5cdae0f6 | 639 | # Usage: copy_or_skip REV TREE NEWPARENTS |
d7fd792e | 640 | copy_or_skip () { |
5cdae0f6 | 641 | assert test $# = 3 |
d6912658 AP |
642 | rev="$1" |
643 | tree="$2" | |
644 | newparents="$3" | |
6ae6a233 | 645 | assert test -n "$tree" |
d6912658 | 646 | |
96db2c04 | 647 | identical= |
49cf8228 | 648 | nonidentical= |
96db2c04 | 649 | p= |
a64f3a72 | 650 | gotparents= |
68f8ff81 | 651 | copycommit= |
6ae6a233 DA |
652 | for parent in $newparents |
653 | do | |
d6912658 | 654 | ptree=$(toptree_for_commit $parent) || exit $? |
6ae6a233 DA |
655 | test -z "$ptree" && continue |
656 | if test "$ptree" = "$tree" | |
657 | then | |
96db2c04 | 658 | # an identical parent could be used in place of this rev. |
68f8ff81 SR |
659 | if test -n "$identical" |
660 | then | |
661 | # if a previous identical parent was found, check whether | |
662 | # one is already an ancestor of the other | |
663 | mergebase=$(git merge-base $identical $parent) | |
664 | if test "$identical" = "$mergebase" | |
665 | then | |
666 | # current identical commit is an ancestor of parent | |
667 | identical="$parent" | |
668 | elif test "$parent" != "$mergebase" | |
669 | then | |
670 | # no common history; commit must be copied | |
671 | copycommit=1 | |
672 | fi | |
673 | else | |
674 | # first identical parent detected | |
675 | identical="$parent" | |
676 | fi | |
49cf8228 AP |
677 | else |
678 | nonidentical="$parent" | |
96db2c04 | 679 | fi |
6ae6a233 | 680 | |
a64f3a72 AP |
681 | # sometimes both old parents map to the same newparent; |
682 | # eliminate duplicates | |
683 | is_new=1 | |
6ae6a233 DA |
684 | for gp in $gotparents |
685 | do | |
686 | if test "$gp" = "$parent" | |
687 | then | |
a64f3a72 AP |
688 | is_new= |
689 | break | |
690 | fi | |
691 | done | |
6ae6a233 DA |
692 | if test -n "$is_new" |
693 | then | |
a64f3a72 | 694 | gotparents="$gotparents $parent" |
d6912658 AP |
695 | p="$p -p $parent" |
696 | fi | |
697 | done | |
933cfeb9 | 698 | |
6ae6a233 DA |
699 | if test -n "$identical" && test -n "$nonidentical" |
700 | then | |
933cfeb9 | 701 | extras=$(git rev-list --count $identical..$nonidentical) |
6ae6a233 DA |
702 | if test "$extras" -ne 0 |
703 | then | |
933cfeb9 DW |
704 | # we need to preserve history along the other branch |
705 | copycommit=1 | |
706 | fi | |
707 | fi | |
6ae6a233 DA |
708 | if test -n "$identical" && test -z "$copycommit" |
709 | then | |
96db2c04 AP |
710 | echo $identical |
711 | else | |
6ae6a233 | 712 | copy_commit "$rev" "$tree" "$p" || exit $? |
96db2c04 | 713 | fi |
d6912658 AP |
714 | } |
715 | ||
5cdae0f6 | 716 | # Usage: ensure_clean |
d7fd792e | 717 | ensure_clean () { |
5cdae0f6 | 718 | assert test $# = 0 |
6ae6a233 DA |
719 | if ! git diff-index HEAD --exit-code --quiet 2>&1 |
720 | then | |
eb7b590c AP |
721 | die "Working tree has modifications. Cannot add." |
722 | fi | |
6ae6a233 DA |
723 | if ! git diff-index --cached HEAD --exit-code --quiet 2>&1 |
724 | then | |
eb7b590c AP |
725 | die "Index has modifications. Cannot add." |
726 | fi | |
13648af5 AP |
727 | } |
728 | ||
5cdae0f6 | 729 | # Usage: ensure_valid_ref_format REF |
d7fd792e | 730 | ensure_valid_ref_format () { |
5cdae0f6 | 731 | assert test $# = 1 |
1c3e0f00 | 732 | git check-ref-format "refs/heads/$1" || |
6ae6a233 | 733 | die "'$1' does not look like a ref" |
1c3e0f00 AB |
734 | } |
735 | ||
e9525a8a | 736 | # Usage: process_split_commit REV PARENTS |
565e4b79 | 737 | process_split_commit () { |
e9525a8a | 738 | assert test $# = 2 |
565e4b79 SR |
739 | local rev="$1" |
740 | local parents="$2" | |
315a84f9 SR |
741 | |
742 | if test $indent -eq 0 | |
743 | then | |
744 | revcount=$(($revcount + 1)) | |
745 | else | |
746 | # processing commit without normal parent information; | |
747 | # fetch from repo | |
19ad68d9 | 748 | parents=$(git rev-parse "$rev^@") |
315a84f9 SR |
749 | extracount=$(($extracount + 1)) |
750 | fi | |
751 | ||
752 | progress "$revcount/$revmax ($createcount) [$extracount]" | |
753 | ||
565e4b79 | 754 | debug "Processing commit: $rev" |
e9525a8a | 755 | local indent=$(($indent + 1)) |
d2f0f819 | 756 | exists=$(cache_get "$rev") || exit $? |
565e4b79 SR |
757 | if test -n "$exists" |
758 | then | |
e9525a8a | 759 | debug "prior: $exists" |
565e4b79 SR |
760 | return |
761 | fi | |
762 | createcount=$(($createcount + 1)) | |
e9525a8a | 763 | debug "parents: $parents" |
3ce8888f | 764 | check_parents $parents |
d2f0f819 | 765 | newparents=$(cache_get $parents) || exit $? |
e9525a8a | 766 | debug "newparents: $newparents" |
565e4b79 | 767 | |
d2f0f819 | 768 | tree=$(subtree_for_commit "$rev" "$dir") || exit $? |
e9525a8a | 769 | debug "tree is: $tree" |
565e4b79 | 770 | |
565e4b79 SR |
771 | # ugly. is there no better way to tell if this is a subtree |
772 | # vs. a mainline commit? Does it matter? | |
773 | if test -z "$tree" | |
774 | then | |
775 | set_notree "$rev" | |
776 | if test -n "$newparents" | |
777 | then | |
778 | cache_set "$rev" "$rev" | |
779 | fi | |
780 | return | |
781 | fi | |
782 | ||
783 | newrev=$(copy_or_skip "$rev" "$tree" "$newparents") || exit $? | |
e9525a8a | 784 | debug "newrev is: $newrev" |
565e4b79 SR |
785 | cache_set "$rev" "$newrev" |
786 | cache_set latest_new "$newrev" | |
787 | cache_set latest_old "$rev" | |
788 | } | |
789 | ||
5cdae0f6 LS |
790 | # Usage: cmd_add REV |
791 | # Or: cmd_add REPOSITORY REF | |
d7fd792e | 792 | cmd_add () { |
c00d1d11 | 793 | |
13648af5 | 794 | ensure_clean |
6ae6a233 DA |
795 | |
796 | if test $# -eq 1 | |
797 | then | |
798 | git rev-parse -q --verify "$1^{commit}" >/dev/null || | |
799 | die "'$1' does not refer to a commit" | |
800 | ||
801 | cmd_add_commit "$@" | |
802 | ||
803 | elif test $# -eq 2 | |
804 | then | |
805 | # Technically we could accept a refspec here but we're | |
806 | # just going to turn around and add FETCH_HEAD under the | |
807 | # specified directory. Allowing a refspec might be | |
808 | # misleading because we won't do anything with any other | |
809 | # branches fetched via the refspec. | |
810 | ensure_valid_ref_format "$2" | |
811 | ||
812 | cmd_add_repository "$@" | |
c00d1d11 | 813 | else |
a94f9110 | 814 | say >&2 "error: parameters were '$*'" |
6ae6a233 | 815 | die "Provide either a commit or a repository and commit." |
eb7b590c | 816 | fi |
c00d1d11 WW |
817 | } |
818 | ||
5cdae0f6 | 819 | # Usage: cmd_add_repository REPOSITORY REFSPEC |
d7fd792e | 820 | cmd_add_repository () { |
5cdae0f6 | 821 | assert test $# = 2 |
c00d1d11 WW |
822 | echo "git fetch" "$@" |
823 | repository=$1 | |
824 | refspec=$2 | |
825 | git fetch "$@" || exit $? | |
e4f8baa8 | 826 | cmd_add_commit FETCH_HEAD |
c00d1d11 WW |
827 | } |
828 | ||
5cdae0f6 | 829 | # Usage: cmd_add_commit REV |
d7fd792e | 830 | cmd_add_commit () { |
e4f8baa8 LS |
831 | # The rev has already been validated by cmd_add(), we just |
832 | # need to normalize it. | |
5cdae0f6 | 833 | assert test $# = 1 |
e4f8baa8 | 834 | rev=$(git rev-parse --verify "$1^{commit}") || exit $? |
6ae6a233 | 835 | |
eb7b590c | 836 | debug "Adding $dir as '$rev'..." |
cb655144 LS |
837 | if test -z "$arg_split_rejoin" |
838 | then | |
839 | # Only bother doing this if this is a genuine 'add', | |
840 | # not a synthetic 'add' from '--rejoin'. | |
841 | git read-tree --prefix="$dir" $rev || exit $? | |
842 | fi | |
227f7811 | 843 | git checkout -- "$dir" || exit $? |
eb7b590c | 844 | tree=$(git write-tree) || exit $? |
6ae6a233 | 845 | |
eb7b590c | 846 | headrev=$(git rev-parse HEAD) || exit $? |
6ae6a233 DA |
847 | if test -n "$headrev" && test "$headrev" != "$rev" |
848 | then | |
eb7b590c AP |
849 | headp="-p $headrev" |
850 | else | |
851 | headp= | |
852 | fi | |
6ae6a233 | 853 | |
e2b11e42 | 854 | if test -n "$arg_addmerge_squash" |
6ae6a233 | 855 | then |
d713e2d8 | 856 | rev=$(new_squash_commit "" "" "$rev") || exit $? |
2da0969a | 857 | commit=$(add_squashed_msg "$rev" "$dir" | |
6ae6a233 | 858 | git commit-tree "$tree" $headp -p "$rev") || exit $? |
d713e2d8 | 859 | else |
d2f0f819 | 860 | revp=$(peel_committish "$rev") || exit $? |
6ae6a233 DA |
861 | commit=$(add_msg "$dir" $headrev "$rev" | |
862 | git commit-tree "$tree" $headp -p "$revp") || exit $? | |
d713e2d8 | 863 | fi |
eb7b590c | 864 | git reset "$commit" || exit $? |
6ae6a233 | 865 | |
6d43585a | 866 | say >&2 "Added dir '$dir'" |
eb7b590c AP |
867 | } |
868 | ||
5cdae0f6 | 869 | # Usage: cmd_split [REV] |
d7fd792e | 870 | cmd_split () { |
e4f8baa8 LS |
871 | if test $# -eq 0 |
872 | then | |
873 | rev=$(git rev-parse HEAD) | |
874 | elif test $# -eq 1 | |
875 | then | |
876 | rev=$(git rev-parse -q --verify "$1^{commit}") || | |
877 | die "'$1' does not refer to a commit" | |
878 | else | |
879 | die "You must provide exactly one revision. Got: '$*'" | |
880 | fi | |
881 | ||
cb655144 LS |
882 | if test -n "$arg_split_rejoin" |
883 | then | |
884 | ensure_clean | |
885 | fi | |
886 | ||
0ca71b37 AP |
887 | debug "Splitting $dir..." |
888 | cache_setup || exit $? | |
6ae6a233 | 889 | |
e2b11e42 | 890 | if test -n "$arg_split_onto" |
6ae6a233 | 891 | then |
e2b11e42 LS |
892 | debug "Reading history for --onto=$arg_split_onto..." |
893 | git rev-list $arg_split_onto | | |
6ae6a233 DA |
894 | while read rev |
895 | do | |
33ff583a AP |
896 | # the 'onto' history is already just the subdir, so |
897 | # any parent we find there can be used verbatim | |
e9525a8a | 898 | debug "cache: $rev" |
6ae6a233 | 899 | cache_set "$rev" "$rev" |
d2f0f819 | 900 | done || exit $? |
33ff583a | 901 | fi |
6ae6a233 | 902 | |
e4f8baa8 | 903 | unrevs="$(find_existing_splits "$dir" "$rev")" || exit $? |
6ae6a233 | 904 | |
1f73862f AP |
905 | # We can't restrict rev-list to only $dir here, because some of our |
906 | # parents have the $dir contents the root, and those won't match. | |
907 | # (and rev-list --follow doesn't seem to solve this) | |
e4f8baa8 | 908 | grl='git rev-list --topo-order --reverse --parents $rev $unrevs' |
942dce55 AP |
909 | revmax=$(eval "$grl" | wc -l) |
910 | revcount=0 | |
911 | createcount=0 | |
315a84f9 | 912 | extracount=0 |
942dce55 | 913 | eval "$grl" | |
6ae6a233 DA |
914 | while read rev parents |
915 | do | |
e9525a8a | 916 | process_split_commit "$rev" "$parents" |
2573354e | 917 | done || exit $? |
6ae6a233 | 918 | |
d2f0f819 | 919 | latest_new=$(cache_get latest_new) || exit $? |
6ae6a233 DA |
920 | if test -z "$latest_new" |
921 | then | |
e25a6bf8 AP |
922 | die "No new revisions were found" |
923 | fi | |
6ae6a233 | 924 | |
e2b11e42 | 925 | if test -n "$arg_split_rejoin" |
6ae6a233 | 926 | then |
b77172f8 | 927 | debug "Merging split branch into HEAD..." |
d2f0f819 | 928 | latest_old=$(cache_get latest_old) || exit $? |
cb655144 LS |
929 | arg_addmerge_message="$(rejoin_msg "$dir" "$latest_old" "$latest_new")" || exit $? |
930 | if test -z "$(find_latest_squash "$dir")" | |
931 | then | |
932 | cmd_add "$latest_new" >&2 || exit $? | |
933 | else | |
934 | cmd_merge "$latest_new" >&2 || exit $? | |
935 | fi | |
6ae6a233 | 936 | fi |
e2b11e42 | 937 | if test -n "$arg_split_branch" |
6ae6a233 | 938 | then |
e2b11e42 | 939 | if rev_exists "refs/heads/$arg_split_branch" |
6ae6a233 | 940 | then |
e2b11e42 | 941 | if ! git merge-base --is-ancestor "$arg_split_branch" "$latest_new" |
6ae6a233 | 942 | then |
e2b11e42 | 943 | die "Branch '$arg_split_branch' is not an ancestor of commit '$latest_new'." |
0a562948 JS |
944 | fi |
945 | action='Updated' | |
946 | else | |
947 | action='Created' | |
948 | fi | |
6ae6a233 | 949 | git update-ref -m 'subtree split' \ |
e2b11e42 LS |
950 | "refs/heads/$arg_split_branch" "$latest_new" || exit $? |
951 | say >&2 "$action branch '$arg_split_branch'" | |
43a39512 | 952 | fi |
6ae6a233 | 953 | echo "$latest_new" |
0ca71b37 AP |
954 | exit 0 |
955 | } | |
956 | ||
5cdae0f6 | 957 | # Usage: cmd_merge REV |
d7fd792e | 958 | cmd_merge () { |
e4f8baa8 LS |
959 | test $# -eq 1 || |
960 | die "You must provide exactly one revision. Got: '$*'" | |
961 | rev=$(git rev-parse -q --verify "$1^{commit}") || | |
962 | die "'$1' does not refer to a commit" | |
13648af5 | 963 | ensure_clean |
6ae6a233 | 964 | |
e2b11e42 | 965 | if test -n "$arg_addmerge_squash" |
6ae6a233 | 966 | then |
d2f0f819 | 967 | first_split="$(find_latest_squash "$dir")" || exit $? |
6ae6a233 DA |
968 | if test -z "$first_split" |
969 | then | |
1cc2cfff AP |
970 | die "Can't squash-merge: '$dir' was never added." |
971 | fi | |
972 | set $first_split | |
973 | old=$1 | |
974 | sub=$2 | |
6ae6a233 DA |
975 | if test "$sub" = "$rev" |
976 | then | |
6d43585a | 977 | say >&2 "Subtree is already at commit $rev." |
eb4fb910 AP |
978 | exit 0 |
979 | fi | |
1cc2cfff AP |
980 | new=$(new_squash_commit "$old" "$sub" "$rev") || exit $? |
981 | debug "New squash commit: $new" | |
982 | rev="$new" | |
983 | fi | |
448e71e2 | 984 | |
e2b11e42 | 985 | if test -n "$arg_addmerge_message" |
6ae6a233 | 986 | then |
9158a356 | 987 | git merge --no-ff -Xsubtree="$arg_prefix" \ |
e2b11e42 | 988 | --message="$arg_addmerge_message" "$rev" |
349a70d5 | 989 | else |
9158a356 | 990 | git merge --no-ff -Xsubtree="$arg_prefix" $rev |
349a70d5 | 991 | fi |
13648af5 AP |
992 | } |
993 | ||
5cdae0f6 | 994 | # Usage: cmd_pull REPOSITORY REMOTEREF |
d7fd792e | 995 | cmd_pull () { |
6ae6a233 DA |
996 | if test $# -ne 2 |
997 | then | |
998 | die "You must provide <repository> <ref>" | |
1c3e0f00 | 999 | fi |
13648af5 | 1000 | ensure_clean |
1c3e0f00 | 1001 | ensure_valid_ref_format "$2" |
e31d1e2f | 1002 | git fetch "$@" || exit $? |
e4f8baa8 | 1003 | cmd_merge FETCH_HEAD |
c00d1d11 WW |
1004 | } |
1005 | ||
49470cd4 | 1006 | # Usage: cmd_push REPOSITORY [+][LOCALREV:]REMOTEREF |
d7fd792e | 1007 | cmd_push () { |
6ae6a233 DA |
1008 | if test $# -ne 2 |
1009 | then | |
49470cd4 | 1010 | die "You must provide <repository> <refspec>" |
c00d1d11 | 1011 | fi |
6ae6a233 DA |
1012 | if test -e "$dir" |
1013 | then | |
1014 | repository=$1 | |
49470cd4 LS |
1015 | refspec=${2#+} |
1016 | remoteref=${refspec#*:} | |
1017 | if test "$remoteref" = "$refspec" | |
1018 | then | |
1019 | localrevname_presplit=HEAD | |
1020 | else | |
1021 | localrevname_presplit=${refspec%%:*} | |
1022 | fi | |
1023 | ensure_valid_ref_format "$remoteref" | |
1024 | localrev_presplit=$(git rev-parse -q --verify "$localrevname_presplit^{commit}") || | |
1025 | die "'$localrevname_presplit' does not refer to a commit" | |
1026 | ||
6ae6a233 | 1027 | echo "git push using: " "$repository" "$refspec" |
49470cd4 LS |
1028 | localrev=$(cmd_split "$localrev_presplit") || die |
1029 | git push "$repository" "$localrev":"refs/heads/$remoteref" | |
c00d1d11 | 1030 | else |
6ae6a233 | 1031 | die "'$dir' must already exist. Try 'git subtree add'." |
c00d1d11 | 1032 | fi |
0ca71b37 AP |
1033 | } |
1034 | ||
5a356977 | 1035 | main "$@" |