]> asedeno.scripts.mit.edu Git - git.git/blob - git-rebase.sh
f2742aa054d2080a4a554ade3916c3bfa110af67
[git.git] / git-rebase.sh
1 #!/bin/sh
2 #
3 # Copyright (c) 2005 Junio C Hamano.
4 #
5
6 USAGE='[--interactive | -i] [-v] [--onto <newbase>] <upstream> [<branch>]'
7 LONG_USAGE='git-rebase replaces <branch> with a new branch of the
8 same name.  When the --onto option is provided the new branch starts
9 out with a HEAD equal to <newbase>, otherwise it is equal to <upstream>
10 It then attempts to create a new commit for each commit from the original
11 <branch> that does not exist in the <upstream> branch.
12
13 It is possible that a merge failure will prevent this process from being
14 completely automatic.  You will have to resolve any such merge failure
15 and run git rebase --continue.  Another option is to bypass the commit
16 that caused the merge failure with git rebase --skip.  To restore the
17 original <branch> and remove the .git/rebase-apply working files, use the
18 command git rebase --abort instead.
19
20 Note that if <branch> is not specified on the command line, the
21 currently checked out branch is used.
22
23 Example:       git-rebase master~1 topic
24
25         A---B---C topic                   A'\''--B'\''--C'\'' topic
26        /                   -->           /
27   D---E---F---G master          D---E---F---G master
28 '
29
30 SUBDIRECTORY_OK=Yes
31 OPTIONS_SPEC=
32 . git-sh-setup
33 set_reflog_action rebase
34 require_work_tree
35 cd_to_toplevel
36
37 OK_TO_SKIP_PRE_REBASE=
38 RESOLVEMSG="
39 When you have resolved this problem run \"git rebase --continue\".
40 If you would prefer to skip this patch, instead run \"git rebase --skip\".
41 To restore the original branch and stop rebasing run \"git rebase --abort\".
42 "
43 unset newbase
44 strategy=recursive
45 do_merge=
46 dotest="$GIT_DIR"/rebase-merge
47 prec=4
48 verbose=
49 git_am_opt=
50
51 continue_merge () {
52         test -n "$prev_head" || die "prev_head must be defined"
53         test -d "$dotest" || die "$dotest directory does not exist"
54
55         unmerged=$(git ls-files -u)
56         if test -n "$unmerged"
57         then
58                 echo "You still have unmerged paths in your index"
59                 echo "did you forget to use git add?"
60                 die "$RESOLVEMSG"
61         fi
62
63         cmt=`cat "$dotest/current"`
64         if ! git diff-index --quiet --ignore-submodules HEAD --
65         then
66                 if ! git commit --no-verify -C "$cmt"
67                 then
68                         echo "Commit failed, please do not call \"git commit\""
69                         echo "directly, but instead do one of the following: "
70                         die "$RESOLVEMSG"
71                 fi
72                 printf "Committed: %0${prec}d " $msgnum
73         else
74                 printf "Already applied: %0${prec}d " $msgnum
75         fi
76         git rev-list --pretty=oneline -1 "$cmt" | sed -e 's/^[^ ]* //'
77
78         prev_head=`git rev-parse HEAD^0`
79         # save the resulting commit so we can read-tree on it later
80         echo "$prev_head" > "$dotest/prev_head"
81
82         # onto the next patch:
83         msgnum=$(($msgnum + 1))
84         echo "$msgnum" >"$dotest/msgnum"
85 }
86
87 call_merge () {
88         cmt="$(cat "$dotest/cmt.$1")"
89         echo "$cmt" > "$dotest/current"
90         hd=$(git rev-parse --verify HEAD)
91         cmt_name=$(git symbolic-ref HEAD 2> /dev/null || echo HEAD)
92         msgnum=$(cat "$dotest/msgnum")
93         end=$(cat "$dotest/end")
94         eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"'
95         eval GITHEAD_$hd='$(cat "$dotest/onto_name")'
96         export GITHEAD_$cmt GITHEAD_$hd
97         git-merge-$strategy "$cmt^" -- "$hd" "$cmt"
98         rv=$?
99         case "$rv" in
100         0)
101                 unset GITHEAD_$cmt GITHEAD_$hd
102                 return
103                 ;;
104         1)
105                 git rerere
106                 die "$RESOLVEMSG"
107                 ;;
108         2)
109                 echo "Strategy: $rv $strategy failed, try another" 1>&2
110                 die "$RESOLVEMSG"
111                 ;;
112         *)
113                 die "Unknown exit code ($rv) from command:" \
114                         "git-merge-$strategy $cmt^ -- HEAD $cmt"
115                 ;;
116         esac
117 }
118
119 move_to_original_branch () {
120         test -z "$head_name" &&
121                 head_name="$(cat "$dotest"/head-name)" &&
122                 onto="$(cat "$dotest"/onto)" &&
123                 orig_head="$(cat "$dotest"/orig-head)"
124         case "$head_name" in
125         refs/*)
126                 message="rebase finished: $head_name onto $onto"
127                 git update-ref -m "$message" \
128                         $head_name $(git rev-parse HEAD) $orig_head &&
129                 git symbolic-ref HEAD $head_name ||
130                 die "Could not move back to $head_name"
131                 ;;
132         esac
133 }
134
135 finish_rb_merge () {
136         move_to_original_branch
137         rm -r "$dotest"
138         echo "All done."
139 }
140
141 is_interactive () {
142         test -f "$dotest"/interactive ||
143         while :; do case $#,"$1" in 0,|*,-i|*,--interactive) break ;; esac
144                 shift
145         done && test -n "$1"
146 }
147
148 run_pre_rebase_hook () {
149         if test -z "$OK_TO_SKIP_PRE_REBASE" &&
150            test -x "$GIT_DIR/hooks/pre-rebase"
151         then
152                 "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} || {
153                         echo >&2 "The pre-rebase hook refused to rebase."
154                         exit 1
155                 }
156         fi
157 }
158
159 test -f "$GIT_DIR"/rebase-apply/applying &&
160         die 'It looks like git-am is in progress. Cannot rebase.'
161
162 is_interactive "$@" && exec git-rebase--interactive "$@"
163
164 if test $# -eq 0
165 then
166         test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply || usage
167         test -d "$dotest" -o -f "$GIT_DIR"/rebase-apply/rebasing &&
168                 die 'A rebase is in progress, try --continue, --skip or --abort.'
169         die "No arguments given and $GIT_DIR/rebase-apply already exists."
170 fi
171
172 while test $# != 0
173 do
174         case "$1" in
175         --no-verify)
176                 OK_TO_SKIP_PRE_REBASE=yes
177                 ;;
178         --continue)
179                 test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply ||
180                         die "No rebase in progress?"
181
182                 git diff-files --quiet --ignore-submodules || {
183                         echo "You must edit all merge conflicts and then"
184                         echo "mark them as resolved using git add"
185                         exit 1
186                 }
187                 if test -d "$dotest"
188                 then
189                         prev_head=$(cat "$dotest/prev_head")
190                         end=$(cat "$dotest/end")
191                         msgnum=$(cat "$dotest/msgnum")
192                         onto=$(cat "$dotest/onto")
193                         continue_merge
194                         while test "$msgnum" -le "$end"
195                         do
196                                 call_merge "$msgnum"
197                                 continue_merge
198                         done
199                         finish_rb_merge
200                         exit
201                 fi
202                 head_name=$(cat "$GIT_DIR"/rebase-apply/head-name) &&
203                 onto=$(cat "$GIT_DIR"/rebase-apply/onto) &&
204                 orig_head=$(cat "$GIT_DIR"/rebase-apply/orig-head) &&
205                 git am --resolved --3way --resolvemsg="$RESOLVEMSG" &&
206                 move_to_original_branch
207                 exit
208                 ;;
209         --skip)
210                 test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply ||
211                         die "No rebase in progress?"
212
213                 git reset --hard HEAD || exit $?
214                 if test -d "$dotest"
215                 then
216                         git rerere clear
217                         prev_head=$(cat "$dotest/prev_head")
218                         end=$(cat "$dotest/end")
219                         msgnum=$(cat "$dotest/msgnum")
220                         msgnum=$(($msgnum + 1))
221                         onto=$(cat "$dotest/onto")
222                         while test "$msgnum" -le "$end"
223                         do
224                                 call_merge "$msgnum"
225                                 continue_merge
226                         done
227                         finish_rb_merge
228                         exit
229                 fi
230                 head_name=$(cat "$GIT_DIR"/rebase-apply/head-name) &&
231                 onto=$(cat "$GIT_DIR"/rebase-apply/onto) &&
232                 orig_head=$(cat "$GIT_DIR"/rebase-apply/orig-head) &&
233                 git am -3 --skip --resolvemsg="$RESOLVEMSG" &&
234                 move_to_original_branch
235                 exit
236                 ;;
237         --abort)
238                 test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply ||
239                         die "No rebase in progress?"
240
241                 git rerere clear
242                 if test -d "$dotest"
243                 then
244                         move_to_original_branch
245                 else
246                         dotest="$GIT_DIR"/rebase-apply
247                         move_to_original_branch
248                 fi
249                 git reset --hard $(cat "$dotest/orig-head")
250                 rm -r "$dotest"
251                 exit
252                 ;;
253         --onto)
254                 test 2 -le "$#" || usage
255                 newbase="$2"
256                 shift
257                 ;;
258         -M|-m|--m|--me|--mer|--merg|--merge)
259                 do_merge=t
260                 ;;
261         -s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
262                 --strateg=*|--strategy=*|\
263         -s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
264                 case "$#,$1" in
265                 *,*=*)
266                         strategy=`expr "z$1" : 'z-[^=]*=\(.*\)'` ;;
267                 1,*)
268                         usage ;;
269                 *)
270                         strategy="$2"
271                         shift ;;
272                 esac
273                 do_merge=t
274                 ;;
275         -v|--verbose)
276                 verbose=t
277                 ;;
278         --whitespace=*)
279                 git_am_opt="$git_am_opt $1"
280                 ;;
281         -C*)
282                 git_am_opt="$git_am_opt $1"
283                 ;;
284         -*)
285                 usage
286                 ;;
287         *)
288                 break
289                 ;;
290         esac
291         shift
292 done
293
294 # Make sure we do not have $GIT_DIR/rebase-apply
295 if test -z "$do_merge"
296 then
297         if mkdir "$GIT_DIR"/rebase-apply 2>/dev/null
298         then
299                 rmdir "$GIT_DIR"/rebase-apply
300         else
301                 echo >&2 '
302 It seems that I cannot create a rebase-apply directory, and
303 I wonder if you are in the middle of patch application or another
304 rebase.  If that is not the case, please
305         rm -fr '"$GIT_DIR"'/rebase-apply
306 and run me again.  I am stopping in case you still have something
307 valuable there.'
308                 exit 1
309         fi
310 else
311         if test -d "$dotest"
312         then
313                 die "previous rebase directory $dotest still exists." \
314                         'Try git rebase (--continue | --abort | --skip)'
315         fi
316 fi
317
318 # The tree must be really really clean.
319 git update-index --ignore-submodules --refresh || exit
320 diff=$(git diff-index --cached --name-status -r --ignore-submodules HEAD --)
321 case "$diff" in
322 ?*)     echo "cannot rebase: your index is not up-to-date"
323         echo "$diff"
324         exit 1
325         ;;
326 esac
327
328 # The upstream head must be given.  Make sure it is valid.
329 upstream_name="$1"
330 upstream=`git rev-parse --verify "${upstream_name}^0"` ||
331     die "invalid upstream $upstream_name"
332
333 # Make sure the branch to rebase onto is valid.
334 onto_name=${newbase-"$upstream_name"}
335 onto=$(git rev-parse --verify "${onto_name}^0") || exit
336
337 # If a hook exists, give it a chance to interrupt
338 run_pre_rebase_hook ${1+"$@"}
339
340 # If the branch to rebase is given, that is the branch we will rebase
341 # $branch_name -- branch being rebased, or HEAD (already detached)
342 # $orig_head -- commit object name of tip of the branch before rebasing
343 # $head_name -- refs/heads/<that-branch> or "detached HEAD"
344 switch_to=
345 case "$#" in
346 2)
347         # Is it "rebase other $branchname" or "rebase other $commit"?
348         branch_name="$2"
349         switch_to="$2"
350
351         if git show-ref --verify --quiet -- "refs/heads/$2" &&
352            branch=$(git rev-parse --verify "refs/heads/$2" 2>/dev/null)
353         then
354                 head_name="refs/heads/$2"
355         elif branch=$(git rev-parse --verify "$2" 2>/dev/null)
356         then
357                 head_name="detached HEAD"
358         else
359                 usage
360         fi
361         ;;
362 *)
363         # Do not need to switch branches, we are already on it.
364         if branch_name=`git symbolic-ref -q HEAD`
365         then
366                 head_name=$branch_name
367                 branch_name=`expr "z$branch_name" : 'zrefs/heads/\(.*\)'`
368         else
369                 head_name="detached HEAD"
370                 branch_name=HEAD ;# detached
371         fi
372         branch=$(git rev-parse --verify "${branch_name}^0") || exit
373         ;;
374 esac
375 orig_head=$branch
376
377 # Now we are rebasing commits $upstream..$branch on top of $onto
378
379 # Check if we are already based on $onto with linear history,
380 # but this should be done only when upstream and onto are the same.
381 mb=$(git merge-base "$onto" "$branch")
382 if test "$upstream" = "$onto" && test "$mb" = "$onto" &&
383         # linear history?
384         ! (git rev-list --parents "$onto".."$branch" | grep " .* ") > /dev/null
385 then
386         # Lazily switch to the target branch if needed...
387         test -z "$switch_to" || git checkout "$switch_to"
388         echo >&2 "Current branch $branch_name is up to date."
389         exit 0
390 fi
391
392 if test -n "$verbose"
393 then
394         echo "Changes from $mb to $onto:"
395         # We want color (if set), but no pager
396         GIT_PAGER='' git diff --stat --summary "$mb" "$onto"
397 fi
398
399 # Detach HEAD and reset the tree
400 echo "First, rewinding head to replay your work on top of it..."
401 git checkout -q "$onto^0" || die "could not detach HEAD"
402 git update-ref ORIG_HEAD $branch
403
404 # If the $onto is a proper descendant of the tip of the branch, then
405 # we just fast forwarded.
406 if test "$mb" = "$branch"
407 then
408         echo >&2 "Fast-forwarded $branch_name to $onto_name."
409         move_to_original_branch
410         exit 0
411 fi
412
413 if test -z "$do_merge"
414 then
415         git format-patch -k --stdout --full-index --ignore-if-in-upstream \
416                 "$upstream..$orig_head" |
417         git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" &&
418         move_to_original_branch
419         ret=$?
420         test 0 != $ret -a -d "$GIT_DIR"/rebase-apply &&
421                 echo $head_name > "$GIT_DIR"/rebase-apply/head-name &&
422                 echo $onto > "$GIT_DIR"/rebase-apply/onto &&
423                 echo $orig_head > "$GIT_DIR"/rebase-apply/orig-head
424         exit $ret
425 fi
426
427 # start doing a rebase with git-merge
428 # this is rename-aware if the recursive (default) strategy is used
429
430 mkdir -p "$dotest"
431 echo "$onto" > "$dotest/onto"
432 echo "$onto_name" > "$dotest/onto_name"
433 prev_head=$orig_head
434 echo "$prev_head" > "$dotest/prev_head"
435 echo "$orig_head" > "$dotest/orig-head"
436 echo "$head_name" > "$dotest/head-name"
437
438 msgnum=0
439 for cmt in `git rev-list --reverse --no-merges "$upstream..$orig_head"`
440 do
441         msgnum=$(($msgnum + 1))
442         echo "$cmt" > "$dotest/cmt.$msgnum"
443 done
444
445 echo 1 >"$dotest/msgnum"
446 echo $msgnum >"$dotest/end"
447
448 end=$msgnum
449 msgnum=1
450
451 while test "$msgnum" -le "$end"
452 do
453         call_merge "$msgnum"
454         continue_merge
455 done
456
457 finish_rb_merge