]>
Commit | Line | Data |
---|---|---|
0ca71b37 AP |
1 | #!/bin/bash |
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 | # |
96db2c04 AP |
7 | if [ $# -eq 0 ]; then |
8 | set -- -h | |
9 | fi | |
0ca71b37 | 10 | OPTS_SPEC="\ |
f4f29557 | 11 | git subtree add --prefix=<prefix> <commit> |
13648af5 AP |
12 | git subtree merge --prefix=<prefix> <commit> |
13 | git subtree pull --prefix=<prefix> <repository> <refspec...> | |
c00d1d11 | 14 | git subtree push --prefix=<prefix> <repository> <refspec...> |
f4f29557 | 15 | git subtree split --prefix=<prefix> <commit...> |
0ca71b37 | 16 | -- |
96db2c04 AP |
17 | h,help show the help |
18 | q quiet | |
942dce55 | 19 | d show debug messages |
6e25f79f | 20 | P,prefix= the name of the subdir to split out |
2da0969a | 21 | m,message= use the given message as the commit message for the merge commit |
13648af5 | 22 | options for 'split' |
d0eb1b14 | 23 | annotate= add a prefix to commit message of new commits |
43a39512 AP |
24 | b,branch= create a new branch from the split subtree |
25 | ignore-joins ignore prior --rejoin commits | |
96db2c04 AP |
26 | onto= try connecting new tree to an existing one |
27 | rejoin merge the new branch back into HEAD | |
c00d1d11 | 28 | options for 'add', 'merge', 'pull' and 'push' |
8e79043c | 29 | squash merge subtree changes as a single commit |
0ca71b37 AP |
30 | " |
31 | eval $(echo "$OPTS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?) | |
33aaa697 AP |
32 | PATH=$(git --exec-path):$PATH |
33 | . git-sh-setup | |
0ca71b37 AP |
34 | require_work_tree |
35 | ||
36 | quiet= | |
43a39512 | 37 | branch= |
942dce55 | 38 | debug= |
0ca71b37 | 39 | command= |
b77172f8 AP |
40 | onto= |
41 | rejoin= | |
96db2c04 | 42 | ignore_joins= |
d0eb1b14 | 43 | annotate= |
8e79043c | 44 | squash= |
2da0969a | 45 | message= |
0ca71b37 AP |
46 | |
47 | debug() | |
942dce55 AP |
48 | { |
49 | if [ -n "$debug" ]; then | |
50 | echo "$@" >&2 | |
51 | fi | |
52 | } | |
53 | ||
54 | say() | |
0ca71b37 AP |
55 | { |
56 | if [ -z "$quiet" ]; then | |
57 | echo "$@" >&2 | |
58 | fi | |
59 | } | |
60 | ||
2573354e AP |
61 | assert() |
62 | { | |
63 | if "$@"; then | |
64 | : | |
65 | else | |
66 | die "assertion failed: " "$@" | |
67 | fi | |
68 | } | |
69 | ||
70 | ||
0ca71b37 AP |
71 | #echo "Options: $*" |
72 | ||
73 | while [ $# -gt 0 ]; do | |
74 | opt="$1" | |
75 | shift | |
76 | case "$opt" in | |
77 | -q) quiet=1 ;; | |
942dce55 | 78 | -d) debug=1 ;; |
d0eb1b14 AP |
79 | --annotate) annotate="$1"; shift ;; |
80 | --no-annotate) annotate= ;; | |
43a39512 | 81 | -b) branch="$1"; shift ;; |
6e25f79f | 82 | -P) prefix="$1"; shift ;; |
2da0969a | 83 | -m) message="$1"; shift ;; |
9a8821ff | 84 | --no-prefix) prefix= ;; |
b77172f8 | 85 | --onto) onto="$1"; shift ;; |
96db2c04 | 86 | --no-onto) onto= ;; |
b77172f8 | 87 | --rejoin) rejoin=1 ;; |
96db2c04 AP |
88 | --no-rejoin) rejoin= ;; |
89 | --ignore-joins) ignore_joins=1 ;; | |
90 | --no-ignore-joins) ignore_joins= ;; | |
8e79043c AP |
91 | --squash) squash=1 ;; |
92 | --no-squash) squash= ;; | |
0ca71b37 | 93 | --) break ;; |
43a39512 | 94 | *) die "Unexpected option: $opt" ;; |
0ca71b37 AP |
95 | esac |
96 | done | |
97 | ||
98 | command="$1" | |
99 | shift | |
100 | case "$command" in | |
13648af5 | 101 | add|merge|pull) default= ;; |
c00d1d11 | 102 | split|push) default="--default HEAD" ;; |
0ca71b37 AP |
103 | *) die "Unknown command '$command'" ;; |
104 | esac | |
105 | ||
9a8821ff AP |
106 | if [ -z "$prefix" ]; then |
107 | die "You must provide the --prefix option." | |
108 | fi | |
ec54f0d9 | 109 | |
77ba3058 AP |
110 | case "$command" in |
111 | add) [ -e "$prefix" ] && | |
112 | die "prefix '$prefix' already exists." ;; | |
113 | *) [ -e "$prefix" ] || | |
114 | die "'$prefix' does not exist; use 'git subtree add'" ;; | |
115 | esac | |
ec54f0d9 | 116 | |
6f2012cd | 117 | dir="$(dirname "$prefix/.")" |
9a8821ff | 118 | |
c00d1d11 | 119 | if [ "$command" != "pull" -a "$command" != "add" -a "$command" != "push" ]; then |
13648af5 AP |
120 | revs=$(git rev-parse $default --revs-only "$@") || exit $? |
121 | dirs="$(git rev-parse --no-revs --no-flags "$@")" || exit $? | |
122 | if [ -n "$dirs" ]; then | |
123 | die "Error: Use --prefix instead of bare filenames." | |
124 | fi | |
0ca71b37 | 125 | fi |
0ca71b37 AP |
126 | |
127 | debug "command: {$command}" | |
128 | debug "quiet: {$quiet}" | |
129 | debug "revs: {$revs}" | |
130 | debug "dir: {$dir}" | |
13648af5 | 131 | debug "opts: {$*}" |
eb7b590c | 132 | debug |
0ca71b37 AP |
133 | |
134 | cache_setup() | |
135 | { | |
2573354e | 136 | cachedir="$GIT_DIR/subtree-cache/$$" |
0ca71b37 AP |
137 | rm -rf "$cachedir" || die "Can't delete old cachedir: $cachedir" |
138 | mkdir -p "$cachedir" || die "Can't create new cachedir: $cachedir" | |
139 | debug "Using cachedir: $cachedir" >&2 | |
0ca71b37 AP |
140 | } |
141 | ||
142 | cache_get() | |
143 | { | |
144 | for oldrev in $*; do | |
145 | if [ -r "$cachedir/$oldrev" ]; then | |
146 | read newrev <"$cachedir/$oldrev" | |
147 | echo $newrev | |
148 | fi | |
149 | done | |
150 | } | |
151 | ||
152 | cache_set() | |
153 | { | |
154 | oldrev="$1" | |
155 | newrev="$2" | |
b77172f8 AP |
156 | if [ "$oldrev" != "latest_old" \ |
157 | -a "$oldrev" != "latest_new" \ | |
158 | -a -e "$cachedir/$oldrev" ]; then | |
0ca71b37 AP |
159 | die "cache for $oldrev already exists!" |
160 | fi | |
161 | echo "$newrev" >"$cachedir/$oldrev" | |
162 | } | |
163 | ||
43a39512 AP |
164 | rev_exists() |
165 | { | |
166 | if git rev-parse "$1" >/dev/null 2>&1; then | |
167 | return 0 | |
168 | else | |
169 | return 1 | |
170 | fi | |
171 | } | |
172 | ||
0a562948 JS |
173 | rev_is_descendant_of_branch() |
174 | { | |
175 | newrev="$1" | |
176 | branch="$2" | |
177 | branch_hash=$(git rev-parse $branch) | |
6bd910a8 | 178 | match=$(git rev-list -1 $branch_hash ^$newrev) |
0a562948 | 179 | |
6bd910a8 | 180 | if [ -z "$match" ]; then |
0a562948 JS |
181 | return 0 |
182 | else | |
183 | return 1 | |
184 | fi | |
185 | } | |
186 | ||
b9de5353 AP |
187 | # if a commit doesn't have a parent, this might not work. But we only want |
188 | # to remove the parent from the rev-list, and since it doesn't exist, it won't | |
189 | # be there anyway, so do nothing in that case. | |
190 | try_remove_previous() | |
191 | { | |
43a39512 | 192 | if rev_exists "$1^"; then |
b9de5353 AP |
193 | echo "^$1^" |
194 | fi | |
195 | } | |
196 | ||
1cc2cfff AP |
197 | find_latest_squash() |
198 | { | |
d713e2d8 | 199 | debug "Looking for latest squash ($dir)..." |
1cc2cfff | 200 | dir="$1" |
d713e2d8 AP |
201 | sq= |
202 | main= | |
203 | sub= | |
6f2012cd | 204 | git log --grep="^git-subtree-dir: $dir/*\$" \ |
1cc2cfff AP |
205 | --pretty=format:'START %H%n%s%n%n%b%nEND%n' HEAD | |
206 | while read a b junk; do | |
d713e2d8 AP |
207 | debug "$a $b $junk" |
208 | debug "{{$sq/$main/$sub}}" | |
1cc2cfff AP |
209 | case "$a" in |
210 | START) sq="$b" ;; | |
211 | git-subtree-mainline:) main="$b" ;; | |
212 | git-subtree-split:) sub="$b" ;; | |
213 | END) | |
214 | if [ -n "$sub" ]; then | |
215 | if [ -n "$main" ]; then | |
216 | # a rejoin commit? | |
217 | # Pretend its sub was a squash. | |
218 | sq="$sub" | |
219 | fi | |
220 | debug "Squash found: $sq $sub" | |
221 | echo "$sq" "$sub" | |
222 | break | |
223 | fi | |
224 | sq= | |
225 | main= | |
226 | sub= | |
227 | ;; | |
228 | esac | |
229 | done | |
230 | } | |
231 | ||
8b4a77f2 AP |
232 | find_existing_splits() |
233 | { | |
234 | debug "Looking for prior splits..." | |
235 | dir="$1" | |
236 | revs="$2" | |
d713e2d8 AP |
237 | main= |
238 | sub= | |
6f2012cd | 239 | git log --grep="^git-subtree-dir: $dir/*\$" \ |
1a8c36dc | 240 | --pretty=format:'START %H%n%s%n%n%b%nEND%n' $revs | |
8b4a77f2 AP |
241 | while read a b junk; do |
242 | case "$a" in | |
2275f707 | 243 | START) sq="$b" ;; |
8b4a77f2 AP |
244 | git-subtree-mainline:) main="$b" ;; |
245 | git-subtree-split:) sub="$b" ;; | |
7ee9eef3 | 246 | END) |
2275f707 | 247 | debug " Main is: '$main'" |
1a8c36dc AP |
248 | if [ -z "$main" -a -n "$sub" ]; then |
249 | # squash commits refer to a subtree | |
2275f707 | 250 | debug " Squash: $sq from $sub" |
1a8c36dc AP |
251 | cache_set "$sq" "$sub" |
252 | fi | |
8b4a77f2 AP |
253 | if [ -n "$main" -a -n "$sub" ]; then |
254 | debug " Prior: $main -> $sub" | |
255 | cache_set $main $sub | |
b9de5353 AP |
256 | try_remove_previous "$main" |
257 | try_remove_previous "$sub" | |
8b4a77f2 | 258 | fi |
7ee9eef3 AP |
259 | main= |
260 | sub= | |
8b4a77f2 AP |
261 | ;; |
262 | esac | |
263 | done | |
264 | } | |
265 | ||
fd9500ee AP |
266 | copy_commit() |
267 | { | |
f96bc790 | 268 | # We're going to set some environment vars here, so |
fd9500ee | 269 | # do it in a subshell to get rid of them safely later |
a64f3a72 | 270 | debug copy_commit "{$1}" "{$2}" "{$3}" |
fd9500ee AP |
271 | git log -1 --pretty=format:'%an%n%ae%n%ad%n%cn%n%ce%n%cd%n%s%n%n%b' "$1" | |
272 | ( | |
273 | read GIT_AUTHOR_NAME | |
274 | read GIT_AUTHOR_EMAIL | |
275 | read GIT_AUTHOR_DATE | |
276 | read GIT_COMMITTER_NAME | |
277 | read GIT_COMMITTER_EMAIL | |
278 | read GIT_COMMITTER_DATE | |
b77172f8 AP |
279 | export GIT_AUTHOR_NAME \ |
280 | GIT_AUTHOR_EMAIL \ | |
281 | GIT_AUTHOR_DATE \ | |
282 | GIT_COMMITTER_NAME \ | |
283 | GIT_COMMITTER_EMAIL \ | |
284 | GIT_COMMITTER_DATE | |
d0eb1b14 | 285 | (echo -n "$annotate"; cat ) | |
fd9500ee AP |
286 | git commit-tree "$2" $3 # reads the rest of stdin |
287 | ) || die "Can't copy commit $1" | |
288 | } | |
289 | ||
eb7b590c AP |
290 | add_msg() |
291 | { | |
292 | dir="$1" | |
293 | latest_old="$2" | |
294 | latest_new="$3" | |
2da0969a JS |
295 | if [ -n "$message" ]; then |
296 | commit_message="$message" | |
297 | else | |
298 | commit_message="Add '$dir/' from commit '$latest_new'" | |
299 | fi | |
eb7b590c | 300 | cat <<-EOF |
2da0969a | 301 | $commit_message |
eb7b590c AP |
302 | |
303 | git-subtree-dir: $dir | |
304 | git-subtree-mainline: $latest_old | |
305 | git-subtree-split: $latest_new | |
306 | EOF | |
307 | } | |
308 | ||
2da0969a JS |
309 | add_squashed_msg() |
310 | { | |
311 | if [ -n "$message" ]; then | |
312 | echo "$message" | |
313 | else | |
314 | echo "Merge commit '$1' as '$2'" | |
315 | fi | |
316 | } | |
317 | ||
7ee9eef3 | 318 | rejoin_msg() |
b77172f8 AP |
319 | { |
320 | dir="$1" | |
321 | latest_old="$2" | |
322 | latest_new="$3" | |
2da0969a JS |
323 | if [ -n "$message" ]; then |
324 | commit_message="$message" | |
325 | else | |
326 | commit_message="Split '$dir/' into commit '$latest_new'" | |
327 | fi | |
b77172f8 | 328 | cat <<-EOF |
12629161 | 329 | $commit_message |
b77172f8 AP |
330 | |
331 | git-subtree-dir: $dir | |
8b4a77f2 AP |
332 | git-subtree-mainline: $latest_old |
333 | git-subtree-split: $latest_new | |
b77172f8 AP |
334 | EOF |
335 | } | |
336 | ||
1cc2cfff AP |
337 | squash_msg() |
338 | { | |
339 | dir="$1" | |
340 | oldsub="$2" | |
341 | newsub="$3" | |
1cc2cfff | 342 | newsub_short=$(git rev-parse --short "$newsub") |
1cc2cfff | 343 | |
d713e2d8 AP |
344 | if [ -n "$oldsub" ]; then |
345 | oldsub_short=$(git rev-parse --short "$oldsub") | |
346 | echo "Squashed '$dir/' changes from $oldsub_short..$newsub_short" | |
347 | echo | |
348 | git log --pretty=tformat:'%h %s' "$oldsub..$newsub" | |
349 | git log --pretty=tformat:'REVERT: %h %s' "$newsub..$oldsub" | |
350 | else | |
351 | echo "Squashed '$dir/' content from commit $newsub_short" | |
352 | fi | |
1cc2cfff | 353 | |
d713e2d8 AP |
354 | echo |
355 | echo "git-subtree-dir: $dir" | |
356 | echo "git-subtree-split: $newsub" | |
1cc2cfff AP |
357 | } |
358 | ||
210d0839 | 359 | toptree_for_commit() |
768d6d10 | 360 | { |
210d0839 AP |
361 | commit="$1" |
362 | git log -1 --pretty=format:'%T' "$commit" -- || exit $? | |
363 | } | |
364 | ||
365 | subtree_for_commit() | |
366 | { | |
367 | commit="$1" | |
368 | dir="$2" | |
369 | git ls-tree "$commit" -- "$dir" | | |
768d6d10 AP |
370 | while read mode type tree name; do |
371 | assert [ "$name" = "$dir" ] | |
8ac5eca1 | 372 | assert [ "$type" = "tree" ] |
768d6d10 AP |
373 | echo $tree |
374 | break | |
375 | done | |
376 | } | |
377 | ||
378 | tree_changed() | |
379 | { | |
380 | tree=$1 | |
381 | shift | |
382 | if [ $# -ne 1 ]; then | |
383 | return 0 # weird parents, consider it changed | |
384 | else | |
210d0839 | 385 | ptree=$(toptree_for_commit $1) |
768d6d10 AP |
386 | if [ "$ptree" != "$tree" ]; then |
387 | return 0 # changed | |
388 | else | |
389 | return 1 # not changed | |
390 | fi | |
391 | fi | |
392 | } | |
393 | ||
1cc2cfff AP |
394 | new_squash_commit() |
395 | { | |
396 | old="$1" | |
397 | oldsub="$2" | |
398 | newsub="$3" | |
399 | tree=$(toptree_for_commit $newsub) || exit $? | |
d713e2d8 AP |
400 | if [ -n "$old" ]; then |
401 | squash_msg "$dir" "$oldsub" "$newsub" | | |
402 | git commit-tree "$tree" -p "$old" || exit $? | |
403 | else | |
404 | squash_msg "$dir" "" "$newsub" | | |
405 | git commit-tree "$tree" || exit $? | |
406 | fi | |
1cc2cfff AP |
407 | } |
408 | ||
d6912658 AP |
409 | copy_or_skip() |
410 | { | |
411 | rev="$1" | |
412 | tree="$2" | |
413 | newparents="$3" | |
414 | assert [ -n "$tree" ] | |
415 | ||
96db2c04 | 416 | identical= |
49cf8228 | 417 | nonidentical= |
96db2c04 | 418 | p= |
a64f3a72 | 419 | gotparents= |
d6912658 AP |
420 | for parent in $newparents; do |
421 | ptree=$(toptree_for_commit $parent) || exit $? | |
a64f3a72 | 422 | [ -z "$ptree" ] && continue |
d6912658 | 423 | if [ "$ptree" = "$tree" ]; then |
96db2c04 AP |
424 | # an identical parent could be used in place of this rev. |
425 | identical="$parent" | |
49cf8228 AP |
426 | else |
427 | nonidentical="$parent" | |
96db2c04 | 428 | fi |
a64f3a72 AP |
429 | |
430 | # sometimes both old parents map to the same newparent; | |
431 | # eliminate duplicates | |
432 | is_new=1 | |
433 | for gp in $gotparents; do | |
434 | if [ "$gp" = "$parent" ]; then | |
435 | is_new= | |
436 | break | |
437 | fi | |
438 | done | |
439 | if [ -n "$is_new" ]; then | |
440 | gotparents="$gotparents $parent" | |
d6912658 AP |
441 | p="$p -p $parent" |
442 | fi | |
443 | done | |
444 | ||
795e730e | 445 | if [ -n "$identical" ]; then |
96db2c04 AP |
446 | echo $identical |
447 | else | |
448 | copy_commit $rev $tree "$p" || exit $? | |
449 | fi | |
d6912658 AP |
450 | } |
451 | ||
13648af5 | 452 | ensure_clean() |
eb7b590c | 453 | { |
c00d1d11 | 454 | if ! git diff-index HEAD --exit-code --quiet 2>&1; then |
eb7b590c AP |
455 | die "Working tree has modifications. Cannot add." |
456 | fi | |
c00d1d11 | 457 | if ! git diff-index --cached HEAD --exit-code --quiet 2>&1; then |
eb7b590c AP |
458 | die "Index has modifications. Cannot add." |
459 | fi | |
13648af5 AP |
460 | } |
461 | ||
462 | cmd_add() | |
463 | { | |
464 | if [ -e "$dir" ]; then | |
465 | die "'$dir' already exists. Cannot add." | |
466 | fi | |
c00d1d11 | 467 | |
13648af5 AP |
468 | ensure_clean |
469 | ||
c00d1d11 WW |
470 | if [ $# -eq 1 ]; then |
471 | "cmd_add_commit" "$@" | |
472 | elif [ $# -eq 2 ]; then | |
473 | "cmd_add_repository" "$@" | |
474 | else | |
475 | say "error: parameters were '$@'" | |
476 | die "Provide either a refspec or a repository and refspec." | |
eb7b590c | 477 | fi |
c00d1d11 WW |
478 | } |
479 | ||
480 | cmd_add_repository() | |
481 | { | |
482 | echo "git fetch" "$@" | |
483 | repository=$1 | |
484 | refspec=$2 | |
485 | git fetch "$@" || exit $? | |
486 | revs=FETCH_HEAD | |
487 | set -- $revs | |
488 | cmd_add_commit "$@" | |
489 | } | |
490 | ||
491 | cmd_add_commit() | |
492 | { | |
493 | revs=$(git rev-parse $default --revs-only "$@") || exit $? | |
494 | set -- $revs | |
eb7b590c AP |
495 | rev="$1" |
496 | ||
497 | debug "Adding $dir as '$rev'..." | |
498 | git read-tree --prefix="$dir" $rev || exit $? | |
227f7811 | 499 | git checkout -- "$dir" || exit $? |
eb7b590c AP |
500 | tree=$(git write-tree) || exit $? |
501 | ||
502 | headrev=$(git rev-parse HEAD) || exit $? | |
503 | if [ -n "$headrev" -a "$headrev" != "$rev" ]; then | |
504 | headp="-p $headrev" | |
505 | else | |
506 | headp= | |
507 | fi | |
d713e2d8 AP |
508 | |
509 | if [ -n "$squash" ]; then | |
510 | rev=$(new_squash_commit "" "" "$rev") || exit $? | |
2da0969a | 511 | commit=$(add_squashed_msg "$rev" "$dir" | |
d713e2d8 AP |
512 | git commit-tree $tree $headp -p "$rev") || exit $? |
513 | else | |
514 | commit=$(add_msg "$dir" "$headrev" "$rev" | | |
515 | git commit-tree $tree $headp -p "$rev") || exit $? | |
516 | fi | |
eb7b590c | 517 | git reset "$commit" || exit $? |
d713e2d8 AP |
518 | |
519 | say "Added dir '$dir'" | |
eb7b590c AP |
520 | } |
521 | ||
0ca71b37 AP |
522 | cmd_split() |
523 | { | |
524 | debug "Splitting $dir..." | |
525 | cache_setup || exit $? | |
526 | ||
33ff583a | 527 | if [ -n "$onto" ]; then |
847e8681 | 528 | debug "Reading history for --onto=$onto..." |
33ff583a AP |
529 | git rev-list $onto | |
530 | while read rev; do | |
531 | # the 'onto' history is already just the subdir, so | |
532 | # any parent we find there can be used verbatim | |
2c71b7c4 | 533 | debug " cache: $rev" |
33ff583a AP |
534 | cache_set $rev $rev |
535 | done | |
536 | fi | |
537 | ||
96db2c04 AP |
538 | if [ -n "$ignore_joins" ]; then |
539 | unrevs= | |
540 | else | |
541 | unrevs="$(find_existing_splits "$dir" "$revs")" | |
542 | fi | |
8b4a77f2 | 543 | |
1f73862f AP |
544 | # We can't restrict rev-list to only $dir here, because some of our |
545 | # parents have the $dir contents the root, and those won't match. | |
546 | # (and rev-list --follow doesn't seem to solve this) | |
942dce55 AP |
547 | grl='git rev-list --reverse --parents $revs $unrevs' |
548 | revmax=$(eval "$grl" | wc -l) | |
549 | revcount=0 | |
550 | createcount=0 | |
551 | eval "$grl" | | |
0ca71b37 | 552 | while read rev parents; do |
942dce55 | 553 | revcount=$(($revcount + 1)) |
e2d0a450 | 554 | say -n "$revcount/$revmax ($createcount)\r" |
2c71b7c4 AP |
555 | debug "Processing commit: $rev" |
556 | exists=$(cache_get $rev) | |
8b4a77f2 AP |
557 | if [ -n "$exists" ]; then |
558 | debug " prior: $exists" | |
559 | continue | |
560 | fi | |
942dce55 | 561 | createcount=$(($createcount + 1)) |
2c71b7c4 AP |
562 | debug " parents: $parents" |
563 | newparents=$(cache_get $parents) | |
564 | debug " newparents: $newparents" | |
8b4a77f2 | 565 | |
210d0839 | 566 | tree=$(subtree_for_commit $rev "$dir") |
768d6d10 | 567 | debug " tree is: $tree" |
7ee9eef3 AP |
568 | |
569 | # ugly. is there no better way to tell if this is a subtree | |
570 | # vs. a mainline commit? Does it matter? | |
da949cc5 JS |
571 | if [ -z $tree ]; then |
572 | cache_set $rev $rev | |
573 | continue | |
574 | fi | |
768d6d10 | 575 | |
d6912658 | 576 | newrev=$(copy_or_skip "$rev" "$tree" "$newparents") || exit $? |
768d6d10 AP |
577 | debug " newrev is: $newrev" |
578 | cache_set $rev $newrev | |
579 | cache_set latest_new $newrev | |
580 | cache_set latest_old $rev | |
2573354e | 581 | done || exit $? |
b77172f8 AP |
582 | latest_new=$(cache_get latest_new) |
583 | if [ -z "$latest_new" ]; then | |
e25a6bf8 AP |
584 | die "No new revisions were found" |
585 | fi | |
b77172f8 AP |
586 | |
587 | if [ -n "$rejoin" ]; then | |
588 | debug "Merging split branch into HEAD..." | |
589 | latest_old=$(cache_get latest_old) | |
590 | git merge -s ours \ | |
7ee9eef3 | 591 | -m "$(rejoin_msg $dir $latest_old $latest_new)" \ |
ea28d674 | 592 | $latest_new >&2 || exit $? |
b77172f8 | 593 | fi |
43a39512 | 594 | if [ -n "$branch" ]; then |
0a562948 JS |
595 | if rev_exists "refs/heads/$branch"; then |
596 | if ! rev_is_descendant_of_branch $latest_new $branch; then | |
597 | die "Branch '$branch' is not an ancestor of commit '$latest_new'." | |
598 | fi | |
599 | action='Updated' | |
600 | else | |
601 | action='Created' | |
602 | fi | |
603 | git update-ref -m 'subtree split' "refs/heads/$branch" $latest_new || exit $? | |
604 | say "$action branch '$branch'" | |
43a39512 | 605 | fi |
b77172f8 | 606 | echo $latest_new |
0ca71b37 AP |
607 | exit 0 |
608 | } | |
609 | ||
610 | cmd_merge() | |
611 | { | |
c00d1d11 | 612 | revs=$(git rev-parse $default --revs-only "$@") || exit $? |
13648af5 AP |
613 | ensure_clean |
614 | ||
615 | set -- $revs | |
616 | if [ $# -ne 1 ]; then | |
617 | die "You must provide exactly one revision. Got: '$revs'" | |
618 | fi | |
619 | rev="$1" | |
620 | ||
1cc2cfff AP |
621 | if [ -n "$squash" ]; then |
622 | first_split="$(find_latest_squash "$dir")" | |
623 | if [ -z "$first_split" ]; then | |
624 | die "Can't squash-merge: '$dir' was never added." | |
625 | fi | |
626 | set $first_split | |
627 | old=$1 | |
628 | sub=$2 | |
eb4fb910 AP |
629 | if [ "$sub" = "$rev" ]; then |
630 | say "Subtree is already at commit $rev." | |
631 | exit 0 | |
632 | fi | |
1cc2cfff AP |
633 | new=$(new_squash_commit "$old" "$sub" "$rev") || exit $? |
634 | debug "New squash commit: $new" | |
635 | rev="$new" | |
636 | fi | |
637 | ||
349a70d5 AP |
638 | if [ -n "$message" ]; then |
639 | git merge -s subtree --message="$message" $rev | |
640 | else | |
641 | git merge -s subtree $rev | |
642 | fi | |
13648af5 AP |
643 | } |
644 | ||
645 | cmd_pull() | |
646 | { | |
647 | ensure_clean | |
e31d1e2f AP |
648 | git fetch "$@" || exit $? |
649 | revs=FETCH_HEAD | |
c00d1d11 WW |
650 | set -- $revs |
651 | cmd_merge "$@" | |
652 | } | |
653 | ||
654 | cmd_push() | |
655 | { | |
656 | if [ $# -ne 2 ]; then | |
657 | die "You must provide <repository> <refspec>" | |
658 | fi | |
659 | if [ -e "$dir" ]; then | |
660 | repository=$1 | |
661 | refspec=$2 | |
662 | echo "git push using: " $repository $refspec | |
663 | git push $repository $(git subtree split --prefix=$prefix):refs/heads/$refspec | |
664 | else | |
665 | die "'$dir' must already exist. Try 'git subtree add'." | |
666 | fi | |
0ca71b37 AP |
667 | } |
668 | ||
13648af5 | 669 | "cmd_$command" "$@" |