combined-diff: use diffcore before intersecting paths.
[git.git] / git-commit.sh
1 #!/bin/sh
2 #
3 # Copyright (c) 2005 Linus Torvalds
4 # Copyright (c) 2006 Junio C Hamano
5
6 USAGE='[-a] [-i] [-s] [-v | --no-verify]  [-m <message> | -F <logfile> | (-C|-c) <commit>] [-e] [--author <author>] [<path>...]'
7
8 SUBDIRECTORY_OK=Yes
9 . git-sh-setup
10
11 git-rev-parse --verify HEAD >/dev/null 2>&1 ||
12 initial_commit=t
13
14 refuse_partial () {
15         echo >&2 "$1"
16         echo >&2 "You might have meant to say 'git commit -i paths...', perhaps?"
17         exit 1
18 }
19
20 SAVE_INDEX="$GIT_DIR/save-index$$"
21 save_index () {
22         cp "$GIT_DIR/index" "$SAVE_INDEX"
23 }
24
25 run_status () {
26         (
27                 cd "$TOP"
28                 if test '' != "$TMP_INDEX"
29                 then
30                         GIT_INDEX_FILE="$TMP_INDEX" git-status
31                 else
32                         git-status
33                 fi
34         )
35 }
36
37 all=
38 also=
39 only=
40 logfile=
41 use_commit=
42 no_edit=
43 log_given=
44 log_message=
45 verify=t
46 signoff=
47 force_author=
48 while case "$#" in 0) break;; esac
49 do
50   case "$1" in
51   -F|--F|-f|--f|--fi|--fil|--file)
52       case "$#" in 1) usage ;; esac
53       shift
54       no_edit=t
55       log_given=t$log_given
56       logfile="$1"
57       shift
58       ;;
59   -F*|-f*)
60       no_edit=t
61       log_given=t$log_given
62       logfile=`expr "$1" : '-[Ff]\(.*\)'`
63       shift
64       ;;
65   --F=*|--f=*|--fi=*|--fil=*|--file=*)
66       no_edit=t
67       log_given=t$log_given
68       logfile=`expr "$1" : '-[^=]*=\(.*\)'`
69       shift
70       ;;
71   -a|--a|--al|--all)
72       all=t
73       shift
74       ;;
75   --au=*|--aut=*|--auth=*|--autho=*|--author=*)
76       force_author=`expr "$1" : '-[^=]*=\(.*\)'`
77       shift
78       ;;
79   --au|--aut|--auth|--autho|--author)
80       case "$#" in 1) usage ;; esac
81       shift
82       force_author="$1"
83       shift
84       ;;
85   -e|--e|--ed|--edi|--edit)
86       no_edit=
87       shift
88       ;;
89   -i|--i|--in|--inc|--incl|--inclu|--includ|--include)
90       also=t
91       shift
92       ;;
93   -o|--o|--on|--onl|--only)
94       only=t
95       shift
96       ;;
97   -m|--m|--me|--mes|--mess|--messa|--messag|--message)
98       case "$#" in 1) usage ;; esac
99       shift
100       log_given=t$log_given
101       log_message="$1"
102       no_edit=t
103       shift
104       ;;
105   -m*)
106       log_given=t$log_given
107       log_message=`expr "$1" : '-m\(.*\)'`
108       no_edit=t
109       shift
110       ;;
111   --m=*|--me=*|--mes=*|--mess=*|--messa=*|--messag=*|--message=*)
112       log_given=t$log_given
113       log_message=`expr "$1" : '-[^=]*=\(.*\)'`
114       no_edit=t
115       shift
116       ;;
117   -n|--n|--no|--no-|--no-v|--no-ve|--no-ver|--no-veri|--no-verif|--no-verify)
118       verify=
119       shift
120       ;;
121   -c)
122       case "$#" in 1) usage ;; esac
123       shift
124       log_given=t$log_given
125       use_commit="$1"
126       no_edit=
127       shift
128       ;;
129   --ree=*|--reed=*|--reedi=*|--reedit=*|--reedit-=*|--reedit-m=*|\
130   --reedit-me=*|--reedit-mes=*|--reedit-mess=*|--reedit-messa=*|\
131   --reedit-messag=*|--reedit-message=*)
132       log_given=t$log_given
133       use_commit=`expr "$1" : '-[^=]*=\(.*\)'`
134       no_edit=
135       shift
136       ;;
137   --ree|--reed|--reedi|--reedit|--reedit-|--reedit-m|--reedit-me|\
138   --reedit-mes|--reedit-mess|--reedit-messa|--reedit-messag|--reedit-message)
139       case "$#" in 1) usage ;; esac
140       shift
141       log_given=t$log_given
142       use_commit="$1"
143       no_edit=
144       shift
145       ;;
146   -C)
147       case "$#" in 1) usage ;; esac
148       shift
149       log_given=t$log_given
150       use_commit="$1"
151       no_edit=t
152       shift
153       ;;
154   --reu=*|--reus=*|--reuse=*|--reuse-=*|--reuse-m=*|--reuse-me=*|\
155   --reuse-mes=*|--reuse-mess=*|--reuse-messa=*|--reuse-messag=*|\
156   --reuse-message=*)
157       log_given=t$log_given
158       use_commit=`expr "$1" : '-[^=]*=\(.*\)'`
159       no_edit=t
160       shift
161       ;;
162   --reu|--reus|--reuse|--reuse-|--reuse-m|--reuse-me|--reuse-mes|\
163   --reuse-mess|--reuse-messa|--reuse-messag|--reuse-message)
164       case "$#" in 1) usage ;; esac
165       shift
166       log_given=t$log_given
167       use_commit="$1"
168       no_edit=t
169       shift
170       ;;
171   -s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
172       signoff=t
173       shift
174       ;;
175   -v|--v|--ve|--ver|--veri|--verif|--verify)
176       verify=t
177      shift
178       ;;
179   --)
180       shift
181       break
182       ;;
183   -*)
184       usage
185       ;;
186   *)
187       break
188       ;;
189   esac
190 done
191
192 case "$log_given" in
193 tt*)
194   die "Only one of -c/-C/-F/-m can be used." ;;
195 esac
196
197 case "$#,$also$only" in
198 *,tt)
199   die "Only one of --include/--only can be used." ;;
200 0,t)
201   die "No paths with --include/--only does not make sense." ;;
202 0,)
203   ;;
204 *,)
205   echo >&2 "assuming --include paths..."
206   also=t
207   # Later when switch the defaults, we will replace them with these:
208   # echo >&2 "assuming --only paths..."
209   # also=
210   ;;
211 esac
212 unset only
213
214 TOP=`git-rev-parse --show-cdup`
215 if test -z "$TOP"
216 then
217         TOP=./
218 fi
219
220 case "$all,$also" in
221 t,t)
222         die "Cannot use -a and -i at the same time." ;;
223 t,)
224         case "$#" in
225         0) ;;
226         *) die "Paths with -a does not make sense." ;;
227         esac
228
229         save_index &&
230         (
231                 cd "$TOP"
232                 git-diff-files --name-only -z |
233                 git-update-index --remove -z --stdin
234         )
235         ;;
236 ,t)
237         case "$#" in
238         0) die "No paths with -i does not make sense." ;;
239         esac
240
241         save_index &&
242         git-diff-files --name-only -z -- "$@"  |
243         (cd "$TOP" && git-update-index --remove -z --stdin)
244         ;;
245 ,)
246         case "$#" in
247         0)
248             ;; # commit as-is
249         *)
250             if test -f "$GIT_DIR/MERGE_HEAD"
251             then
252                 refuse_partial "Cannot do a partial commit during a merge."
253             fi
254             TMP_INDEX="$GIT_DIR/tmp-index$$"
255             if test -z "$initial_commit"
256             then
257                 # make sure index is clean at the specified paths, or
258                 # they are additions.
259                 dirty_in_index=`git-diff-index --cached --name-status \
260                         --diff-filter=DMTU HEAD -- "$@"`
261                 test -z "$dirty_in_index" ||
262                 refuse_partial "Different in index and the last commit:
263 $dirty_in_index"
264             fi
265             commit_only=`git-ls-files -- "$@"` ;;
266         esac
267         ;;
268 esac
269
270 git-update-index -q --refresh || exit 1
271
272 trap '
273         test -z "$TMP_INDEX" || {
274                 test -f "$TMP_INDEX" && rm -f "$TMP_INDEX"
275         }
276         test -f "$SAVE_INDEX" && mv -f "$SAVE_INDEX" "$GIT_DIR/index"
277 ' 0
278
279 if test "$TMP_INDEX"
280 then
281         if test -z "$initial_commit"
282         then
283                 GIT_INDEX_FILE="$TMP_INDEX" git-read-tree HEAD
284         else
285                 rm -f "$TMP_INDEX"
286         fi || exit
287         echo "$commit_only" |
288         GIT_INDEX_FILE="$TMP_INDEX" git-update-index --add --remove --stdin &&
289         save_index &&
290         echo "$commit_only" |
291         git-update-index --remove --stdin ||
292         exit
293 fi
294
295 if test t = "$verify" && test -x "$GIT_DIR"/hooks/pre-commit
296 then
297         if test "$TMP_INDEX"
298         then
299                 GIT_INDEX_FILE="$TMP_INDEX" "$GIT_DIR"/hooks/pre-commit
300         else
301                 "$GIT_DIR"/hooks/pre-commit
302         fi || exit
303 fi
304
305 if test "$log_message" != ''
306 then
307         echo "$log_message"
308 elif test "$logfile" != ""
309 then
310         if test "$logfile" = -
311         then
312                 test -t 0 &&
313                 echo >&2 "(reading log message from standard input)"
314                 cat
315         else
316                 cat <"$logfile"
317         fi
318 elif test "$use_commit" != ""
319 then
320         git-cat-file commit "$use_commit" | sed -e '1,/^$/d'
321 elif test -f "$GIT_DIR/MERGE_HEAD" && test -f "$GIT_DIR/MERGE_MSG"
322 then
323         cat "$GIT_DIR/MERGE_MSG"
324 fi | git-stripspace >"$GIT_DIR"/COMMIT_EDITMSG
325
326 case "$signoff" in
327 t)
328         {
329                 echo
330                 git-var GIT_COMMITTER_IDENT | sed -e '
331                         s/>.*/>/
332                         s/^/Signed-off-by: /
333                 '
334         } >>"$GIT_DIR"/COMMIT_EDITMSG
335         ;;
336 esac
337
338 if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
339         echo "#"
340         echo "# It looks like you may be committing a MERGE."
341         echo "# If this is not correct, please remove the file"
342         echo "# $GIT_DIR/MERGE_HEAD"
343         echo "# and try again"
344         echo "#"
345 fi >>"$GIT_DIR"/COMMIT_EDITMSG
346
347 # Author
348 if test '' != "$force_author"
349 then
350         GIT_AUTHOR_NAME=`expr "$force_author" : '\(.*[^ ]\) *<.*'` &&
351         GIT_AUTHOR_EMAIL=`expr "$force_author" : '.*\(<.*\)'` &&
352         test '' != "$GIT_AUTHOR_NAME" &&
353         test '' != "$GIT_AUTHOR_EMAIL" ||
354         die "malformatted --author parameter"
355         export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
356 elif test '' != "$use_commit"
357 then
358         pick_author_script='
359         /^author /{
360                 s/'\''/'\''\\'\'\''/g
361                 h
362                 s/^author \([^<]*\) <[^>]*> .*$/\1/
363                 s/'\''/'\''\'\'\''/g
364                 s/.*/GIT_AUTHOR_NAME='\''&'\''/p
365
366                 g
367                 s/^author [^<]* <\([^>]*\)> .*$/\1/
368                 s/'\''/'\''\'\'\''/g
369                 s/.*/GIT_AUTHOR_EMAIL='\''&'\''/p
370
371                 g
372                 s/^author [^<]* <[^>]*> \(.*\)$/\1/
373                 s/'\''/'\''\'\'\''/g
374                 s/.*/GIT_AUTHOR_DATE='\''&'\''/p
375
376                 q
377         }
378         '
379         set_author_env=`git-cat-file commit "$use_commit" |
380         LANG=C LC_ALL=C sed -ne "$pick_author_script"`
381         eval "$set_author_env"
382         export GIT_AUTHOR_NAME
383         export GIT_AUTHOR_EMAIL
384         export GIT_AUTHOR_DATE
385 fi
386
387 PARENTS="-p HEAD"
388 if test -z "$initial_commit"
389 then
390         if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
391                 PARENTS="-p HEAD "`sed -e 's/^/-p /' "$GIT_DIR/MERGE_HEAD"`
392         fi
393 else
394         if [ -z "$(git-ls-files)" ]; then
395                 echo >&2 Nothing to commit
396                 exit 1
397         fi
398         PARENTS=""
399 fi
400
401
402 run_status >>"$GIT_DIR"/COMMIT_EDITMSG
403 if [ "$?" != "0" -a ! -f "$GIT_DIR/MERGE_HEAD" ]
404 then
405         rm -f "$GIT_DIR/COMMIT_EDITMSG"
406         run_status
407         exit 1
408 fi
409 case "$no_edit" in
410 '')
411         case "${VISUAL:-$EDITOR},$TERM" in
412         ,dumb)
413                 echo >&2 "Terminal is dumb but no VISUAL nor EDITOR defined."
414                 echo >&2 "Please supply the commit log message using either"
415                 echo >&2 "-m or -F option.  A boilerplate log message has"
416                 echo >&2 "been prepared in $GIT_DIR/COMMIT_EDITMSG"
417                 exit 1
418                 ;;
419         esac
420         ${VISUAL:-${EDITOR:-vi}} "$GIT_DIR/COMMIT_EDITMSG"
421         ;;
422 esac
423
424 case "$verify" in
425 t)
426         if test -x "$GIT_DIR"/hooks/commit-msg
427         then
428                 "$GIT_DIR"/hooks/commit-msg "$GIT_DIR"/COMMIT_EDITMSG || exit
429         fi
430 esac
431
432 grep -v '^#' < "$GIT_DIR"/COMMIT_EDITMSG |
433 git-stripspace > "$GIT_DIR"/COMMIT_MSG
434
435 if cnt=`grep -v -i '^Signed-off-by' "$GIT_DIR"/COMMIT_MSG |
436         git-stripspace |
437         wc -l` &&
438    test 0 -lt $cnt
439 then
440         if test -z "$TMP_INDEX"
441         then
442                 tree=$(git-write-tree)
443         else
444                 tree=$(GIT_INDEX_FILE="$TMP_INDEX" git-write-tree) &&
445                 rm -f "$TMP_INDEX"
446         fi &&
447         commit=$(cat "$GIT_DIR"/COMMIT_MSG | git-commit-tree $tree $PARENTS) &&
448         git-update-ref HEAD $commit $current &&
449         rm -f -- "$GIT_DIR/MERGE_HEAD"
450 else
451         echo >&2 "* no commit message?  aborting commit."
452         false
453 fi
454 ret="$?"
455 rm -f "$GIT_DIR/COMMIT_MSG" "$GIT_DIR/COMMIT_EDITMSG"
456 git-rerere
457
458 if test -x "$GIT_DIR"/hooks/post-commit && test "$ret" = 0
459 then
460         "$GIT_DIR"/hooks/post-commit
461 fi
462 if test 0 -eq "$ret"
463 then
464         rm -f "$SAVE_INDEX"
465 fi
466 exit "$ret"