# This shell scriplet is meant to be included by other shell scripts # to set up some variables pointing at the normal git directories and # a few helper shell functions. # Source git-sh-i18n for gettext support. source git-sh-i18n # Having this variable in your environment would break scripts because # you would cause "cd" to be taken to unexpected places. If you # like CDPATH, define it for your interactive shell sessions without # exporting it. # But we protect ourselves from such a user mistake nevertheless. unset CDPATH # Similarly for IFS, but some shells (e.g. FreeBSD 7.2) are buggy and # do not equate an unset IFS with IFS with the default, so here is # an explicit SP HT LF. setglobal IFS = '' '' proc git_broken_path_fix { match ":$PATH:" { with *:$1:* : ok with * setglobal PATH = $[ setglobal SANE_TOOL_PATH = $1 setglobal IFS = ':', path = '', sep = '' set x $PATH shiftfor elem in @Argv { match "$SANE_TOOL_PATH:$elem" { with (?*:/bin | ?*:/usr/bin setglobal path = ""$path$sep$SANE_TOOL_PATH"" setglobal sep = ':' setglobal SANE_TOOL_PATH = '' } setglobal path = ""$path$sep$elem"" setglobal sep = ':' } echo $path] } } # @@BROKEN_PATH_FIX@@ proc die { die_with_status 1 @Argv } proc die_with_status { setglobal status = $1 shift printf >&2 '%s\n' "$ifsjoin(Argv)> !2 '%s\n' "$*" exit "$status" } setglobal GIT_QUIET = '' proc say { if test -z $GIT_QUIET { printf '%s\n' "$ifsjoin(Argv)" } } if test -n $OPTIONS_SPEC { proc usage { $0 -h exit 1 } setglobal parseopt_extra = '' test -n $OPTIONS_KEEPDASHDASH && setglobal parseopt_extra = '"--keep-dashdash'" test -n $OPTIONS_STUCKLONG && setglobal parseopt_extra = ""$parseopt_extra --stuck-long"" eval $[ echo $OPTIONS_SPEC | git rev-parse --parseopt $parseopt_extra -- @Argv || echo exit $Status] } else { setglobal dashless = $[basename -- $0 | sed -e 's/-/ /] proc usage { die $[eval_gettext "usage: \$dashless \$USAGE] } if test -z $LONG_USAGE { setglobal LONG_USAGE = $[eval_gettext "usage: \$dashless \$USAGE] } else { setglobal LONG_USAGE = $[eval_gettext "usage: \$dashless \$USAGE $LONG_USAGE] } match $1 { with -h echo $LONG_USAGE exit } } # Set the name of the end-user facing command in the reflog when the # script may update refs. When GIT_REFLOG_ACTION is already set, this # will not overwrite it, so that a scripted Porcelain (e.g. "git # rebase") can set it to its own name (e.g. "rebase") and then call # another scripted Porcelain (e.g. "git am") and a call to this # function in the latter will keep the name of the end-user facing # program (e.g. "rebase") in GIT_REFLOG_ACTION, ensuring whatever it # does will be record as actions done as part of the end-user facing # operation (e.g. "rebase"). # # NOTE NOTE NOTE: consequently, after assigning a specific message to # GIT_REFLOG_ACTION when calling a "git" command to record a custom # reflog message, do not leave that custom value in GIT_REFLOG_ACTION, # after you are done. Other callers of "git" commands that rely on # writing the default "program name" in reflog expect the variable to # contain the value set by this function. # # To use a custom reflog message, do either one of these three: # # (a) use a single-shot export form: # GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: preparing frotz" \ # git command-that-updates-a-ref # # (b) save the original away and restore: # SAVED_ACTION=$GIT_REFLOG_ACTION # GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: preparing frotz" # git command-that-updates-a-ref # GIT_REFLOG_ACITON=$SAVED_ACTION # # (c) assign the variable in a subshell: # ( # GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: preparing frotz" # git command-that-updates-a-ref # ) proc set_reflog_action { if test -z $(GIT_REFLOG_ACTION:+set) { setglobal GIT_REFLOG_ACTION = "$ifsjoin(Argv)" export GIT_REFLOG_ACTION } } proc git_editor { if test -z $(GIT_EDITOR:+set) { setglobal GIT_EDITOR = $[git var GIT_EDITOR] || return $? } eval $GIT_EDITOR '"$@"' } proc git_pager { if test -t 1 { setglobal GIT_PAGER = $[git var GIT_PAGER] } else { setglobal GIT_PAGER = 'cat' } for vardef in [@@PAGER_ENV@@] { setglobal var = $(vardef%%=*) eval ": \"\${$vardef}\" && export $var" } eval $GIT_PAGER '"$@"' } proc sane_grep { env GREP_OPTIONS= LC_ALL=C grep @@SANE_TEXT_GREP@@ @Argv } proc sane_egrep { env GREP_OPTIONS= LC_ALL=C egrep @@SANE_TEXT_GREP@@ @Argv } proc is_bare_repository { git rev-parse --is-bare-repository } proc cd_to_toplevel { setglobal cdup = $[git rev-parse --show-toplevel] && cd $cdup || do { gettextln "Cannot chdir to \$cdup, the toplevel of the working tree" > !2 exit 1 } } proc require_work_tree_exists { if test "z$[git rev-parse --is-bare-repository]" != zfalse { setglobal program_name = $0 die $[gettext "fatal: \$program_name cannot be used without a working tree.] } } proc require_work_tree { test $[git rev-parse --is-inside-work-tree !2 >/dev/null] = true || do { setglobal program_name = $0 die $[gettext "fatal: \$program_name cannot be used without a working tree.] } } proc require_clean_work_tree { git rev-parse --verify HEAD >/dev/null || exit 1 git update-index -q --ignore-submodules --refresh setglobal err = '0' if ! git diff-files --quiet --ignore-submodules { setglobal action = $1 match $action { with rebase gettextln "Cannot rebase: You have unstaged changes." > !2 with "rewrite branches" gettextln "Cannot rewrite branches: You have unstaged changes." > !2 with "pull with rebase" gettextln "Cannot pull with rebase: You have unstaged changes." > !2 with * eval_gettextln "Cannot \$action: You have unstaged changes." > !2 } setglobal err = '1' } if ! git diff-index --cached --quiet --ignore-submodules HEAD -- { if test $err = 0 { setglobal action = $1 match $action { with rebase gettextln "Cannot rebase: Your index contains uncommitted changes." > !2 with "pull with rebase" gettextln "Cannot pull with rebase: Your index contains uncommitted changes." > !2 with * eval_gettextln "Cannot \$action: Your index contains uncommitted changes." > !2 } } else { gettextln "Additionally, your index contains uncommitted changes." > !2 } setglobal err = '1' } if test $err = 1 { test -n $2 && echo $2 > !2 exit 1 } } # Generate a sed script to parse identities from a commit. # # Reads the commit from stdin, which should be in raw format (e.g., from # cat-file or "--pretty=raw"). # # The first argument specifies the ident line to parse (e.g., "author"), and # the second specifies the environment variable to put it in (e.g., "AUTHOR" # for "GIT_AUTHOR_*"). Multiple pairs can be given to parse author and # committer. proc pick_ident_script { while test $# -gt 0 { setglobal lid = $1; shift setglobal uid = $1; shift printf '%s' " /^$lid /{ s/'/'\\\\''/g h s/^$lid "'\([^<]*\) <[^>]*> .*$/\1/'" s/.*/GIT_$(uid)_NAME='&'/p g s/^$lid "'[^<]* <\([^>]*\)> .*$/\1/'" s/.*/GIT_$(uid)_EMAIL='&'/p g s/^$lid "'[^<]* <[^>]*> \(.*\)$/@\1/'" s/.*/GIT_$(uid)_DATE='&'/p } " } echo '/^$/q' } # Create a pick-script as above and feed it to sed. Stdout is suitable for # feeding to eval. proc parse_ident_from_commit { env LANG=C LC_ALL=C sed -ne $[pick_ident_script @Argv] } # Parse the author from a commit given as an argument. Stdout is suitable for # feeding to eval to set the usual GIT_* ident variables. proc get_author_ident_from_commit { setglobal encoding = $[git config i18n.commitencoding || echo UTF-8] git show -s --pretty=raw --encoding="$encoding" $1 -- | parse_ident_from_commit author AUTHOR } # Clear repo-local GIT_* environment variables. Useful when switching to # another repository (e.g. when entering a submodule). See also the env # list in git_connect() proc clear_local_git_env { unset $[git rev-parse --local-env-vars] } # Generate a virtual base file for a two-file merge. Uses git apply to # remove lines from $1 that are not in $2, leaving only common lines. proc create_virtual_base { setglobal sz0 = $[wc -c <$1] @@DIFF@@ -u -La/"$1" -Lb/"$1" $1 $2 | git apply --no-add setglobal sz1 = $[wc -c <$1] # If we do not have enough common material, it is not # worth trying two-file merge using common subsections. expr $sz0 '<' $sz1 '*' 2 >/dev/null || : >$1 } # Platform specific tweaks to work around some commands match $[uname -s] { with *MINGW* # Windows has its own (incompatible) sort and find proc sort { /usr/bin/sort @Argv } proc find { /usr/bin/find @Argv } # git sees Windows-style pwd proc pwd { builtin pwd -W } proc is_absolute_path { match $1 { with [/\\]* | [A-Za-z]:* return 0 } return 1 } with * proc is_absolute_path { match $1 { with /* return 0 } return 1 } } # Make sure we are in a valid repository of a vintage we understand, # if we require to be in a git repository. proc git_dir_init { setglobal GIT_DIR = $[git rev-parse --git-dir] || exit if test -z $SUBDIRECTORY_OK { test -z $[git rev-parse --show-cdup] || do { setglobal exit = $Status gettextln "You need to run this command from the toplevel of the working tree." > !2 exit $exit } } test -n $GIT_DIR && setglobal GIT_DIR = $[cd $GIT_DIR && pwd] || do { gettextln "Unable to determine absolute path of git directory" > !2 exit 1 } : $(GIT_OBJECT_DIRECTORY="$(git rev-parse --git-path objects)") } if test -z $NONGIT_OK { git_dir_init } proc peel_committish { match $1 { with :/* setglobal peeltmp = $[git rev-parse --verify $1] && git rev-parse --verify "$(peeltmp)^0" with * git rev-parse --verify "$(1)^0" } }