update: improve sed expression
[git] / git-update.sh
1 #!/bin/sh
2 #
3 # Copyright (C) 2014 Felipe Contreras
4 # Copyright (C) 2005 Junio C Hamano
5 #
6 # Fetch upstream and update the current branch.
7
8 USAGE='[-n | --no-stat] [--[no-]commit] [--[no-]squash] [--[no-]ff] [--[no-]rebase|--rebase=preserve] [-s strategy]...'
9 LONG_USAGE='Fetch upstram and update the current branch.'
10 SUBDIRECTORY_OK=Yes
11 OPTIONS_SPEC=
12 . git-sh-setup
13 . git-sh-i18n
14 set_reflog_action "update${1+ $*}"
15 require_work_tree_exists
16 cd_to_toplevel
17
18
19 warn () {
20         printf >&2 'warning: %s\n' "$*"
21 }
22
23 die_conflict () {
24         git diff-index --cached --name-status -r --ignore-submodules HEAD --
25         die "$(gettext "Update is not possible because you have instaged files.")"
26 }
27
28 die_merge () {
29         die "$(gettext "You have not concluded your merge (MERGE_HEAD exists).")"
30 }
31
32 test -z "$(git ls-files -u)" || die_conflict
33 test -f "$GIT_DIR/MERGE_HEAD" && die_merge
34
35 bool_or_string_config () {
36         git config --bool "$1" 2>/dev/null || git config "$1"
37 }
38
39 strategy_args= diffstat= no_commit= squash= no_ff= ff_only=
40 log_arg= verbosity= progress= recurse_submodules= verify_signatures=
41 merge_args= edit= rebase_args=
42 curr_branch=$(git symbolic-ref -q HEAD)
43 curr_branch_short="${curr_branch#refs/heads/}"
44 mode=$(git config branch.${curr_branch_short}.updatemode)
45 if test -z "$mode"
46 then
47         mode=$(git config update.mode)
48 fi
49 case "$mode" in
50 merge|rebase|ff-only|'')
51         ;;
52 rebase-preserve)
53         mode="rebase"
54         rebase_args="--preserve-merges"
55         ;;
56 *)
57         echo "Invalid value for 'mode'"
58         usage
59         exit 1
60         ;;
61 esac
62 # compatibility with pull configuration
63 if test -z "$mode"
64 then
65         rebase=$(bool_or_string_config branch.$curr_branch_short.rebase)
66         if test -z "$rebase"
67         then
68                 rebase=$(bool_or_string_config pull.rebase)
69         fi
70 fi
71 test -z "$mode" && mode=ff-only
72 dry_run=
73 while :
74 do
75         case "$1" in
76         -q|--quiet)
77                 verbosity="$verbosity -q" ;;
78         -v|--verbose)
79                 verbosity="$verbosity -v" ;;
80         --progress)
81                 progress=--progress ;;
82         --no-progress)
83                 progress=--no-progress ;;
84         -n|--no-stat|--no-summary)
85                 diffstat=--no-stat ;;
86         --stat|--summary)
87                 diffstat=--stat ;;
88         --log|--no-log)
89                 log_arg=$1 ;;
90         -e|--edit)
91                 edit=--edit ;;
92         --no-edit)
93                 edit=--no-edit ;;
94         # TODO
95         --no-commit)
96                 no_commit=--no-commit ;;
97         --commit)
98                 no_commit=--commit ;;
99         --squash)
100                 squash=--squash ;;
101         --no-squash)
102                 squash=--no-squash ;;
103         --ff)
104                 no_ff=--ff ;;
105         --no-ff)
106                 no_ff=--no-ff ;;
107         --ff-only)
108                 ff_only=--ff-only ;;
109         -s=*|--strategy=*|-s|--strategy)
110                 case "$#,$1" in
111                 *,*=*)
112                         strategy=`expr "z$1" : 'z-[^=]*=\(.*\)'` ;;
113                 1,*)
114                         usage ;;
115                 *)
116                         strategy="$2"
117                         shift ;;
118                 esac
119                 strategy_args="${strategy_args}-s $strategy "
120                 ;;
121         -X*)
122                 case "$#,$1" in
123                 1,-X)
124                         usage ;;
125                 *,-X)
126                         xx="-X $(git rev-parse --sq-quote "$2")"
127                         shift ;;
128                 *,*)
129                         xx=$(git rev-parse --sq-quote "$1") ;;
130                 esac
131                 merge_args="$merge_args$xx "
132                 ;;
133         -r=*|--rebase=*)
134                 rebase="${1#*=}"
135                 ;;
136         -r|--rebase)
137                 mode=rebase
138                 ;;
139         -m|--merge)
140                 mode=merge
141                 ;;
142         --recurse-submodules)
143                 recurse_submodules=--recurse-submodules
144                 ;;
145         --recurse-submodules=*)
146                 recurse_submodules="$1"
147                 ;;
148         --no-recurse-submodules)
149                 recurse_submodules=--no-recurse-submodules
150                 ;;
151         --verify-signatures)
152                 verify_signatures=--verify-signatures
153                 ;;
154         --no-verify-signatures)
155                 verify_signatures=--no-verify-signatures
156                 ;;
157         --d|--dry-run)
158                 dry_run=--dry-run
159                 ;;
160         -h|--help-all)
161                 usage
162                 ;;
163         *)
164                 # Pass thru anything that may be meant for fetch.
165                 break
166                 ;;
167         esac
168         shift
169 done
170
171 if test -n "$rebase"
172 then
173         case "$rebase" in
174         true)
175                 mode="rebase"
176                 ;;
177         false)
178                 mode="merge"
179                 ;;
180         preserve)
181                 mode="rebase"
182                 rebase_args=--preserve-merges
183                 ;;
184         *)
185                 echo "Invalid value for --rebase, should be true, false, or preserve"
186                 usage
187                 exit 1
188                 ;;
189         esac
190 fi
191
192 test -z "$curr_branch" &&
193         die "$(gettext "You are not currently on a branch.")"
194
195 get_remote_branch () {
196         local ref
197
198         ref=$(git rev-parse --symbolic-full-name $1)
199
200         git config --get-regexp 'remote.*.fetch' | while read key value
201         do
202                 remote=${key#remote.}
203                 remote=${remote%.fetch}
204                 right=${value#*:}
205                 case $ref in
206                 $right)
207                         prefix=${right%\*}
208                         branch=${ref#$prefix}
209                         echo "remote=$remote branch=$branch"
210                         break
211                         ;;
212                 esac
213         done
214 }
215
216 case $# in
217 0)
218         branch=$(git config "branch.$curr_branch_short.merge")
219         remote=$(git config "branch.$curr_branch_short.remote")
220
221         test -z "$branch" && branch=$curr_branch
222         test -z "$remote" && remote="origin"
223         ;;
224 1)
225         branch=$1
226         remote=.
227
228         eval $(get_remote_branch $1)
229         ;;
230 *)
231         usage
232         exit 1
233         ;;
234 esac
235
236 branch="${branch#refs/heads/}"
237
238 test "$mode" = rebase && {
239         require_clean_work_tree "update with rebase" "Please commit or stash them."
240         oldremoteref=$(git merge-base --fork-point $branch $curr_branch 2>/dev/null)
241 }
242 orig_head=$(git rev-parse -q --verify HEAD)
243
244 if test $remote = "."
245 then
246         extra=--quiet
247 fi
248
249 git fetch $verbosity $progress $dry_run $recurse_submodules $remote $branch $extra || exit 1
250
251 test -z "$dry_run" || exit 0
252
253 merge_head=$(sed -e '/  not-for-merge   /d' -e 's/      .*//' "$GIT_DIR"/FETCH_HEAD)
254
255 test -z "$merge_head" &&
256         die "$(gettext "Couldnot fetch branch '${branch#refs/heads/}'.")"
257
258 if test "$mode" = 'ff-only' && test -z "$no_ff$ff_only${squash#--no-squash}"
259 then
260         # check if a non-fast-forward merge would be needed
261         if ! git merge-base --is-ancestor "$orig_head" "$merge_head" &&
262                 ! git merge-base --is-ancestor "$merge_head" "$orig_head"
263         then
264                 die "$(gettext "The update was not fast-forward, please either merge or rebase.
265 If unsure, run 'git update --merge'.")"
266         fi
267         ff_only=--ff-only
268 fi
269
270 if test "$mode" = rebase
271 then
272         o=$(git show-branch --merge-base $curr_branch $merge_head $oldremoteref)
273         test "$oldremoteref" = "$o" && unset oldremoteref
274 fi
275
276 build_msg () {
277         if test "$curr_branch_short" = "$branch"
278         then
279                 echo "Update branch '$curr_branch_short'"
280         else
281                 msg="Merge"
282                 msg="${msg} branch '$curr_branch_short'"
283                 test "$branch" != "master" && msg="${msg} into $branch"
284                 echo "$msg"
285         fi
286 }
287
288 fix_msg () {
289         build_msg
290         sed -e '1 d'
291 }
292
293 case "$mode" in
294 rebase)
295         eval="git-rebase $diffstat $strategy_args $merge_args $rebase_args $verbosity"
296         eval="$eval --onto $merge_head ${oldremoteref:-$merge_head}"
297         eval "exec $eval"
298         ;;
299 *)
300         merge_msg=$(git fmt-merge-msg $log_arg < "$GIT_DIR"/FETCH_HEAD | fix_msg) || exit
301         eval="git-merge $diffstat $no_commit $verify_signatures $edit $squash $no_ff $ff_only"
302         eval="$eval $log_arg $strategy_args $merge_args $verbosity $progress"
303         eval="$eval --reverse-parents -m \"\$merge_msg\" $merge_head"
304         eval "exec $eval"
305         ;;
306 esac