Test commit
[cogito/jonas.git] / cg-commit
blob20059f5a792e5708d82727c3d1a47f611c063d78
1 #!/usr/bin/env bash
3 # Commit changes in the working tree to the repository
4 # Copyright (c) Petr Baudis, 2005
6 # Commits your changes to the GIT repository. Accepts the commit message
7 # from `stdin`. If the commit message is not modified the commit will be
8 # aborted.
10 # Note that you can undo a commit by the `cg-admin-uncommit` command,
11 # but that is possible only under special circumstances. See the CAVEATS
12 # section in its documentation.
14 # Commit author
15 # ~~~~~~~~~~~~~
16 # Each commit has two user identification fields - commit author and committer.
17 # By default, it is recorded that you authored the commit, but it is considered
18 # a good practice to change this to the actual author of the change if you are
19 # merely applying someone else's patch. It is always recorded that you were the
20 # patch committer.
22 # The commit author is determined by examining various sources in this order:
24 # * '--author' (see OPTIONS)
26 # * 'GIT_AUTHOR_*' (see ENVIRONMENT)
28 # * Configuration file: you can insert this to '.git/config' or '~/.gitconfig':
30 # [user]
31 # name = "Your Name"
32 # email = "your@email.address.xz"
34 # * System information: The author name defaults to the GECOS field of your
35 # '/etc/passwd' entry, which is taken almost verbatim. The author email
36 # defaults to your 'username@hostname.domainname' (but you should change this
37 # to the real email address you use if it is any different).
39 # OPTIONS
40 # -------
41 # --amend:: Amend the last commit (dangerous: see `cg-admin-uncommit`)
42 # Amend the last commit with some additional stuff; this will use your
43 # current working copy as the new commit "contents" and otherwise
44 # have similar effects as -c HEAD; the new commit will _replace_ the
45 # current HEAD - which means that you should read `cg-admin-uncommit`
46 # caveats section. If you want to adjust the log message or authorship
47 # information, use it with combination with the '-e' option.
49 # --author AUTHOR_STRING:: Set the author information according to the argument
50 # Set the commit author information according to the argument instead
51 # of your environment, .git/author, or user information.
53 # The 'AUTHOR_STRING' format is `Author Name <author@email> Date`. The
54 # author name and date is optional, only the email is required to be
55 # always present (e.g. '--author "<pasky@ucw.cz>"' will use the current
56 # date and the real name set for your system account (usually in
57 # the GECOS field), but a different email address).
59 # -c COMMIT_ID:: Copy author info and commit message from COMMIT_ID
60 # Copy the commit metainformation from a given commit ID (that is, only
61 # the author information and the commit message - NOT committer
62 # information and NOT the commit diff). This option is typically used
63 # when replaying commits from one lineage or repository to another - see
64 # also `cg-patch -C` (if you want to apply the diffs as well).
66 # -m MESSAGE:: Specify commit message
67 # Specify the commit message, which is used instead of starting
68 # up an editor (if the input is not `stdin`, the input is appended
69 # after all the '-m' messages). Multiple '-m' parameters are appended
70 # to a single commit message, each as separate paragraph.
72 # -M FILE:: Read commit message from a file
73 # Include commit message from a file (this has the same effect as if
74 # you would cat it to stdin).
76 # -e:: Force message editing of messages given with -m
77 # Force the editor to be brought up even when '-m' parameters were
78 # passed to `cg-commit`.
80 # -E:: Force message editing and commit the result
81 # Force the editor to be brought up and do the commit even if
82 # the default commit message is not changed.
84 # -f:: Force commit when no changes has been made
85 # Force the commit even when there's "nothing to commit", that is
86 # the tree is the same as the last time you committed, no changes
87 # happened. This also forces the commit even if committing is blocked
88 # for some reason.
90 # -N:: Only update the cache
91 # Don't add the files to the object database, just update the caches
92 # and the commit information. This is for special purposes when you
93 # might not actually _have_ any object database. This option is
94 # normally not interesting.
96 # --no-hooks:: Do not call any commit hooks
97 # Do not call any commit hooks during the commit.
99 # -p, --review:: Show and enable editing of changes being committed
100 # Show changes being committed as a patch appended to the commit message
101 # buffer. Changes made to the patch will be reapplied before completing
102 # the commit. This implies '-e'.
104 # --push[=BRANCH]:: Immediately push the commit to a remote branch
105 # Push the new commit to a remote branch (defaults to 'origin') right
106 # after commiting it. Furthermore, the push is done in a transactional
107 # way so that in case it fails, nothing gets committed locally either.
108 # Note that you need to update your branch against the remote branch
109 # before committing so that the remote branch contains no commits
110 # not contained in your branch.
112 # The preferred workflow is that you first commit your changes, then
113 # do an update from the remote branch, then push to the remote branch.
114 # This has the advantage that the act of merging is explicitly recorded,
115 # reducing the danger of inadverent loss or damage of some changes during
116 # the merge and making the history reflect the reality more accurately.
117 # Also, it makes for nicer gitk diagrams. ;-) In this workflow, this
118 # '--push' switch does not come into play. Use it only if you know
119 # exactly why you do not want to do the above.
121 # This flag is not supported when committing merges since the recovery
122 # in case of push failure would be too complicated. Please resort to
123 # the commit-before-update workflow in that case - you are already doing
124 # a merge anyway.
126 # -q:: Be very very quiet
127 # Be quiet in case there's "nothing to commit", and silently exit
128 # returning success. In a sense, this is the opposite to '-f'.
130 # -s, --signoff[=STRING]:: Automatically append a sign off line
131 # Add Signed-off-by line at the end of the commit message.
132 # Optionally, specify the exact name and email to sign off with by
133 # passing: `--signoff="Author Name <user@example.com>"`.
135 # FILES
136 # -----
137 # $GIT_DIR/commit-template::
138 # If the file exists it will be used as a template when creating
139 # the commit message. The template file makes it possible to
140 # automatically add `Signed-off-by` line to the log message.
142 # $GIT_DIR/hooks/commit-post::
143 # If the file exists and is executable it will be executed upon
144 # completion of the commit. The script is passed two arguments.
145 # The first argument is the commit ID and the second is the
146 # branchname. A sample `commit-post` script might look like:
148 # #!/bin/sh
149 # id=$1
150 # branch=$2
151 # echo "Committed $id in $branch" | mail user@host
153 # $GIT_DIR/hooks/post-commit::
154 # If the file exists and is executable it will be executed upon
155 # completion of the commit. It is passed the same arguments and
156 # called at exactly the same time as the 'commit-post' hook; it
157 # exists for compatibility with `git-commit`(1). The default
158 # 'post-commit' hook, when enabled, demonstrates how to send out
159 # a commit notification e-mail. (Note that `git-commit`(1) calls
160 # the hook with no arguments.)
162 # ENVIRONMENT VARIABLES
163 # ---------------------
164 # See the 'Commit author' section above for details about the name/email/date
165 # environment variables meaning and default values.
167 # GIT_AUTHOR_NAME::
168 # Author's name.
170 # GIT_AUTHOR_EMAIL::
171 # Author's e-mail address.
173 # GIT_AUTHOR_DATE::
174 # Date, useful when applying patches submitted over e-mail.
176 # GIT_COMMITTER_NAME::
177 # Committer's name.
179 # GIT_COMMITTER_EMAIL::
180 # Committer's e-mail address. The recommended policy is not to change
181 # this, though - it may not be necessarily a valid e-mail address, but
182 # its purpose is more to identify the actual user and machine
183 # where the commit was done. However, it is obviously ultimately
184 # a policy decision of a particular project to determine whether
185 # this should be a real e-mail or not.
187 # GIT_COMMITTER_DATE::
188 # This is the date of the commit itself. It should be never
189 # overridden, unless you know you absolutely need to override it
190 # (to ensure the commit gets the same ID as another or when
191 # migrating history around).
193 # EDITOR::
194 # The editor used for entering revision log information.
196 # CONFIGURATION VARIABLES
197 # -----------------------
198 # The following GIT configuration file variables are recognized:
200 # user.author, user.email::
201 # User credentials. See the "Commit author" section for details.
203 # cogito.hooks.commit.post.allmerged::
204 # If set to "true" and you are committing a merge, the post-hook will
205 # be called for all the merged commits in sequence (the earliest first).
206 # Otherwise, the hook will be called only for the merge commit.
208 # Developer's documentation:
210 # -C:: Ignore cache
211 # Make `cg-commit` ignore the cache and just commit the thing as-is.
212 # Note, this is used internally by 'Cogito' when merging, and it is
213 # also useful when you are performing the initial commit. This
214 # option does not make sense when files are given on the command line.
216 # -w FILE:: Do not update ref but save commit id to FILE
217 # Do not update the HEAD ref with the new commit id, but just
218 # save the newly created commit id to FILE. Implies '--no-hooks'.
220 # Testsuite: Partial (used in many tests but a dedicated testsuite is missing)
222 USAGE="cg-commit [-m MESSAGE]... [-e] [-c COMMIT_ID] [OTHER_OPTIONS] [FILE]... [< MESSAGE]"
224 . "${COGITO_LIB}"cg-Xlib || exit 1
227 ### XXX: The spaghetti code below got rather messy and convoluted over
228 ### the time. Someone should clean it up. :/ --pasky
231 load_author()
233 local astr="$1" force="$2"
234 if [ "$force" -o -z "$GIT_AUTHOR_NAME" ] && echo "$astr" | grep -q '^[^< ]'; then
235 export GIT_AUTHOR_NAME="$(echo "$astr" | sed 's/ *<.*//')"
237 if [ "$force" -o -z "$GIT_AUTHOR_EMAIL" ] && echo "$astr" | grep -q '<.*>'; then
238 export GIT_AUTHOR_EMAIL="$(echo "$astr" | sed 's/.*<\(.*\)>.*/\1/')"
240 if [ "$force" -o -z "$GIT_AUTHOR_DATE" ] && echo "$astr" | grep -q '[^> ]$'; then
241 export GIT_AUTHOR_DATE="$(echo "$astr" | sed 's/.*> *//')"
245 if [ -s "$_git/author" ]; then
246 warn ".git/author is obsolete, use .git/config instead (see the cg-commit docs)"
247 load_author "$(cat "$_git/author")"
249 if [ -z "$GIT_AUTHOR_NAME" -o -z "$GIT_AUTHOR_EMAIL" ]; then
250 # Always pre-fill those so that the user can modify them in the
251 # commit template.
252 idline="$(git-var GIT_AUTHOR_IDENT)"
253 [ -z "$GIT_AUTHOR_NAME" ] && export GIT_AUTHOR_NAME="$(echo "$idline" | sed 's/ *<.*//')"
254 [ -z "$GIT_AUTHOR_EMAIL" ] && export GIT_AUTHOR_EMAIL="$(echo "$idline" | sed 's/.*<\(.*\)>.*/\1/')"
257 force=
258 forceeditor=
259 ignorecache=
260 infoonly=
261 commitalways=
262 missingok=
263 amend=
264 review=
265 signoff=
266 copy_commit=
267 msgs=()
268 msgfile=
269 quiet=
270 no_hooks=
271 writeref=
272 push=
273 push_branch=
274 while optparse; do
275 if optparse --author=; then
276 load_author "$OPTARG" force
277 elif optparse -C; then
278 ignorecache=1
279 elif optparse -N; then
280 missingok=--missing-ok
281 infoonly=--info-only
282 elif optparse -e; then
283 forceeditor=1
284 elif optparse -E; then
285 forceeditor=1
286 commitalways=-f
287 elif optparse -f; then
288 force=1
289 elif optparse -q; then
290 quiet=1
291 elif optparse --amend; then
292 amend=1
293 copy_commit="$(cg-object-id -c HEAD)" || exit 1
294 elif optparse -p || optparse --review; then
295 review=1
296 forceeditor=1
297 elif optparse -s || optparse --signoff; then
298 [ "$signoff" ] || signoff="$(git-var GIT_AUTHOR_IDENT | sed 's/> .*/>/')"
299 elif optparse --signoff=; then
300 signoff="$OPTARG"
301 elif optparse -m=; then
302 msgs[${#msgs[@]}]="$OPTARG"
303 elif optparse -M=; then
304 msgfile="$OPTARG"
305 elif optparse -c=; then
306 copy_commit="$(cg-object-id -c "$OPTARG")" || exit 1
307 elif optparse --no-hooks; then
308 no_hooks=1
309 elif optparse -w=; then
310 writeref="$OPTARG"
311 elif optparse --push; then
312 push=1
313 elif optparse --push=; then
314 push=1
315 push_branch="$OPTARG"
316 else
317 optfail
319 done
321 if [ -s "$_git/blocked" ] && [ ! "$writeref" ]; then
322 if [ "$force" ]; then
323 warn "committing to a blocked repository. Assuming you know what are you doing."
324 else
325 die "committing blocked: $(cat "$_git/blocked")"
328 # Deprecated as of 2006-11-17
329 [ -s "$_git/merging" ] && die "old-style merge state detected, panicking; you upgraded cogito in the middle of a merge! redo the merge, cg-reset will bring you back to the starting line"
330 mstatedir="$_git/cg-merge-state"
332 if [ ! "$ignorecache" ]; then
333 cg-object-id HEAD >/dev/null 2>&1 || ignorecache=1
336 editor=
337 [ "$forceeditor" ] && editor=1
338 no_custom_messages=
339 [ ! "$msgs" ] && [ ! "$msgfile" ] && no_custom_messages=1
340 [ "$no_custom_messages" ] && [ ! "$copy_commit" ] && editor=1
342 tmpdir="$(mktemp -d -t gitci.XXXXXX)"
343 cleanup () { rm -rf "$tmpdir"; }
344 cleanup_trap "cleanup"
345 trap "cleanup" EXIT
347 if [ "$review" ]; then
348 PATCH="$tmpdir/patch.diff"
349 PATCH2="$tmpdir/patch2.diff"
350 editor=1
353 if [ "$amend" ]; then
354 [ "$merging" ] && die "cannot amend previous commit in the middle of a merge"
355 # Recommit even with no changes to the content; meta might change
356 force=1
359 [ "$push" ] && [ "$merging" ] && die "no support for auto-pushing merge commits"
361 if [ "$ARGS" -o "$_git_relpath" ]; then
362 [ "$ignorecache" ] && die "you cannot list files for the initial commit"
363 [ -s "$mstatedir/merging" ] && die "cannot commit individual files when merging"
365 filter="$tmpdir/filter"
366 [ "$_git_relpath" -a ! "$ARGS" ] && echo "$_git_relpath" >>"$filter"
367 for file in "${ARGS[@]}"; do
368 echo "${_git_relpath}$file" >>"$filter"
369 done
371 eval "commitfiles=($(cat "$filter" | path_xargs git-diff-index --name-status -z -r -m HEAD -- | \
372 perl -n0e 'chomp; if (defined $meta) { s/([\"\\])/\\\1/; print "\"$meta $_\"\n"; $meta=undef } else { $meta = $_ }'))"
373 customfiles=1
375 [ "$review" ] && cat "$filter" | path_xargs git-diff-index -r -m -p HEAD -- > "$PATCH"
376 rm "$filter"
378 else
379 # We bother with added/removed files here instead of updating
380 # the cache at the time of cg-(add|rm), since we want to
381 # have the cache in a consistent state representing the tree
382 # as it was the last time we committed. Otherwise, e.g. partial
383 # conflicts would be a PITA since added/removed files would
384 # be committed along automagically as well.
386 if [ ! "$ignorecache" ]; then
387 # \t instead of the tab character itself works only with new
388 # sed versions.
389 eval "commitfiles=($(git-diff-index --name-status -z -r -m HEAD | \
390 perl -n0e 'chomp; if (defined $meta) { s/([\"\\])/\\\1/; print "\"$meta $_\"\n"; $meta=undef } else { $meta = $_ }'))"
392 if [ -s "$mstatedir/commit-ignore" ]; then
393 newcommitfiles=()
394 for file in "${commitfiles[@]}"; do
395 fgrep -qx "${file:2}" "$mstatedir/commit-ignore" && continue
396 newcommitfiles[${#newcommitfiles[@]}]="$file"
397 done
398 commitfiles=("${newcommitfiles[@]}")
402 [ "$review" ] && git-diff-index -r -m -p HEAD > "$PATCH"
404 merging=
405 [ -s "$mstatedir/merging" ] && merging="$(cat "$mstatedir/merging" | sed 's/^/-p /')"
409 if [ "$review" ]; then
410 LOGMSG="$tmpdir/logmsg.diff"
411 LOGMSG2="$tmpdir/logmsg2.diff"
412 else
413 LOGMSG="$tmpdir/logmsg"
414 LOGMSG2="$tmpdir/logmsg2"
417 written=
418 if [ "$merging" ] && [ ! "$editor" ]; then
419 warn "suppressing default merge log messages in favour of the custom -m passed to me."
420 elif [ "$merging" ]; then
421 echo -n 'Merge with ' >>"$LOGMSG"
422 [ -s "$mstatedir/merging-sym" ] || cp "$mstatedir/merging" "$mstatedir/merging-sym"
423 for sym in $(cat "$mstatedir/merging-sym"); do
424 uri="$(cat "$_git/branches/$sym" 2>/dev/null)"
425 [ "$uri" ] || uri="$sym"
426 echo "$uri" >>"$LOGMSG"
427 done
428 echo >>"$LOGMSG"
429 if [ -s "$mstatedir/squashing" ]; then
430 # We are squashing all the merged commits to a single one.
431 # Therefore, helpfully pre-fill the commit message with
432 # the messages of all the merged commits.
433 git-rev-list --pretty "$(cat "$mstatedir/merging")" ^HEAD >>"$LOGMSG"
435 written=1
437 for msg in "${msgs[@]}"; do
438 [ "$written" ] && echo >>"$LOGMSG"
439 echo "$msg" | fmt -s >>"$LOGMSG"
440 written=1
441 done
443 if [ "$copy_commit" ]; then
444 [ "$written" ] && echo >>"$LOGMSG"
445 eval "$(git-cat-file commit "$copy_commit" | pick_author)"
446 # --amend -m _replaces_ the original message
447 if [ ! "$amend" ] || [ "$editor" ] || [ "$no_custom_messages" ]; then
448 git-cat-file commit "$copy_commit" | sed -e '1,/^$/d' >>"$LOGMSG"
449 written=1
453 if [ "$msgfile" ]; then
454 [ "$written" ] && echo >>"$LOGMSG"
455 cat "$_git_relpath$msgfile" >>"$LOGMSG" || exit 1
456 written=1
459 add_signoff() {
460 local logmsg="$1"
461 if [ "$signoff" ] && ! grep -q -i "signed-off-by: $signoff" $logmsg; then
462 grep -q -i signed-off-by $logmsg || echo
463 echo "Signed-off-by: $signoff"
464 fi >>"$logmsg"
467 if editor_shalluse "$forceeditor"; then
468 # Always have at least one blank line, to ease the editing for
469 # the poor people whose text editor has no 'O' command.
470 [ "$written" ] || echo >>"$LOGMSG"
471 # Also, add the signoff line _now_ before spewing out CG: lines.
472 # (In case of non-tty input we do it later after taking the actual
473 # log message from stdin.)
474 add_signoff "$LOGMSG"
477 # CG: -----------------------------------------------------------------------
478 editor_comment_start commit
480 if [ "$GIT_AUTHOR_NAME" -o "$GIT_AUTHOR_EMAIL" -o "$GIT_AUTHOR_DATE" ]; then
481 echo "CG:" >>"$LOGMSG"
482 [ "$GIT_AUTHOR_NAME" ] && echo "CG: Author: $GIT_AUTHOR_NAME" >>"$LOGMSG"
483 [ "$GIT_AUTHOR_EMAIL" ] && echo "CG: Email: $GIT_AUTHOR_EMAIL" >>"$LOGMSG"
484 [ "$GIT_AUTHOR_DATE" ] && echo "CG: Date: $GIT_AUTHOR_DATE" >>"$LOGMSG"
485 echo "CG:" >>"$LOGMSG"
488 if [ ! "$ignorecache" ] && [ ! "$review" ]; then
489 if [ ! "$merging" ]; then
490 if [ ! "$force" ] && [ ! "${commitfiles[*]}" ]; then
491 rm "$LOGMSG"
492 [ "$quiet" ] && exit 0 || die 'Nothing to commit'
494 echo "CG: By deleting lines beginning with CG:F, the associated file" >>"$LOGMSG"
495 echo "CG: will be removed from the commit list." >>"$LOGMSG"
497 echo "CG:" >>"$LOGMSG"
498 [ ${#commitfiles[@]} -gt 0 ] && echo "CG: Modified files:" >>"$LOGMSG"
499 for file in "${commitfiles[@]}"; do
500 # TODO: Prepend a letter describing whether it's addition,
501 # removal or update. Or call git status on those files.
502 echo "CG:F $file" >>"$LOGMSG"
503 [ ! "$editor" ] && echo "$file"
504 done
505 if [ -s "$mstatedir/commit-ignore" ]; then
506 echo "CG:" >>"$LOGMSG"
507 echo "CG: I have kept back the $(wc -l "$mstatedir/commit-ignore" | cut -d ' ' -f 1) file(s) containing your local changes." >>"$LOGMSG"
508 echo "CG: You need not worry, the local changes will not interfere with the merge." >>"$LOGMSG"
511 if [ "$review" ]; then
512 echo "CG: Changes summary:"
513 echo "CG:"
514 git-apply --stat --summary < "$PATCH" | sed 's/^/CG: /'
515 echo "CG:"
516 fi >>"$LOGMSG"
518 # CG: -----------------------------------------------------------------------
519 editor_comment_end $commitalways commit
521 ftdiff=
522 if [ "$review" ]; then
524 echo "CG:"
525 echo "CG: The patch being committed:"
526 echo "CG: (You can edit it; your tree will be modified accordingly and"
527 echo "CG: the modified patch will be committed.)"
528 echo "CG:"
529 cat "$PATCH"
530 } >>"$LOGMSG"
532 editor_msg_end
534 cp "$LOGMSG" "$LOGMSG2"
535 if editor_shalluse "$forceeditor"; then
536 if [ "$editor" ] && ! editor $commitalways commit c; then
537 rm "$LOGMSG" "$LOGMSG2"
538 [ "$review" ] && rm "$PATCH"
539 echo "Commit message not modified, commit aborted" >&2
540 if [ "$merging" ]; then
541 cat >&2 <<__END__
542 Note that the merge is NOT aborted - you can cg-commit again, cg-reset will abort it.
543 __END__
544 [ -s "$mstatedir/commit-ignore" ] && cat >&2 <<__END__
545 (But note that cg-reset will remove your pending local changes as well!)
546 __END__
548 exit 1
550 if [ ! "$ignorecache" ] && [ ! "$merging" ] && [ ! "$review" ]; then
551 eval "newcommitfiles=($(grep ^CG:F "$LOGMSG2" | sed -e 's/\"/\\&/g' -e 's/^CG:F *\(.*\)$/"\1"/'))"
552 if [ ! "$force" ] && [ ! "${newcommitfiles[*]}" ]; then
553 rm "$LOGMSG" "$LOGMSG2"
554 [ "$quiet" ] && exit 0 || die 'Nothing to commit'
556 if [ "${commitfiles[*]}" != "${newcommitfiles[*]}" ]; then
557 commitfiles=("${newcommitfiles[@]}")
558 customfiles=1
561 editor_parse_setif GIT_AUTHOR_NAME Author
562 editor_parse_setif GIT_AUTHOR_EMAIL Email
563 editor_parse_setif GIT_AUTHOR_DATE Date
564 else
565 cat >>"$LOGMSG2"
566 add_signoff "$LOGMSG2"
569 if [ ! "$review" ]; then
570 editor_parse_clean
571 else
572 sed '/^CG: Changes summary:/,$d' < "$LOGMSG2" > "$LOGMSG"
573 sed -n '/^CG: Changes summary:/,$p' < "$LOGMSG2" | grep -v ^CG: > "$PATCH2"
574 mv "$LOGMSG" "$LOGMSG2"; editor_parse_clean
576 rm "$LOGMSG2"
578 if [ "$review" ]; then
579 if ! cmp -s "$PATCH" "$PATCH2"; then
580 echo "Reverting the original patch..."
581 if ! cg-patch -R < "$PATCH"; then
582 die "unable to revert the original patch; the original patch is available in $PATCH, your edited patch is available in $PATCH2, your log message is in $LOGMSG, your working copy is in undefined state now and the world is about to end in ten minutes, have a nice day"
584 echo "Applying the edited patch..."
585 if ! cg-patch < "$PATCH2"; then
586 # FIXME: Do something better to alleviate this situation.
587 # At least restore the tree to the original state.
588 die "unable to apply the edited patch; the original patch is available in $PATCH, your edited patch is available in $PATCH2, your log message is in $LOGMSG, your working copy is in undefined state now and the world is about to end in five minutes, have a nice day"
594 precommit_update()
596 queueN=(); queueD=(); queueM=();
597 for file in "$@"; do
598 op="${file%% *}"
599 fname="${file#* }"
600 [ "$op" = "N" ] && op=A # N is to be renamed to A
601 [ "$op" = "A" ] || [ "$op" = "D" ] || [ "$op" = "M" ] || op=M
602 eval "queue$op[\${#queue$op[@]}]=\"\$fname\""
603 done
604 oldIFS="$IFS"
605 IFS=$'\n'
606 # XXX: Do we even need to do the --add and --remove update-caches?
607 [ "$queueA" ] && { ( echo "${queueA[*]}" | path_xargs git-update-index --add ${infoonly} -- ) || return 1; }
608 [ "$queueD" ] && { ( echo "${queueD[*]}" | path_xargs git-update-index --force-remove -- ) || return 1; }
609 [ "$queueM" ] && { ( echo "${queueM[*]}" | path_xargs git-update-index ${infoonly} -- ) || return 1; }
610 IFS="$oldIFS"
611 return 0
614 if [ ! "$ignorecache" ]; then
615 if [ "$customfiles" ]; then
616 precommit_update "${commitfiles[@]}" || die "update-cache failed"
617 export GIT_INDEX_FILE="$tmpdir/index"
618 git-read-tree HEAD
620 precommit_update "${commitfiles[@]}" || die "update-cache failed"
624 oldhead=
625 oldheadname="$(git-symbolic-ref HEAD)"
626 if [ -s "$_git/$oldheadname" ]; then
627 oldhead="$(get_ref "$oldheadname")"
628 oldheadstr="-p $oldhead"
630 if [ "$amend" ]; then
631 oldheadstr="$(cg-object-id -p "$oldhead" | sed 's/^/-p /')"
634 treeid="$(git-write-tree ${missingok})"
635 [ "$treeid" ] || die "git-write-tree failed"
636 if [ ! "$force" ] && [ ! "$merging" ] && [ "$oldhead" ] &&
637 [ "$treeid" = "$(cg-object-id -t)" ]; then
638 echo "Refusing to make an empty commit - the tree was not modified" >&2
639 echo "since the previous commit. If you really want to make the" >&2
640 echo "commit, pass cg-commit the -f argument." >&2
641 exit 2;
644 [ -s "$mstatedir/squashing" ] && merging=" " # viciously prevent recording a proper merge
645 newhead=$(git-commit-tree $treeid $oldheadstr $merging <"$LOGMSG")
646 rm "$LOGMSG"
648 if [ "$customfiles" ]; then
649 rm "$GIT_INDEX_FILE"
650 export GIT_INDEX_FILE=
653 commit_over()
655 echo "Committed as $newhead"
656 commit_cleanup
659 commit_cleanup()
661 rm -rf "$mstatedir"
664 if [ "$push" ]; then
665 pushargs=()
666 [ "$push_branch" ] && pushargs[${#pushargs[@]}]="$push_branch"
667 if ! cg-push -r "$newhead" "${pushargs[@]}"; then
668 commit_cleanup
669 die "push failed, you probably need to cg-update and try again (use cg-commit -c $newhead ... to reuse the log message)"
673 if [ "$newhead" ] && [ "$writeref" ]; then
674 echo "$newhead" >"$writeref" || die "unable to move to the new commit $newhead inside $writeref"
675 commit_over
677 elif [ "$newhead" ]; then
678 git-update-ref HEAD $newhead $oldhead || die "unable to move to the new commit $newhead"
679 commit_over
681 # Trigger the postcommit hook
682 branchname=
683 if [ -s "$_git/branch-name" ]; then
684 warn ".git/branch-name is deprecated and support for it will be removed soon."
685 warn "So please stop relying on it, or complain at pasky@suse.cz. Thanks."
686 branchname="$(cat "$_git/branch-name")"
688 [ -z "$branchname" ] && [ "$_git_head" != "master" ] && branchname="$_git_head"
689 if [ -x "$_git/hooks/commit-post" -o -x "$_git/hooks/post-commit" ] && [ ! "$no_hooks" ]; then
690 if [ "$(git-repo-config --bool cogito.hooks.commit.post.allmerged)" = "true" ]; then
691 # We just hope that for the initial commit, the user didn't
692 # manage to install the hook yet.
693 for merged in $(git-rev-list $newhead ^$oldhead | tac); do
694 [ -x "$_git/hooks/commit-post" ] && "$_git/hooks/commit-post" "$merged" "$branchname"
695 [ -x "$_git/hooks/post-commit" ] && "$_git/hooks/post-commit" "$merged" "$branchname"
696 done
697 else
698 [ -x "$_git/hooks/commit-post" ] && "$_git/hooks/commit-post" "$newhead" "$branchname"
699 [ -x "$_git/hooks/post-commit" ] && "$_git/hooks/post-commit" "$newhead" "$branchname"
703 exit 0
704 else
705 commit_cleanup
706 die "error during commit (oldhead $oldhead, treeid $treeid)"