diff options
Diffstat (limited to 'contrib/examples')
-rw-r--r-- | contrib/examples/builtin-fetch--tool.c | 4 | ||||
-rwxr-xr-x | contrib/examples/git-am.sh | 975 | ||||
-rwxr-xr-x | contrib/examples/git-commit.sh | 8 | ||||
-rwxr-xr-x | contrib/examples/git-difftool.perl | 481 | ||||
-rwxr-xr-x | contrib/examples/git-fetch.sh | 4 | ||||
-rwxr-xr-x | contrib/examples/git-merge.sh | 4 | ||||
-rwxr-xr-x | contrib/examples/git-pull.sh | 381 | ||||
-rwxr-xr-x | contrib/examples/git-repack.sh | 4 | ||||
-rwxr-xr-x | contrib/examples/git-revert.sh | 8 |
9 files changed, 1853 insertions, 16 deletions
diff --git a/contrib/examples/builtin-fetch--tool.c b/contrib/examples/builtin-fetch--tool.c index ee1916641e..a3eb19de04 100644 --- a/contrib/examples/builtin-fetch--tool.c +++ b/contrib/examples/builtin-fetch--tool.c @@ -516,7 +516,7 @@ int cmd_fetch__tool(int argc, const char **argv, const char *prefix) if (argc != 8) return error("append-fetch-head takes 6 args"); - filename = git_path("FETCH_HEAD"); + filename = git_path_fetch_head(); fp = fopen(filename, "a"); if (!fp) return error("cannot open %s: %s", filename, strerror(errno)); @@ -534,7 +534,7 @@ int cmd_fetch__tool(int argc, const char **argv, const char *prefix) if (argc != 5) return error("fetch-native-store takes 3 args"); - filename = git_path("FETCH_HEAD"); + filename = git_path_fetch_head(); fp = fopen(filename, "a"); if (!fp) return error("cannot open %s: %s", filename, strerror(errno)); diff --git a/contrib/examples/git-am.sh b/contrib/examples/git-am.sh new file mode 100755 index 0000000000..dd539f1a8a --- /dev/null +++ b/contrib/examples/git-am.sh @@ -0,0 +1,975 @@ +#!/bin/sh +# +# Copyright (c) 2005, 2006 Junio C Hamano + +SUBDIRECTORY_OK=Yes +OPTIONS_KEEPDASHDASH= +OPTIONS_STUCKLONG=t +OPTIONS_SPEC="\ +git am [options] [(<mbox>|<Maildir>)...] +git am [options] (--continue | --skip | --abort) +-- +i,interactive run interactively +b,binary* (historical option -- no-op) +3,3way allow fall back on 3way merging if needed +q,quiet be quiet +s,signoff add a Signed-off-by line to the commit message +u,utf8 recode into utf8 (default) +k,keep pass -k flag to git-mailinfo +keep-non-patch pass -b flag to git-mailinfo +m,message-id pass -m flag to git-mailinfo +keep-cr pass --keep-cr flag to git-mailsplit for mbox format +no-keep-cr do not pass --keep-cr flag to git-mailsplit independent of am.keepcr +c,scissors strip everything before a scissors line +whitespace= pass it through git-apply +ignore-space-change pass it through git-apply +ignore-whitespace pass it through git-apply +directory= pass it through git-apply +exclude= pass it through git-apply +include= pass it through git-apply +C= pass it through git-apply +p= pass it through git-apply +patch-format= format the patch(es) are in +reject pass it through git-apply +resolvemsg= override error message when patch failure occurs +continue continue applying patches after resolving a conflict +r,resolved synonyms for --continue +skip skip the current patch +abort restore the original branch and abort the patching operation. +committer-date-is-author-date lie about committer date +ignore-date use current timestamp for author date +rerere-autoupdate update the index with reused conflict resolution if possible +S,gpg-sign? GPG-sign commits +rebasing* (internal use for git-rebase)" + +. git-sh-setup +. git-sh-i18n +prefix=$(git rev-parse --show-prefix) +set_reflog_action am +require_work_tree +cd_to_toplevel + +git var GIT_COMMITTER_IDENT >/dev/null || + die "$(gettext "You need to set your committer info first")" + +if git rev-parse --verify -q HEAD >/dev/null +then + HAS_HEAD=yes +else + HAS_HEAD= +fi + +cmdline="git am" +if test '' != "$interactive" +then + cmdline="$cmdline -i" +fi +if test '' != "$threeway" +then + cmdline="$cmdline -3" +fi + +empty_tree=4b825dc642cb6eb9a060e54bf8d69288fbee4904 + +sq () { + git rev-parse --sq-quote "$@" +} + +stop_here () { + echo "$1" >"$dotest/next" + git rev-parse --verify -q HEAD >"$dotest/abort-safety" + exit 1 +} + +safe_to_abort () { + if test -f "$dotest/dirtyindex" + then + return 1 + fi + + if ! test -f "$dotest/abort-safety" + then + return 0 + fi + + abort_safety=$(cat "$dotest/abort-safety") + if test "z$(git rev-parse --verify -q HEAD)" = "z$abort_safety" + then + return 0 + fi + gettextln "You seem to have moved HEAD since the last 'am' failure. +Not rewinding to ORIG_HEAD" >&2 + return 1 +} + +stop_here_user_resolve () { + if [ -n "$resolvemsg" ]; then + printf '%s\n' "$resolvemsg" + stop_here $1 + fi + eval_gettextln "When you have resolved this problem, run \"\$cmdline --continue\". +If you prefer to skip this patch, run \"\$cmdline --skip\" instead. +To restore the original branch and stop patching, run \"\$cmdline --abort\"." + + stop_here $1 +} + +go_next () { + rm -f "$dotest/$msgnum" "$dotest/msg" "$dotest/msg-clean" \ + "$dotest/patch" "$dotest/info" + echo "$next" >"$dotest/next" + this=$next +} + +cannot_fallback () { + echo "$1" + gettextln "Cannot fall back to three-way merge." + exit 1 +} + +fall_back_3way () { + O_OBJECT=$(cd "$GIT_OBJECT_DIRECTORY" && pwd) + + rm -fr "$dotest"/patch-merge-* + mkdir "$dotest/patch-merge-tmp-dir" + + # First see if the patch records the index info that we can use. + cmd="git apply $git_apply_opt --build-fake-ancestor" && + cmd="$cmd "'"$dotest/patch-merge-tmp-index" "$dotest/patch"' && + eval "$cmd" && + GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \ + git write-tree >"$dotest/patch-merge-base+" || + cannot_fallback "$(gettext "Repository lacks necessary blobs to fall back on 3-way merge.")" + + say "$(gettext "Using index info to reconstruct a base tree...")" + + cmd='GIT_INDEX_FILE="$dotest/patch-merge-tmp-index"' + + if test -z "$GIT_QUIET" + then + eval "$cmd git diff-index --cached --diff-filter=AM --name-status HEAD" + fi + + cmd="$cmd git apply --cached $git_apply_opt"' <"$dotest/patch"' + if eval "$cmd" + then + mv "$dotest/patch-merge-base+" "$dotest/patch-merge-base" + mv "$dotest/patch-merge-tmp-index" "$dotest/patch-merge-index" + else + cannot_fallback "$(gettext "Did you hand edit your patch? +It does not apply to blobs recorded in its index.")" + fi + + test -f "$dotest/patch-merge-index" && + his_tree=$(GIT_INDEX_FILE="$dotest/patch-merge-index" git write-tree) && + orig_tree=$(cat "$dotest/patch-merge-base") && + rm -fr "$dotest"/patch-merge-* || exit 1 + + say "$(gettext "Falling back to patching base and 3-way merge...")" + + # This is not so wrong. Depending on which base we picked, + # orig_tree may be wildly different from ours, but his_tree + # has the same set of wildly different changes in parts the + # patch did not touch, so recursive ends up canceling them, + # saying that we reverted all those changes. + + eval GITHEAD_$his_tree='"$FIRSTLINE"' + export GITHEAD_$his_tree + if test -n "$GIT_QUIET" + then + GIT_MERGE_VERBOSITY=0 && export GIT_MERGE_VERBOSITY + fi + our_tree=$(git rev-parse --verify -q HEAD || echo $empty_tree) + git-merge-recursive $orig_tree -- $our_tree $his_tree || { + git rerere $allow_rerere_autoupdate + die "$(gettext "Failed to merge in the changes.")" + } + unset GITHEAD_$his_tree +} + +clean_abort () { + test $# = 0 || echo >&2 "$@" + rm -fr "$dotest" + exit 1 +} + +patch_format= + +check_patch_format () { + # early return if patch_format was set from the command line + if test -n "$patch_format" + then + return 0 + fi + + # we default to mbox format if input is from stdin and for + # directories + if test $# = 0 || test "x$1" = "x-" || test -d "$1" + then + patch_format=mbox + return 0 + fi + + # otherwise, check the first few non-blank lines of the first + # patch to try to detect its format + { + # Start from first line containing non-whitespace + l1= + while test -z "$l1" + do + read l1 || break + done + read l2 + read l3 + case "$l1" in + "From "* | "From: "*) + patch_format=mbox + ;; + '# This series applies on GIT commit'*) + patch_format=stgit-series + ;; + "# HG changeset patch") + patch_format=hg + ;; + *) + # if the second line is empty and the third is + # a From, Author or Date entry, this is very + # likely an StGIT patch + case "$l2,$l3" in + ,"From: "* | ,"Author: "* | ,"Date: "*) + patch_format=stgit + ;; + *) + ;; + esac + ;; + esac + if test -z "$patch_format" && + test -n "$l1" && + test -n "$l2" && + test -n "$l3" + then + # This begins with three non-empty lines. Is this a + # piece of e-mail a-la RFC2822? Grab all the headers, + # discarding the indented remainder of folded lines, + # and see if it looks like that they all begin with the + # header field names... + tr -d '\015' <"$1" | + sed -n -e '/^$/q' -e '/^[ ]/d' -e p | + sane_egrep -v '^[!-9;-~]+:' >/dev/null || + patch_format=mbox + fi + } < "$1" || clean_abort +} + +split_patches () { + case "$patch_format" in + mbox) + if test t = "$keepcr" + then + keep_cr=--keep-cr + else + keep_cr= + fi + git mailsplit -d"$prec" -o"$dotest" -b $keep_cr -- "$@" > "$dotest/last" || + clean_abort + ;; + stgit-series) + if test $# -ne 1 + then + clean_abort "$(gettext "Only one StGIT patch series can be applied at once")" + fi + series_dir=$(dirname "$1") + series_file="$1" + shift + { + set x + while read filename + do + set "$@" "$series_dir/$filename" + done + # remove the safety x + shift + # remove the arg coming from the first-line comment + shift + } < "$series_file" || clean_abort + # set the patch format appropriately + patch_format=stgit + # now handle the actual StGIT patches + split_patches "$@" + ;; + stgit) + this=0 + test 0 -eq "$#" && set -- - + for stgit in "$@" + do + this=$(expr "$this" + 1) + msgnum=$(printf "%0${prec}d" $this) + # Perl version of StGIT parse_patch. The first nonemptyline + # not starting with Author, From or Date is the + # subject, and the body starts with the next nonempty + # line not starting with Author, From or Date + @@PERL@@ -ne 'BEGIN { $subject = 0 } + if ($subject > 1) { print ; } + elsif (/^\s+$/) { next ; } + elsif (/^Author:/) { s/Author/From/ ; print ;} + elsif (/^(From|Date)/) { print ; } + elsif ($subject) { + $subject = 2 ; + print "\n" ; + print ; + } else { + print "Subject: ", $_ ; + $subject = 1; + } + ' -- "$stgit" >"$dotest/$msgnum" || clean_abort + done + echo "$this" > "$dotest/last" + this= + msgnum= + ;; + hg) + this=0 + test 0 -eq "$#" && set -- - + for hg in "$@" + do + this=$(( $this + 1 )) + msgnum=$(printf "%0${prec}d" $this) + # hg stores changeset metadata in #-commented lines preceding + # the commit message and diff(s). The only metadata we care about + # are the User and Date (Node ID and Parent are hashes which are + # only relevant to the hg repository and thus not useful to us) + # Since we cannot guarantee that the commit message is in + # git-friendly format, we put no Subject: line and just consume + # all of the message as the body + LANG=C LC_ALL=C @@PERL@@ -M'POSIX qw(strftime)' -ne 'BEGIN { $subject = 0 } + if ($subject) { print ; } + elsif (/^\# User /) { s/\# User/From:/ ; print ; } + elsif (/^\# Date /) { + my ($hashsign, $str, $time, $tz) = split ; + $tz_str = sprintf "%+05d", (0-$tz)/36; + print "Date: " . + strftime("%a, %d %b %Y %H:%M:%S ", + gmtime($time-$tz)) + . "$tz_str\n"; + } elsif (/^\# /) { next ; } + else { + print "\n", $_ ; + $subject = 1; + } + ' -- "$hg" >"$dotest/$msgnum" || clean_abort + done + echo "$this" >"$dotest/last" + this= + msgnum= + ;; + *) + if test -n "$patch_format" + then + clean_abort "$(eval_gettext "Patch format \$patch_format is not supported.")" + else + clean_abort "$(gettext "Patch format detection failed.")" + fi + ;; + esac +} + +prec=4 +dotest="$GIT_DIR/rebase-apply" +sign= utf8=t keep= keepcr= skip= interactive= resolved= rebasing= abort= +messageid= resolvemsg= resume= scissors= no_inbody_headers= +git_apply_opt= +committer_date_is_author_date= +ignore_date= +allow_rerere_autoupdate= +gpg_sign_opt= +threeway= + +if test "$(git config --bool --get am.messageid)" = true +then + messageid=t +fi + +if test "$(git config --bool --get am.keepcr)" = true +then + keepcr=t +fi + +while test $# != 0 +do + case "$1" in + -i|--interactive) + interactive=t ;; + -b|--binary) + gettextln >&2 "The -b/--binary option has been a no-op for long time, and +it will be removed. Please do not use it anymore." + ;; + -3|--3way) + threeway=t ;; + -s|--signoff) + sign=t ;; + -u|--utf8) + utf8=t ;; # this is now default + --no-utf8) + utf8= ;; + -m|--message-id) + messageid=t ;; + --no-message-id) + messageid=f ;; + -k|--keep) + keep=t ;; + --keep-non-patch) + keep=b ;; + -c|--scissors) + scissors=t ;; + --no-scissors) + scissors=f ;; + -r|--resolved|--continue) + resolved=t ;; + --skip) + skip=t ;; + --abort) + abort=t ;; + --rebasing) + rebasing=t threeway=t ;; + --resolvemsg=*) + resolvemsg="${1#--resolvemsg=}" ;; + --whitespace=*|--directory=*|--exclude=*|--include=*) + git_apply_opt="$git_apply_opt $(sq "$1")" ;; + -C*|-p*) + git_apply_opt="$git_apply_opt $(sq "$1")" ;; + --patch-format=*) + patch_format="${1#--patch-format=}" ;; + --reject|--ignore-whitespace|--ignore-space-change) + git_apply_opt="$git_apply_opt $1" ;; + --committer-date-is-author-date) + committer_date_is_author_date=t ;; + --ignore-date) + ignore_date=t ;; + --rerere-autoupdate|--no-rerere-autoupdate) + allow_rerere_autoupdate="$1" ;; + -q|--quiet) + GIT_QUIET=t ;; + --keep-cr) + keepcr=t ;; + --no-keep-cr) + keepcr=f ;; + --gpg-sign) + gpg_sign_opt=-S ;; + --gpg-sign=*) + gpg_sign_opt="-S${1#--gpg-sign=}" ;; + --) + shift; break ;; + *) + usage ;; + esac + shift +done + +# If the dotest directory exists, but we have finished applying all the +# patches in them, clear it out. +if test -d "$dotest" && + test -f "$dotest/last" && + test -f "$dotest/next" && + last=$(cat "$dotest/last") && + next=$(cat "$dotest/next") && + test $# != 0 && + test "$next" -gt "$last" +then + rm -fr "$dotest" +fi + +if test -d "$dotest" && test -f "$dotest/last" && test -f "$dotest/next" +then + case "$#,$skip$resolved$abort" in + 0,*t*) + # Explicit resume command and we do not have file, so + # we are happy. + : ;; + 0,) + # No file input but without resume parameters; catch + # user error to feed us a patch from standard input + # when there is already $dotest. This is somewhat + # unreliable -- stdin could be /dev/null for example + # and the caller did not intend to feed us a patch but + # wanted to continue unattended. + test -t 0 + ;; + *) + false + ;; + esac || + die "$(eval_gettext "previous rebase directory \$dotest still exists but mbox given.")" + resume=yes + + case "$skip,$abort" in + t,t) + die "$(gettext "Please make up your mind. --skip or --abort?")" + ;; + t,) + git rerere clear + head_tree=$(git rev-parse --verify -q HEAD || echo $empty_tree) && + git read-tree --reset -u $head_tree $head_tree && + index_tree=$(git write-tree) && + git read-tree -m -u $index_tree $head_tree + git read-tree -m $head_tree + ;; + ,t) + if test -f "$dotest/rebasing" + then + exec git rebase --abort + fi + git rerere clear + if safe_to_abort + then + head_tree=$(git rev-parse --verify -q HEAD || echo $empty_tree) && + git read-tree --reset -u $head_tree $head_tree && + index_tree=$(git write-tree) && + orig_head=$(git rev-parse --verify -q ORIG_HEAD || echo $empty_tree) && + git read-tree -m -u $index_tree $orig_head + if git rev-parse --verify -q ORIG_HEAD >/dev/null 2>&1 + then + git reset ORIG_HEAD + else + git read-tree $empty_tree + curr_branch=$(git symbolic-ref HEAD 2>/dev/null) && + git update-ref -d $curr_branch + fi + fi + rm -fr "$dotest" + exit ;; + esac + rm -f "$dotest/dirtyindex" +else + # Possible stray $dotest directory in the independent-run + # case; in the --rebasing case, it is upto the caller + # (git-rebase--am) to take care of stray directories. + if test -d "$dotest" && test -z "$rebasing" + then + case "$skip,$resolved,$abort" in + ,,t) + rm -fr "$dotest" + exit 0 + ;; + *) + die "$(eval_gettext "Stray \$dotest directory found. +Use \"git am --abort\" to remove it.")" + ;; + esac + fi + + # Make sure we are not given --skip, --continue, or --abort + test "$skip$resolved$abort" = "" || + die "$(gettext "Resolve operation not in progress, we are not resuming.")" + + # Start afresh. + mkdir -p "$dotest" || exit + + if test -n "$prefix" && test $# != 0 + then + first=t + for arg + do + test -n "$first" && { + set x + first= + } + if is_absolute_path "$arg" + then + set "$@" "$arg" + else + set "$@" "$prefix$arg" + fi + done + shift + fi + + check_patch_format "$@" + + split_patches "$@" + + # -i can and must be given when resuming; everything + # else is kept + echo " $git_apply_opt" >"$dotest/apply-opt" + echo "$threeway" >"$dotest/threeway" + echo "$sign" >"$dotest/sign" + echo "$utf8" >"$dotest/utf8" + echo "$keep" >"$dotest/keep" + echo "$messageid" >"$dotest/messageid" + echo "$scissors" >"$dotest/scissors" + echo "$no_inbody_headers" >"$dotest/no_inbody_headers" + echo "$GIT_QUIET" >"$dotest/quiet" + echo 1 >"$dotest/next" + if test -n "$rebasing" + then + : >"$dotest/rebasing" + else + : >"$dotest/applying" + if test -n "$HAS_HEAD" + then + git update-ref ORIG_HEAD HEAD + else + git update-ref -d ORIG_HEAD >/dev/null 2>&1 + fi + fi +fi + +git update-index -q --refresh + +case "$resolved" in +'') + case "$HAS_HEAD" in + '') + files=$(git ls-files) ;; + ?*) + files=$(git diff-index --cached --name-only HEAD --) ;; + esac || exit + if test "$files" + then + test -n "$HAS_HEAD" && : >"$dotest/dirtyindex" + die "$(eval_gettext "Dirty index: cannot apply patches (dirty: \$files)")" + fi +esac + +# Now, decide what command line options we will give to the git +# commands we invoke, based on the result of parsing command line +# options and previous invocation state stored in $dotest/ files. + +if test "$(cat "$dotest/utf8")" = t +then + utf8=-u +else + utf8=-n +fi +keep=$(cat "$dotest/keep") +case "$keep" in +t) + keep=-k ;; +b) + keep=-b ;; +*) + keep= ;; +esac +case "$(cat "$dotest/messageid")" in +t) + messageid=-m ;; +f) + messageid= ;; +esac +case "$(cat "$dotest/scissors")" in +t) + scissors=--scissors ;; +f) + scissors=--no-scissors ;; +esac +if test "$(cat "$dotest/no_inbody_headers")" = t +then + no_inbody_headers=--no-inbody-headers +else + no_inbody_headers= +fi +if test "$(cat "$dotest/quiet")" = t +then + GIT_QUIET=t +fi +if test "$(cat "$dotest/threeway")" = t +then + threeway=t +fi +git_apply_opt=$(cat "$dotest/apply-opt") +if test "$(cat "$dotest/sign")" = t +then + SIGNOFF=$(git var GIT_COMMITTER_IDENT | sed -e ' + s/>.*/>/ + s/^/Signed-off-by: /' + ) +else + SIGNOFF= +fi + +last=$(cat "$dotest/last") +this=$(cat "$dotest/next") +if test "$skip" = t +then + this=$(expr "$this" + 1) + resume= +fi + +while test "$this" -le "$last" +do + msgnum=$(printf "%0${prec}d" $this) + next=$(expr "$this" + 1) + test -f "$dotest/$msgnum" || { + resume= + go_next + continue + } + + # If we are not resuming, parse and extract the patch information + # into separate files: + # - info records the authorship and title + # - msg is the rest of commit log message + # - patch is the patch body. + # + # When we are resuming, these files are either already prepared + # by the user, or the user can tell us to do so by --continue flag. + case "$resume" in + '') + if test -f "$dotest/rebasing" + then + commit=$(sed -e 's/^From \([0-9a-f]*\) .*/\1/' \ + -e q "$dotest/$msgnum") && + test "$(git cat-file -t "$commit")" = commit || + stop_here $this + git cat-file commit "$commit" | + sed -e '1,/^$/d' >"$dotest/msg-clean" + echo "$commit" >"$dotest/original-commit" + get_author_ident_from_commit "$commit" >"$dotest/author-script" + git diff-tree --root --binary --full-index "$commit" >"$dotest/patch" + else + git mailinfo $keep $no_inbody_headers $messageid $scissors $utf8 "$dotest/msg" "$dotest/patch" \ + <"$dotest/$msgnum" >"$dotest/info" || + stop_here $this + + # skip pine's internal folder data + sane_grep '^Author: Mail System Internal Data$' \ + <"$dotest"/info >/dev/null && + go_next && continue + + test -s "$dotest/patch" || { + eval_gettextln "Patch is empty. Was it split wrong? +If you would prefer to skip this patch, instead run \"\$cmdline --skip\". +To restore the original branch and stop patching run \"\$cmdline --abort\"." + stop_here $this + } + rm -f "$dotest/original-commit" "$dotest/author-script" + { + sed -n '/^Subject/ s/Subject: //p' "$dotest/info" + echo + cat "$dotest/msg" + } | + git stripspace > "$dotest/msg-clean" + fi + ;; + esac + + if test -f "$dotest/author-script" + then + eval $(cat "$dotest/author-script") + else + GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' "$dotest/info")" + GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$dotest/info")" + GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$dotest/info")" + fi + + if test -z "$GIT_AUTHOR_EMAIL" + then + gettextln "Patch does not have a valid e-mail address." + stop_here $this + fi + + export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE + + case "$resume" in + '') + if test '' != "$SIGNOFF" + then + LAST_SIGNED_OFF_BY=$( + sed -ne '/^Signed-off-by: /p' \ + "$dotest/msg-clean" | + sed -ne '$p' + ) + ADD_SIGNOFF=$( + test "$LAST_SIGNED_OFF_BY" = "$SIGNOFF" || { + test '' = "$LAST_SIGNED_OFF_BY" && echo + echo "$SIGNOFF" + }) + else + ADD_SIGNOFF= + fi + { + if test -s "$dotest/msg-clean" + then + cat "$dotest/msg-clean" + fi + if test '' != "$ADD_SIGNOFF" + then + echo "$ADD_SIGNOFF" + fi + } >"$dotest/final-commit" + ;; + *) + case "$resolved$interactive" in + tt) + # This is used only for interactive view option. + git diff-index -p --cached HEAD -- >"$dotest/patch" + ;; + esac + esac + + resume= + if test "$interactive" = t + then + test -t 0 || + die "$(gettext "cannot be interactive without stdin connected to a terminal.")" + action=again + while test "$action" = again + do + gettextln "Commit Body is:" + echo "--------------------------" + cat "$dotest/final-commit" + echo "--------------------------" + # TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a] + # in your translation. The program will only accept English + # input at this point. + gettext "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all " + read reply + case "$reply" in + [yY]*) action=yes ;; + [aA]*) action=yes interactive= ;; + [nN]*) action=skip ;; + [eE]*) git_editor "$dotest/final-commit" + action=again ;; + [vV]*) action=again + git_pager "$dotest/patch" ;; + *) action=again ;; + esac + done + else + action=yes + fi + + if test $action = skip + then + go_next + continue + fi + + hook="$(git rev-parse --git-path hooks/applypatch-msg)" + if test -x "$hook" + then + "$hook" "$dotest/final-commit" || stop_here $this + fi + + if test -f "$dotest/final-commit" + then + FIRSTLINE=$(sed 1q "$dotest/final-commit") + else + FIRSTLINE="" + fi + + say "$(eval_gettext "Applying: \$FIRSTLINE")" + + case "$resolved" in + '') + # When we are allowed to fall back to 3-way later, don't give + # false errors during the initial attempt. + squelch= + if test "$threeway" = t + then + squelch='>/dev/null 2>&1 ' + fi + eval "git apply $squelch$git_apply_opt"' --index "$dotest/patch"' + apply_status=$? + ;; + t) + # Resolved means the user did all the hard work, and + # we do not have to do any patch application. Just + # trust what the user has in the index file and the + # working tree. + resolved= + git diff-index --quiet --cached HEAD -- && { + gettextln "No changes - did you forget to use 'git add'? +If there is nothing left to stage, chances are that something else +already introduced the same changes; you might want to skip this patch." + stop_here_user_resolve $this + } + unmerged=$(git ls-files -u) + if test -n "$unmerged" + then + gettextln "You still have unmerged paths in your index +did you forget to use 'git add'?" + stop_here_user_resolve $this + fi + apply_status=0 + git rerere + ;; + esac + + if test $apply_status != 0 && test "$threeway" = t + then + if (fall_back_3way) + then + # Applying the patch to an earlier tree and merging the + # result may have produced the same tree as ours. + git diff-index --quiet --cached HEAD -- && { + say "$(gettext "No changes -- Patch already applied.")" + go_next + continue + } + # clear apply_status -- we have successfully merged. + apply_status=0 + fi + fi + if test $apply_status != 0 + then + eval_gettextln 'Patch failed at $msgnum $FIRSTLINE' + if test "$(git config --bool advice.amworkdir)" != false + then + eval_gettextln 'The copy of the patch that failed is found in: + $dotest/patch' + fi + stop_here_user_resolve $this + fi + + hook="$(git rev-parse --git-path hooks/pre-applypatch)" + if test -x "$hook" + then + "$hook" || stop_here $this + fi + + tree=$(git write-tree) && + commit=$( + if test -n "$ignore_date" + then + GIT_AUTHOR_DATE= + fi + parent=$(git rev-parse --verify -q HEAD) || + say >&2 "$(gettext "applying to an empty history")" + + if test -n "$committer_date_is_author_date" + then + GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE" + export GIT_COMMITTER_DATE + fi && + git commit-tree ${parent:+-p} $parent ${gpg_sign_opt:+"$gpg_sign_opt"} $tree \ + <"$dotest/final-commit" + ) && + git update-ref -m "$GIT_REFLOG_ACTION: $FIRSTLINE" HEAD $commit $parent || + stop_here $this + + if test -f "$dotest/original-commit"; then + echo "$(cat "$dotest/original-commit") $commit" >> "$dotest/rewritten" + fi + + hook="$(git rev-parse --git-path hooks/post-applypatch)" + test -x "$hook" && "$hook" + + go_next +done + +if test -s "$dotest"/rewritten; then + git notes copy --for-rewrite=rebase < "$dotest"/rewritten + hook="$(git rev-parse --git-path hooks/post-rewrite)" + if test -x "$hook"; then + "$hook" rebase < "$dotest"/rewritten + fi +fi + +# If am was called with --rebasing (from git-rebase--am), it's up to +# the caller to take care of housekeeping. +if ! test -f "$dotest/rebasing" +then + rm -fr "$dotest" + git gc --auto +fi diff --git a/contrib/examples/git-commit.sh b/contrib/examples/git-commit.sh index 934505bab9..86c9cfa0c7 100755 --- a/contrib/examples/git-commit.sh +++ b/contrib/examples/git-commit.sh @@ -574,10 +574,10 @@ then if test "$templatefile" != "" then # Test whether this is just the unaltered template. - if cnt=`sed -e '/^#/d' < "$templatefile" | + if cnt=$(sed -e '/^#/d' < "$templatefile" | git stripspace | diff "$GIT_DIR"/COMMIT_BAREMSG - | - wc -l` && + wc -l) && test 0 -lt $cnt then have_commitmsg=t @@ -630,8 +630,8 @@ then fi if test -z "$quiet" then - commit=`git diff-tree --always --shortstat --pretty="format:%h: %s"\ - --abbrev --summary --root HEAD --` + commit=$(git diff-tree --always --shortstat --pretty="format:%h: %s"\ + --abbrev --summary --root HEAD --) echo "Created${initial_commit:+ initial} commit $commit" fi fi diff --git a/contrib/examples/git-difftool.perl b/contrib/examples/git-difftool.perl new file mode 100755 index 0000000000..df59bdfe97 --- /dev/null +++ b/contrib/examples/git-difftool.perl @@ -0,0 +1,481 @@ +#!/usr/bin/perl +# Copyright (c) 2009, 2010 David Aguilar +# Copyright (c) 2012 Tim Henigan +# +# This is a wrapper around the GIT_EXTERNAL_DIFF-compatible +# git-difftool--helper script. +# +# This script exports GIT_EXTERNAL_DIFF and GIT_PAGER for use by git. +# The GIT_DIFF* variables are exported for use by git-difftool--helper. +# +# Any arguments that are unknown to this script are forwarded to 'git diff'. + +use 5.008; +use strict; +use warnings; +use Error qw(:try); +use File::Basename qw(dirname); +use File::Copy; +use File::Find; +use File::stat; +use File::Path qw(mkpath rmtree); +use File::Temp qw(tempdir); +use Getopt::Long qw(:config pass_through); +use Git; +use Git::I18N; + +sub usage +{ + my $exitcode = shift; + print << 'USAGE'; +usage: git difftool [-t|--tool=<tool>] [--tool-help] + [-x|--extcmd=<cmd>] + [-g|--gui] [--no-gui] + [--prompt] [-y|--no-prompt] + [-d|--dir-diff] + ['git diff' options] +USAGE + exit($exitcode); +} + +sub print_tool_help +{ + # See the comment at the bottom of file_diff() for the reason behind + # using system() followed by exit() instead of exec(). + my $rc = system(qw(git mergetool --tool-help=diff)); + exit($rc | ($rc >> 8)); +} + +sub exit_cleanup +{ + my ($tmpdir, $status) = @_; + my $errno = $!; + rmtree($tmpdir); + if ($status and $errno) { + my ($package, $file, $line) = caller(); + warn "$file line $line: $errno\n"; + } + exit($status | ($status >> 8)); +} + +sub use_wt_file +{ + my ($file, $sha1) = @_; + my $null_sha1 = '0' x 40; + + if (-l $file || ! -e _) { + return (0, $null_sha1); + } + + my $wt_sha1 = Git::command_oneline('hash-object', $file); + my $use = ($sha1 eq $null_sha1) || ($sha1 eq $wt_sha1); + return ($use, $wt_sha1); +} + +sub changed_files +{ + my ($repo_path, $index, $worktree) = @_; + $ENV{GIT_INDEX_FILE} = $index; + + my @gitargs = ('--git-dir', $repo_path, '--work-tree', $worktree); + my @refreshargs = ( + @gitargs, 'update-index', + '--really-refresh', '-q', '--unmerged'); + try { + Git::command_oneline(@refreshargs); + } catch Git::Error::Command with {}; + + my @diffargs = (@gitargs, 'diff-files', '--name-only', '-z'); + my $line = Git::command_oneline(@diffargs); + my @files; + if (defined $line) { + @files = split('\0', $line); + } else { + @files = (); + } + + delete($ENV{GIT_INDEX_FILE}); + + return map { $_ => 1 } @files; +} + +sub setup_dir_diff +{ + my ($worktree, $symlinks) = @_; + my @gitargs = ('diff', '--raw', '--no-abbrev', '-z', @ARGV); + my $diffrtn = Git::command_oneline(@gitargs); + exit(0) unless defined($diffrtn); + + # Go to the root of the worktree now that we've captured the list of + # changed files. The paths returned by diff --raw are relative to the + # top-level of the repository, but we defer changing directories so + # that @ARGV can perform pathspec limiting in the current directory. + chdir($worktree); + + # Build index info for left and right sides of the diff + my $submodule_mode = '160000'; + my $symlink_mode = '120000'; + my $null_mode = '0' x 6; + my $null_sha1 = '0' x 40; + my $lindex = ''; + my $rindex = ''; + my $wtindex = ''; + my %submodule; + my %symlink; + my @files = (); + my %working_tree_dups = (); + my @rawdiff = split('\0', $diffrtn); + + my $i = 0; + while ($i < $#rawdiff) { + if ($rawdiff[$i] =~ /^::/) { + warn __ <<'EOF'; +Combined diff formats ('-c' and '--cc') are not supported in +directory diff mode ('-d' and '--dir-diff'). +EOF + exit(1); + } + + my ($lmode, $rmode, $lsha1, $rsha1, $status) = + split(' ', substr($rawdiff[$i], 1)); + my $src_path = $rawdiff[$i + 1]; + my $dst_path; + + if ($status =~ /^[CR]/) { + $dst_path = $rawdiff[$i + 2]; + $i += 3; + } else { + $dst_path = $src_path; + $i += 2; + } + + if ($lmode eq $submodule_mode or $rmode eq $submodule_mode) { + $submodule{$src_path}{left} = $lsha1; + if ($lsha1 ne $rsha1) { + $submodule{$dst_path}{right} = $rsha1; + } else { + $submodule{$dst_path}{right} = "$rsha1-dirty"; + } + next; + } + + if ($lmode eq $symlink_mode) { + $symlink{$src_path}{left} = + Git::command_oneline('show', $lsha1); + } + + if ($rmode eq $symlink_mode) { + $symlink{$dst_path}{right} = + Git::command_oneline('show', $rsha1); + } + + if ($lmode ne $null_mode and $status !~ /^C/) { + $lindex .= "$lmode $lsha1\t$src_path\0"; + } + + if ($rmode ne $null_mode) { + # Avoid duplicate entries + if ($working_tree_dups{$dst_path}++) { + next; + } + my ($use, $wt_sha1) = + use_wt_file($dst_path, $rsha1); + if ($use) { + push @files, $dst_path; + $wtindex .= "$rmode $wt_sha1\t$dst_path\0"; + } else { + $rindex .= "$rmode $rsha1\t$dst_path\0"; + } + } + } + + # Go to the root of the worktree so that the left index files + # are properly setup -- the index is toplevel-relative. + chdir($worktree); + + # Setup temp directories + my $tmpdir = tempdir('git-difftool.XXXXX', CLEANUP => 0, TMPDIR => 1); + my $ldir = "$tmpdir/left"; + my $rdir = "$tmpdir/right"; + mkpath($ldir) or exit_cleanup($tmpdir, 1); + mkpath($rdir) or exit_cleanup($tmpdir, 1); + + # Populate the left and right directories based on each index file + my ($inpipe, $ctx); + $ENV{GIT_INDEX_FILE} = "$tmpdir/lindex"; + ($inpipe, $ctx) = + Git::command_input_pipe('update-index', '-z', '--index-info'); + print($inpipe $lindex); + Git::command_close_pipe($inpipe, $ctx); + + my $rc = system('git', 'checkout-index', '--all', "--prefix=$ldir/"); + exit_cleanup($tmpdir, $rc) if $rc != 0; + + $ENV{GIT_INDEX_FILE} = "$tmpdir/rindex"; + ($inpipe, $ctx) = + Git::command_input_pipe('update-index', '-z', '--index-info'); + print($inpipe $rindex); + Git::command_close_pipe($inpipe, $ctx); + + $rc = system('git', 'checkout-index', '--all', "--prefix=$rdir/"); + exit_cleanup($tmpdir, $rc) if $rc != 0; + + $ENV{GIT_INDEX_FILE} = "$tmpdir/wtindex"; + ($inpipe, $ctx) = + Git::command_input_pipe('update-index', '--info-only', '-z', '--index-info'); + print($inpipe $wtindex); + Git::command_close_pipe($inpipe, $ctx); + + # If $GIT_DIR was explicitly set just for the update/checkout + # commands, then it should be unset before continuing. + delete($ENV{GIT_INDEX_FILE}); + + # Changes in the working tree need special treatment since they are + # not part of the index. + for my $file (@files) { + my $dir = dirname($file); + unless (-d "$rdir/$dir") { + mkpath("$rdir/$dir") or + exit_cleanup($tmpdir, 1); + } + if ($symlinks) { + symlink("$worktree/$file", "$rdir/$file") or + exit_cleanup($tmpdir, 1); + } else { + copy($file, "$rdir/$file") or + exit_cleanup($tmpdir, 1); + + my $mode = stat($file)->mode; + chmod($mode, "$rdir/$file") or + exit_cleanup($tmpdir, 1); + } + } + + # Changes to submodules require special treatment. This loop writes a + # temporary file to both the left and right directories to show the + # change in the recorded SHA1 for the submodule. + for my $path (keys %submodule) { + my $ok = 0; + if (defined($submodule{$path}{left})) { + $ok = write_to_file("$ldir/$path", + "Subproject commit $submodule{$path}{left}"); + } + if (defined($submodule{$path}{right})) { + $ok = write_to_file("$rdir/$path", + "Subproject commit $submodule{$path}{right}"); + } + exit_cleanup($tmpdir, 1) if not $ok; + } + + # Symbolic links require special treatment. The standard "git diff" + # shows only the link itself, not the contents of the link target. + # This loop replicates that behavior. + for my $path (keys %symlink) { + my $ok = 0; + if (defined($symlink{$path}{left})) { + $ok = write_to_file("$ldir/$path", + $symlink{$path}{left}); + } + if (defined($symlink{$path}{right})) { + $ok = write_to_file("$rdir/$path", + $symlink{$path}{right}); + } + exit_cleanup($tmpdir, 1) if not $ok; + } + + return ($ldir, $rdir, $tmpdir, @files); +} + +sub write_to_file +{ + my $path = shift; + my $value = shift; + + # Make sure the path to the file exists + my $dir = dirname($path); + unless (-d "$dir") { + mkpath("$dir") or return 0; + } + + # If the file already exists in that location, delete it. This + # is required in the case of symbolic links. + unlink($path); + + open(my $fh, '>', $path) or return 0; + print($fh $value); + close($fh); + + return 1; +} + +sub main +{ + # parse command-line options. all unrecognized options and arguments + # are passed through to the 'git diff' command. + my %opts = ( + difftool_cmd => undef, + dirdiff => undef, + extcmd => undef, + gui => undef, + help => undef, + prompt => undef, + symlinks => $^O ne 'cygwin' && + $^O ne 'MSWin32' && $^O ne 'msys', + tool_help => undef, + trust_exit_code => undef, + ); + GetOptions('g|gui!' => \$opts{gui}, + 'd|dir-diff' => \$opts{dirdiff}, + 'h' => \$opts{help}, + 'prompt!' => \$opts{prompt}, + 'y' => sub { $opts{prompt} = 0; }, + 'symlinks' => \$opts{symlinks}, + 'no-symlinks' => sub { $opts{symlinks} = 0; }, + 't|tool:s' => \$opts{difftool_cmd}, + 'tool-help' => \$opts{tool_help}, + 'trust-exit-code' => \$opts{trust_exit_code}, + 'no-trust-exit-code' => sub { $opts{trust_exit_code} = 0; }, + 'x|extcmd:s' => \$opts{extcmd}); + + if (defined($opts{help})) { + usage(0); + } + if (defined($opts{tool_help})) { + print_tool_help(); + } + if (defined($opts{difftool_cmd})) { + if (length($opts{difftool_cmd}) > 0) { + $ENV{GIT_DIFF_TOOL} = $opts{difftool_cmd}; + } else { + print __("No <tool> given for --tool=<tool>\n"); + usage(1); + } + } + if (defined($opts{extcmd})) { + if (length($opts{extcmd}) > 0) { + $ENV{GIT_DIFFTOOL_EXTCMD} = $opts{extcmd}; + } else { + print __("No <cmd> given for --extcmd=<cmd>\n"); + usage(1); + } + } + if ($opts{gui}) { + my $guitool = Git::config('diff.guitool'); + if (defined($guitool) && length($guitool) > 0) { + $ENV{GIT_DIFF_TOOL} = $guitool; + } + } + + if (!defined $opts{trust_exit_code}) { + $opts{trust_exit_code} = Git::config_bool('difftool.trustExitCode'); + } + if ($opts{trust_exit_code}) { + $ENV{GIT_DIFFTOOL_TRUST_EXIT_CODE} = 'true'; + } else { + $ENV{GIT_DIFFTOOL_TRUST_EXIT_CODE} = 'false'; + } + + # In directory diff mode, 'git-difftool--helper' is called once + # to compare the a/b directories. In file diff mode, 'git diff' + # will invoke a separate instance of 'git-difftool--helper' for + # each file that changed. + if (defined($opts{dirdiff})) { + dir_diff($opts{extcmd}, $opts{symlinks}); + } else { + file_diff($opts{prompt}); + } +} + +sub dir_diff +{ + my ($extcmd, $symlinks) = @_; + my $rc; + my $error = 0; + my $repo = Git->repository(); + my $repo_path = $repo->repo_path(); + my $worktree = $repo->wc_path(); + $worktree =~ s|/$||; # Avoid double slashes in symlink targets + my ($a, $b, $tmpdir, @files) = setup_dir_diff($worktree, $symlinks); + + if (defined($extcmd)) { + $rc = system($extcmd, $a, $b); + } else { + $ENV{GIT_DIFFTOOL_DIRDIFF} = 'true'; + $rc = system('git', 'difftool--helper', $a, $b); + } + # If the diff including working copy files and those + # files were modified during the diff, then the changes + # should be copied back to the working tree. + # Do not copy back files when symlinks are used and the + # external tool did not replace the original link with a file. + # + # These hashes are loaded lazily since they aren't needed + # in the common case of --symlinks and the difftool updating + # files through the symlink. + my %wt_modified; + my %tmp_modified; + my $indices_loaded = 0; + + for my $file (@files) { + next if $symlinks && -l "$b/$file"; + next if ! -f "$b/$file"; + + if (!$indices_loaded) { + %wt_modified = changed_files( + $repo_path, "$tmpdir/wtindex", $worktree); + %tmp_modified = changed_files( + $repo_path, "$tmpdir/wtindex", $b); + $indices_loaded = 1; + } + + if (exists $wt_modified{$file} and exists $tmp_modified{$file}) { + warn sprintf(__( + "warning: Both files modified:\n" . + "'%s/%s' and '%s/%s'.\n" . + "warning: Working tree file has been left.\n" . + "warning:\n"), $worktree, $file, $b, $file); + $error = 1; + } elsif (exists $tmp_modified{$file}) { + my $mode = stat("$b/$file")->mode; + copy("$b/$file", $file) or + exit_cleanup($tmpdir, 1); + + chmod($mode, $file) or + exit_cleanup($tmpdir, 1); + } + } + if ($error) { + warn sprintf(__( + "warning: Temporary files exist in '%s'.\n" . + "warning: You may want to cleanup or recover these.\n"), $tmpdir); + exit(1); + } else { + exit_cleanup($tmpdir, $rc); + } +} + +sub file_diff +{ + my ($prompt) = @_; + + if (defined($prompt)) { + if ($prompt) { + $ENV{GIT_DIFFTOOL_PROMPT} = 'true'; + } else { + $ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true'; + } + } + + $ENV{GIT_PAGER} = ''; + $ENV{GIT_EXTERNAL_DIFF} = 'git-difftool--helper'; + + # ActiveState Perl for Win32 does not implement POSIX semantics of + # exec* system call. It just spawns the given executable and finishes + # the starting program, exiting with code 0. + # system will at least catch the errors returned by git diff, + # allowing the caller of git difftool better handling of failures. + my $rc = system('git', 'diff', @ARGV); + exit($rc | ($rc >> 8)); +} + +main(); diff --git a/contrib/examples/git-fetch.sh b/contrib/examples/git-fetch.sh index 554070909c..57d2e5616f 100755 --- a/contrib/examples/git-fetch.sh +++ b/contrib/examples/git-fetch.sh @@ -146,13 +146,13 @@ esac reflist=$(get_remote_refs_for_fetch "$@") if test "$tags" then - taglist=`IFS=' ' && + taglist=$(IFS=' ' && echo "$ls_remote_result" | git show-ref --exclude-existing=refs/tags/ | while read sha1 name do echo ".${name}:${name}" - done` || exit + done) || exit if test "$#" -gt 1 then # remote URL plus explicit refspecs; we need to merge them. diff --git a/contrib/examples/git-merge.sh b/contrib/examples/git-merge.sh index 52f2aafb9d..ee99f1a4ee 100755 --- a/contrib/examples/git-merge.sh +++ b/contrib/examples/git-merge.sh @@ -523,10 +523,10 @@ do if test "$exit" -eq 1 then - cnt=`{ + cnt=$({ git diff-files --name-only git ls-files --unmerged - } | wc -l` + } | wc -l) if test $best_cnt -le 0 || test $cnt -le $best_cnt then best_strategy=$strategy diff --git a/contrib/examples/git-pull.sh b/contrib/examples/git-pull.sh new file mode 100755 index 0000000000..6b3a03f9b0 --- /dev/null +++ b/contrib/examples/git-pull.sh @@ -0,0 +1,381 @@ +#!/bin/sh +# +# Copyright (c) 2005 Junio C Hamano +# +# Fetch one or more remote refs and merge it/them into the current HEAD. + +SUBDIRECTORY_OK=Yes +OPTIONS_KEEPDASHDASH= +OPTIONS_STUCKLONG=Yes +OPTIONS_SPEC="\ +git pull [options] [<repository> [<refspec>...]] + +Fetch one or more remote refs and integrate it/them with the current HEAD. +-- +v,verbose be more verbose +q,quiet be more quiet +progress force progress reporting + + Options related to merging +r,rebase?false|true|preserve incorporate changes by rebasing rather than merging +n! do not show a diffstat at the end of the merge +stat show a diffstat at the end of the merge +summary (synonym to --stat) +log?n add (at most <n>) entries from shortlog to merge commit message +squash create a single commit instead of doing a merge +commit perform a commit if the merge succeeds (default) +e,edit edit message before committing +ff allow fast-forward +ff-only! abort if fast-forward is not possible +verify-signatures verify that the named commit has a valid GPG signature +s,strategy=strategy merge strategy to use +X,strategy-option=option option for selected merge strategy +S,gpg-sign?key-id GPG sign commit + + Options related to fetching +all fetch from all remotes +a,append append to .git/FETCH_HEAD instead of overwriting +upload-pack=path path to upload pack on remote end +f,force force overwrite of local branch +t,tags fetch all tags and associated objects +p,prune prune remote-tracking branches no longer on remote +recurse-submodules?on-demand control recursive fetching of submodules +dry-run dry run +k,keep keep downloaded pack +depth=depth deepen history of shallow clone +unshallow convert to a complete repository +update-shallow accept refs that update .git/shallow +refmap=refmap specify fetch refmap +" +test $# -gt 0 && args="$*" +. git-sh-setup +. git-sh-i18n +set_reflog_action "pull${args+ $args}" +require_work_tree_exists +cd_to_toplevel + + +die_conflict () { + git diff-index --cached --name-status -r --ignore-submodules HEAD -- + if [ $(git config --bool --get advice.resolveConflict || echo true) = "true" ]; then + die "$(gettext "Pull is not possible because you have unmerged files. +Please, fix them up in the work tree, and then use 'git add/rm <file>' +as appropriate to mark resolution and make a commit.")" + else + die "$(gettext "Pull is not possible because you have unmerged files.")" + fi +} + +die_merge () { + if [ $(git config --bool --get advice.resolveConflict || echo true) = "true" ]; then + die "$(gettext "You have not concluded your merge (MERGE_HEAD exists). +Please, commit your changes before merging.")" + else + die "$(gettext "You have not concluded your merge (MERGE_HEAD exists).")" + fi +} + +test -z "$(git ls-files -u)" || die_conflict +test -f "$GIT_DIR/MERGE_HEAD" && die_merge + +bool_or_string_config () { + git config --bool "$1" 2>/dev/null || git config "$1" +} + +strategy_args= diffstat= no_commit= squash= no_ff= ff_only= +log_arg= verbosity= progress= recurse_submodules= verify_signatures= +merge_args= edit= rebase_args= all= append= upload_pack= force= tags= prune= +keep= depth= unshallow= update_shallow= refmap= +curr_branch=$(git symbolic-ref -q HEAD) +curr_branch_short="${curr_branch#refs/heads/}" +rebase=$(bool_or_string_config branch.$curr_branch_short.rebase) +if test -z "$rebase" +then + rebase=$(bool_or_string_config pull.rebase) +fi + +# Setup default fast-forward options via `pull.ff` +pull_ff=$(bool_or_string_config pull.ff) +case "$pull_ff" in +true) + no_ff=--ff + ;; +false) + no_ff=--no-ff + ;; +only) + ff_only=--ff-only + ;; +esac + + +dry_run= +while : +do + case "$1" in + -q|--quiet) + verbosity="$verbosity -q" ;; + -v|--verbose) + verbosity="$verbosity -v" ;; + --progress) + progress=--progress ;; + --no-progress) + progress=--no-progress ;; + -n|--no-stat|--no-summary) + diffstat=--no-stat ;; + --stat|--summary) + diffstat=--stat ;; + --log|--log=*|--no-log) + log_arg="$1" ;; + --no-commit) + no_commit=--no-commit ;; + --commit) + no_commit=--commit ;; + -e|--edit) + edit=--edit ;; + --no-edit) + edit=--no-edit ;; + --squash) + squash=--squash ;; + --no-squash) + squash=--no-squash ;; + --ff) + no_ff=--ff ;; + --no-ff) + no_ff=--no-ff ;; + --ff-only) + ff_only=--ff-only ;; + -s*|--strategy=*) + strategy_args="$strategy_args $1" + ;; + -X*|--strategy-option=*) + merge_args="$merge_args $(git rev-parse --sq-quote "$1")" + ;; + -r*|--rebase=*) + rebase="${1#*=}" + ;; + --rebase) + rebase=true + ;; + --no-rebase) + rebase=false + ;; + --recurse-submodules) + recurse_submodules=--recurse-submodules + ;; + --recurse-submodules=*) + recurse_submodules="$1" + ;; + --no-recurse-submodules) + recurse_submodules=--no-recurse-submodules + ;; + --verify-signatures) + verify_signatures=--verify-signatures + ;; + --no-verify-signatures) + verify_signatures=--no-verify-signatures + ;; + --gpg-sign|-S) + gpg_sign_args=-S + ;; + --gpg-sign=*) + gpg_sign_args=$(git rev-parse --sq-quote "-S${1#--gpg-sign=}") + ;; + -S*) + gpg_sign_args=$(git rev-parse --sq-quote "$1") + ;; + --dry-run) + dry_run=--dry-run + ;; + --all|--no-all) + all=$1 ;; + -a|--append|--no-append) + append=$1 ;; + --upload-pack=*|--no-upload-pack) + upload_pack=$1 ;; + -f|--force|--no-force) + force="$force $1" ;; + -t|--tags|--no-tags) + tags=$1 ;; + -p|--prune|--no-prune) + prune=$1 ;; + -k|--keep|--no-keep) + keep=$1 ;; + --depth=*|--no-depth) + depth=$1 ;; + --unshallow|--no-unshallow) + unshallow=$1 ;; + --update-shallow|--no-update-shallow) + update_shallow=$1 ;; + --refmap=*|--no-refmap) + refmap=$1 ;; + -h|--help-all) + usage + ;; + --) + shift + break + ;; + *) + usage + ;; + esac + shift +done + +case "$rebase" in +preserve) + rebase=true + rebase_args=--preserve-merges + ;; +true|false|'') + ;; +*) + echo "Invalid value for --rebase, should be true, false, or preserve" + usage + exit 1 + ;; +esac + +error_on_no_merge_candidates () { + exec >&2 + + if test true = "$rebase" + then + op_type=rebase + op_prep=against + else + op_type=merge + op_prep=with + fi + + upstream=$(git config "branch.$curr_branch_short.merge") + remote=$(git config "branch.$curr_branch_short.remote") + + if [ $# -gt 1 ]; then + if [ "$rebase" = true ]; then + printf "There is no candidate for rebasing against " + else + printf "There are no candidates for merging " + fi + echo "among the refs that you just fetched." + echo "Generally this means that you provided a wildcard refspec which had no" + echo "matches on the remote end." + elif [ $# -gt 0 ] && [ "$1" != "$remote" ]; then + echo "You asked to pull from the remote '$1', but did not specify" + echo "a branch. Because this is not the default configured remote" + echo "for your current branch, you must specify a branch on the command line." + elif [ -z "$curr_branch" -o -z "$upstream" ]; then + . git-parse-remote + error_on_missing_default_upstream "pull" $op_type $op_prep \ + "git pull <remote> <branch>" + else + echo "Your configuration specifies to $op_type $op_prep the ref '${upstream#refs/heads/}'" + echo "from the remote, but no such ref was fetched." + fi + exit 1 +} + +test true = "$rebase" && { + if ! git rev-parse -q --verify HEAD >/dev/null + then + # On an unborn branch + if test -f "$(git rev-parse --git-path index)" + then + die "$(gettext "updating an unborn branch with changes added to the index")" + fi + else + require_clean_work_tree "pull with rebase" "Please commit or stash them." + fi + oldremoteref= && + test -n "$curr_branch" && + . git-parse-remote && + remoteref="$(get_remote_merge_branch "$@" 2>/dev/null)" && + oldremoteref=$(git merge-base --fork-point "$remoteref" $curr_branch 2>/dev/null) +} +orig_head=$(git rev-parse -q --verify HEAD) +git fetch $verbosity $progress $dry_run $recurse_submodules $all $append \ +${upload_pack:+"$upload_pack"} $force $tags $prune $keep $depth $unshallow $update_shallow \ +$refmap --update-head-ok "$@" || exit 1 +test -z "$dry_run" || exit 0 + +curr_head=$(git rev-parse -q --verify HEAD) +if test -n "$orig_head" && test "$curr_head" != "$orig_head" +then + # The fetch involved updating the current branch. + + # The working tree and the index file is still based on the + # $orig_head commit, but we are merging into $curr_head. + # First update the working tree to match $curr_head. + + eval_gettextln "Warning: fetch updated the current branch head. +Warning: fast-forwarding your working tree from +Warning: commit \$orig_head." >&2 + git update-index -q --refresh + git read-tree -u -m "$orig_head" "$curr_head" || + die "$(eval_gettext "Cannot fast-forward your working tree. +After making sure that you saved anything precious from +$ git diff \$orig_head +output, run +$ git reset --hard +to recover.")" + +fi + +merge_head=$(sed -e '/ not-for-merge /d' \ + -e 's/ .*//' "$GIT_DIR"/FETCH_HEAD | \ + tr '\012' ' ') + +case "$merge_head" in +'') + error_on_no_merge_candidates "$@" + ;; +?*' '?*) + if test -z "$orig_head" + then + die "$(gettext "Cannot merge multiple branches into empty head")" + fi + if test true = "$rebase" + then + die "$(gettext "Cannot rebase onto multiple branches")" + fi + ;; +esac + +# Pulling into unborn branch: a shorthand for branching off +# FETCH_HEAD, for lazy typers. +if test -z "$orig_head" +then + # Two-way merge: we claim the index is based on an empty tree, + # and try to fast-forward to HEAD. This ensures we will not + # lose index/worktree changes that the user already made on + # the unborn branch. + empty_tree=4b825dc642cb6eb9a060e54bf8d69288fbee4904 + git read-tree -m -u $empty_tree $merge_head && + git update-ref -m "initial pull" HEAD $merge_head "$curr_head" + exit +fi + +if test true = "$rebase" +then + o=$(git show-branch --merge-base $curr_branch $merge_head $oldremoteref) + if test "$oldremoteref" = "$o" + then + unset oldremoteref + fi +fi + +case "$rebase" in +true) + eval="git-rebase $diffstat $strategy_args $merge_args $rebase_args $verbosity" + eval="$eval $gpg_sign_args" + eval="$eval --onto $merge_head ${oldremoteref:-$merge_head}" + ;; +*) + eval="git-merge $diffstat $no_commit $verify_signatures $edit $squash $no_ff $ff_only" + eval="$eval $log_arg $strategy_args $merge_args $verbosity $progress" + eval="$eval $gpg_sign_args" + eval="$eval FETCH_HEAD" + ;; +esac +eval "exec $eval" diff --git a/contrib/examples/git-repack.sh b/contrib/examples/git-repack.sh index 96e3fed326..672af93443 100755 --- a/contrib/examples/git-repack.sh +++ b/contrib/examples/git-repack.sh @@ -67,8 +67,8 @@ case ",$all_into_one," in ,t,) args= existing= if [ -d "$PACKDIR" ]; then - for e in `cd "$PACKDIR" && find . -type f -name '*.pack' \ - | sed -e 's/^\.\///' -e 's/\.pack$//'` + for e in $(cd "$PACKDIR" && find . -type f -name '*.pack' \ + | sed -e 's/^\.\///' -e 's/\.pack$//') do if [ -e "$PACKDIR/$e.keep" ]; then : keep diff --git a/contrib/examples/git-revert.sh b/contrib/examples/git-revert.sh index 7e2aad5491..197838d10b 100755 --- a/contrib/examples/git-revert.sh +++ b/contrib/examples/git-revert.sh @@ -138,8 +138,8 @@ cherry-pick) }' logmsg=$(git show -s --pretty=raw --encoding="$encoding" "$commit") - set_author_env=`echo "$logmsg" | - LANG=C LC_ALL=C sed -ne "$pick_author_script"` + set_author_env=$(echo "$logmsg" | + LANG=C LC_ALL=C sed -ne "$pick_author_script") eval "$set_author_env" export GIT_AUTHOR_NAME export GIT_AUTHOR_EMAIL @@ -160,9 +160,9 @@ cherry-pick) esac >.msg eval GITHEAD_$head=HEAD -eval GITHEAD_$next='`git show -s \ +eval GITHEAD_$next='$(git show -s \ --pretty=oneline --encoding="$encoding" "$commit" | - sed -e "s/^[^ ]* //"`' + sed -e "s/^[^ ]* //")' export GITHEAD_$head GITHEAD_$next # This three way merge is an interesting one. We are at |