diff options
Diffstat (limited to 'contrib')
63 files changed, 1347 insertions, 11331 deletions
diff --git a/contrib/coccinelle/commit.cocci b/contrib/coccinelle/commit.cocci new file mode 100644 index 0000000000..aec3345adb --- /dev/null +++ b/contrib/coccinelle/commit.cocci @@ -0,0 +1,28 @@ +@@ +expression c; +@@ +- &c->maybe_tree->object.oid ++ get_commit_tree_oid(c) + +@@ +expression c; +@@ +- c->maybe_tree->object.oid.hash ++ get_commit_tree_oid(c)->hash + +// These excluded functions must access c->maybe_tree direcly. +@@ +identifier f !~ "^(get_commit_tree|get_commit_tree_in_graph_one|load_tree_for_commit)$"; +expression c; +@@ + f(...) {... +- c->maybe_tree ++ get_commit_tree(c) + ...} + +@@ +expression c; +expression s; +@@ +- get_commit_tree(c) = s ++ c->maybe_tree = s diff --git a/contrib/coccinelle/strbuf.cocci b/contrib/coccinelle/strbuf.cocci index 1d580e49b0..e34eada1ad 100644 --- a/contrib/coccinelle/strbuf.cocci +++ b/contrib/coccinelle/strbuf.cocci @@ -1,21 +1,6 @@ @ strbuf_addf_with_format_only @ expression E; -constant fmt; -@@ - strbuf_addf(E, -( - fmt -| - _(fmt) -) - ); - -@ script:python @ -fmt << strbuf_addf_with_format_only.fmt; -@@ -cocci.include_match("%" not in fmt) - -@ extends strbuf_addf_with_format_only @ +constant fmt !~ "%"; @@ - strbuf_addf + strbuf_addstr @@ -29,8 +14,9 @@ cocci.include_match("%" not in fmt) @@ expression E1, E2; +format F =~ "s"; @@ -- strbuf_addf(E1, "%s", E2); +- strbuf_addf(E1, "%@F@", E2); + strbuf_addstr(E1, E2); @@ diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 17929b0809..94c95516eb 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -29,6 +29,8 @@ # tell the completion to use commit completion. This also works with aliases # of form "!sh -c '...'". For example, "!sh -c ': git commit ; ... '". # +# Compatible with bash 3.2.57. +# # You can set the following environment variables to influence the behavior of # the completion routines: # @@ -92,6 +94,70 @@ __git () ${__git_dir:+--git-dir="$__git_dir"} "$@" 2>/dev/null } +# Removes backslash escaping, single quotes and double quotes from a word, +# stores the result in the variable $dequoted_word. +# 1: The word to dequote. +__git_dequote () +{ + local rest="$1" len ch + + dequoted_word="" + + while test -n "$rest"; do + len=${#dequoted_word} + dequoted_word="$dequoted_word${rest%%[\\\'\"]*}" + rest="${rest:$((${#dequoted_word}-$len))}" + + case "${rest:0:1}" in + \\) + ch="${rest:1:1}" + case "$ch" in + $'\n') + ;; + *) + dequoted_word="$dequoted_word$ch" + ;; + esac + rest="${rest:2}" + ;; + \') + rest="${rest:1}" + len=${#dequoted_word} + dequoted_word="$dequoted_word${rest%%\'*}" + rest="${rest:$((${#dequoted_word}-$len+1))}" + ;; + \") + rest="${rest:1}" + while test -n "$rest" ; do + len=${#dequoted_word} + dequoted_word="$dequoted_word${rest%%[\\\"]*}" + rest="${rest:$((${#dequoted_word}-$len))}" + case "${rest:0:1}" in + \\) + ch="${rest:1:1}" + case "$ch" in + \"|\\|\$|\`) + dequoted_word="$dequoted_word$ch" + ;; + $'\n') + ;; + *) + dequoted_word="$dequoted_word\\$ch" + ;; + esac + rest="${rest:2}" + ;; + \") + rest="${rest:1}" + break + ;; + esac + done + ;; + esac + done +} + # The following function is based on code from: # # bash_completion - programmable completion functions for bash 3.2+ @@ -264,9 +330,32 @@ __gitcomp () case "$cur_" in --*=) ;; + --no-*) + local c i=0 IFS=$' \t\n' + for c in $1; do + if [[ $c == "--" ]]; then + continue + fi + c="$c${4-}" + if [[ $c == "$cur_"* ]]; then + case $c in + --*=*|*.) ;; + *) c="$c " ;; + esac + COMPREPLY[i++]="${2-}$c" + fi + done + ;; *) local c i=0 IFS=$' \t\n' for c in $1; do + if [[ $c == "--" ]]; then + c="--no-...${4-}" + if [[ $c == "$cur_"* ]]; then + COMPREPLY[i++]="${2-}$c " + fi + break + fi c="$c${4-}" if [[ $c == "$cur_"* ]]; then case $c in @@ -280,6 +369,47 @@ __gitcomp () esac } +# Clear the variables caching builtins' options when (re-)sourcing +# the completion script. +if [[ -n ${ZSH_VERSION-} ]]; then + unset $(set |sed -ne 's/^\(__gitcomp_builtin_[a-zA-Z0-9_][a-zA-Z0-9_]*\)=.*/\1/p') 2>/dev/null +else + unset $(compgen -v __gitcomp_builtin_) +fi + +# This function is equivalent to +# +# __gitcomp "$(git xxx --git-completion-helper) ..." +# +# except that the output is cached. Accept 1-3 arguments: +# 1: the git command to execute, this is also the cache key +# 2: extra options to be added on top (e.g. negative forms) +# 3: options to be excluded +__gitcomp_builtin () +{ + # spaces must be replaced with underscore for multi-word + # commands, e.g. "git remote add" becomes remote_add. + local cmd="$1" + local incl="$2" + local excl="$3" + + local var=__gitcomp_builtin_"${cmd/-/_}" + local options + eval "options=\$$var" + + if [ -z "$options" ]; then + # leading and trailing spaces are significant to make + # option removal work correctly. + options=" $(__git ${cmd/_/ } --git-completion-helper) $incl " + for i in $excl; do + options="${options/ $i / }" + done + eval "$var=\"$options\"" + fi + + __gitcomp "$options" +} + # Variation of __gitcomp_nl () that appends to the existing list of # completion candidates, COMPREPLY. __gitcomp_nl_append () @@ -303,6 +433,24 @@ __gitcomp_nl () __gitcomp_nl_append "$@" } +# Fills the COMPREPLY array with prefiltered paths without any additional +# processing. +# Callers must take care of providing only paths that match the current path +# to be completed and adding any prefix path components, if necessary. +# 1: List of newline-separated matching paths, complete with all prefix +# path componens. +__gitcomp_file_direct () +{ + local IFS=$'\n' + + COMPREPLY=($1) + + # use a hack to enable file mode in bash < 4 + compopt -o filenames +o nospace 2>/dev/null || + compgen -f /non-existing-dir/ >/dev/null || + true +} + # Generates completion reply with compgen from newline-separated possible # completion filenames. # It accepts 1 to 3 arguments: @@ -322,7 +470,8 @@ __gitcomp_file () # use a hack to enable file mode in bash < 4 compopt -o filenames +o nospace 2>/dev/null || - compgen -f /non-existing-dir/ > /dev/null + compgen -f /non-existing-dir/ >/dev/null || + true } # Execute 'git ls-files', unless the --committable option is specified, in @@ -332,10 +481,12 @@ __gitcomp_file () __git_ls_files_helper () { if [ "$2" == "--committable" ]; then - __git -C "$1" diff-index --name-only --relative HEAD + __git -C "$1" -c core.quotePath=false diff-index \ + --name-only --relative HEAD -- "${3//\\/\\\\}*" else # NOTE: $2 is not quoted in order to support multiple options - __git -C "$1" ls-files --exclude-standard $2 + __git -C "$1" -c core.quotePath=false ls-files \ + --exclude-standard $2 -- "${3//\\/\\\\}*" fi } @@ -346,17 +497,103 @@ __git_ls_files_helper () # If provided, only files within the specified directory are listed. # Sub directories are never recursed. Path must have a trailing # slash. +# 3: List only paths matching this path component (optional). __git_index_files () { - local root="${2-.}" file + local root="$2" match="$3" - __git_ls_files_helper "$root" "$1" | - while read -r file; do - case "$file" in - ?*/*) echo "${file%%/*}" ;; - *) echo "$file" ;; - esac - done | sort | uniq + __git_ls_files_helper "$root" "$1" "$match" | + awk -F / -v pfx="${2//\\/\\\\}" '{ + paths[$1] = 1 + } + END { + for (p in paths) { + if (substr(p, 1, 1) != "\"") { + # No special characters, easy! + print pfx p + continue + } + + # The path is quoted. + p = dequote(p) + if (p == "") + continue + + # Even when a directory name itself does not contain + # any special characters, it will still be quoted if + # any of its (stripped) trailing path components do. + # Because of this we may have seen the same direcory + # both quoted and unquoted. + if (p in paths) + # We have seen the same directory unquoted, + # skip it. + continue + else + print pfx p + } + } + function dequote(p, bs_idx, out, esc, esc_idx, dec) { + # Skip opening double quote. + p = substr(p, 2) + + # Interpret backslash escape sequences. + while ((bs_idx = index(p, "\\")) != 0) { + out = out substr(p, 1, bs_idx - 1) + esc = substr(p, bs_idx + 1, 1) + p = substr(p, bs_idx + 2) + + if ((esc_idx = index("abtvfr\"\\", esc)) != 0) { + # C-style one-character escape sequence. + out = out substr("\a\b\t\v\f\r\"\\", + esc_idx, 1) + } else if (esc == "n") { + # Uh-oh, a newline character. + # We cant reliably put a pathname + # containing a newline into COMPREPLY, + # and the newline would create a mess. + # Skip this path. + return "" + } else { + # Must be a \nnn octal value, then. + dec = esc * 64 + \ + substr(p, 1, 1) * 8 + \ + substr(p, 2, 1) + out = out sprintf("%c", dec) + p = substr(p, 3) + } + } + # Drop closing double quote, if there is one. + # (There isnt any if this is a directory, as it was + # already stripped with the trailing path components.) + if (substr(p, length(p), 1) == "\"") + out = out substr(p, 1, length(p) - 1) + else + out = out p + + return out + }' +} + +# __git_complete_index_file requires 1 argument: +# 1: the options to pass to ls-file +# +# The exception is --committable, which finds the files appropriate commit. +__git_complete_index_file () +{ + local dequoted_word pfx="" cur_ + + __git_dequote "$cur" + + case "$dequoted_word" in + ?*/*) + pfx="${dequoted_word%/*}/" + cur_="${dequoted_word##*/}" + ;; + *) + cur_="$dequoted_word" + esac + + __gitcomp_file_direct "$(__git_index_files "$1" "$pfx" "$cur_")" } # Lists branches from the local repository. @@ -439,7 +676,7 @@ __git_refs () track="" ;; *) - for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD; do + for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD REBASE_HEAD; do case "$i" in $match*) if [ -e "$dir/$i" ]; then @@ -594,7 +831,7 @@ __git_is_configured_remote () __git_list_merge_strategies () { - git merge -s help 2>&1 | + LANG=C LC_ALL=C git merge -s help 2>&1 | sed -n -e '/[Aa]vailable strategies are: /,/^$/{ s/\.$// s/.*:// @@ -675,26 +912,6 @@ __git_complete_revlist_file () esac } - -# __git_complete_index_file requires 1 argument: -# 1: the options to pass to ls-file -# -# The exception is --committable, which finds the files appropriate commit. -__git_complete_index_file () -{ - local pfx="" cur_="$cur" - - case "$cur_" in - ?*/*) - pfx="${cur_%/*}" - cur_="${cur_##*/}" - pfx="${pfx}/" - ;; - esac - - __gitcomp_file "$(__git_index_files "$1" ${pfx:+"$pfx"})" "$pfx" "$cur_" -} - __git_complete_file () { __git_complete_revlist_file @@ -795,126 +1012,11 @@ __git_complete_strategy () return 1 } -__git_commands () { - if test -n "${GIT_TESTING_COMMAND_COMPLETION:-}" - then - printf "%s" "${GIT_TESTING_COMMAND_COMPLETION}" - else - git help -a|egrep '^ [a-zA-Z0-9]' - fi -} - -__git_list_all_commands () -{ - local i IFS=" "$'\n' - for i in $(__git_commands) - do - case $i in - *--*) : helper pattern;; - *) echo $i;; - esac - done -} - __git_all_commands= __git_compute_all_commands () { test -n "$__git_all_commands" || - __git_all_commands=$(__git_list_all_commands) -} - -__git_list_porcelain_commands () -{ - local i IFS=" "$'\n' - __git_compute_all_commands - for i in $__git_all_commands - do - case $i in - *--*) : helper pattern;; - applymbox) : ask gittus;; - applypatch) : ask gittus;; - archimport) : import;; - cat-file) : plumbing;; - check-attr) : plumbing;; - check-ignore) : plumbing;; - check-mailmap) : plumbing;; - check-ref-format) : plumbing;; - checkout-index) : plumbing;; - column) : internal helper;; - commit-tree) : plumbing;; - count-objects) : infrequent;; - credential) : credentials;; - credential-*) : credentials helper;; - cvsexportcommit) : export;; - cvsimport) : import;; - cvsserver) : daemon;; - daemon) : daemon;; - diff-files) : plumbing;; - diff-index) : plumbing;; - diff-tree) : plumbing;; - fast-import) : import;; - fast-export) : export;; - fsck-objects) : plumbing;; - fetch-pack) : plumbing;; - fmt-merge-msg) : plumbing;; - for-each-ref) : plumbing;; - hash-object) : plumbing;; - http-*) : transport;; - index-pack) : plumbing;; - init-db) : deprecated;; - local-fetch) : plumbing;; - ls-files) : plumbing;; - ls-remote) : plumbing;; - ls-tree) : plumbing;; - mailinfo) : plumbing;; - mailsplit) : plumbing;; - merge-*) : plumbing;; - mktree) : plumbing;; - mktag) : plumbing;; - pack-objects) : plumbing;; - pack-redundant) : plumbing;; - pack-refs) : plumbing;; - parse-remote) : plumbing;; - patch-id) : plumbing;; - prune) : plumbing;; - prune-packed) : plumbing;; - quiltimport) : import;; - read-tree) : plumbing;; - receive-pack) : plumbing;; - remote-*) : transport;; - rerere) : plumbing;; - rev-list) : plumbing;; - rev-parse) : plumbing;; - runstatus) : plumbing;; - sh-setup) : internal;; - shell) : daemon;; - show-ref) : plumbing;; - send-pack) : plumbing;; - show-index) : plumbing;; - ssh-*) : transport;; - stripspace) : plumbing;; - symbolic-ref) : plumbing;; - unpack-file) : plumbing;; - unpack-objects) : plumbing;; - update-index) : plumbing;; - update-ref) : plumbing;; - update-server-info) : daemon;; - upload-archive) : plumbing;; - upload-pack) : plumbing;; - write-tree) : plumbing;; - var) : infrequent;; - verify-pack) : infrequent;; - verify-tag) : plumbing;; - *) echo $i;; - esac - done -} - -__git_porcelain_commands= -__git_compute_porcelain_commands () -{ - test -n "$__git_porcelain_commands" || - __git_porcelain_commands=$(__git_list_porcelain_commands) + __git_all_commands=$(git --list-cmds=main,others,alias,nohelpers) } # Lists all set config variables starting with the given section prefix, @@ -932,11 +1034,6 @@ __git_pretty_aliases () __git_get_config_variables "pretty" } -__git_aliases () -{ - __git_get_config_variables "alias" -} - # __git_aliased_command requires 1 argument __git_aliased_command () { @@ -1072,12 +1169,13 @@ __git_count_arguments () } __git_whitespacelist="nowarn warn error error-all fix" +__git_am_inprogress_options="--skip --continue --resolved --abort --quit --show-current-patch" _git_am () { __git_find_repo_path if [ -d "$__git_repo_path"/rebase-apply ]; then - __gitcomp "--skip --continue --resolved --abort" + __gitcomp "$__git_am_inprogress_options" return fi case "$cur" in @@ -1086,12 +1184,8 @@ _git_am () return ;; --*) - __gitcomp " - --3way --committer-date-is-author-date --ignore-date - --ignore-whitespace --ignore-space-change - --interactive --keep --no-utf8 --signoff --utf8 - --whitespace= --scissors - " + __gitcomp_builtin am "" \ + "$__git_am_inprogress_options" return esac } @@ -1104,14 +1198,7 @@ _git_apply () return ;; --*) - __gitcomp " - --stat --numstat --summary --check --index - --cached --index-info --reverse --reject --unidiff-zero - --apply --no-add --exclude= - --ignore-whitespace --ignore-space-change - --whitespace= --inaccurate-eof --verbose - --recount --directory= - " + __gitcomp_builtin apply return esac } @@ -1120,10 +1207,7 @@ _git_add () { case "$cur" in --*) - __gitcomp " - --interactive --refresh --patch --update --dry-run - --ignore-errors --intent-to-add --force --edit --chmod= - " + __gitcomp_builtin add return esac @@ -1200,13 +1284,7 @@ _git_branch () __git_complete_refs --cur="${cur##--set-upstream-to=}" ;; --*) - __gitcomp " - --color --no-color --verbose --abbrev= --no-abbrev - --track --no-track --contains --no-contains --merged --no-merged - --set-upstream-to= --edit-description --list - --unset-upstream --delete --move --copy --remotes - --column --no-column --sort= --points-at - " + __gitcomp_builtin branch ;; *) if [ $only_local_ref = "y" -a $has_r = "n" ]; then @@ -1247,11 +1325,7 @@ _git_checkout () __gitcomp "diff3 merge" "" "${cur##--conflict=}" ;; --*) - __gitcomp " - --quiet --ours --theirs --track --no-track --merge - --conflict= --orphan --patch --detach --ignore-skip-worktree-bits - --recurse-submodules --no-recurse-submodules - " + __gitcomp_builtin checkout ;; *) # check if --track, --no-track, or --no-guess was specified @@ -1268,19 +1342,28 @@ _git_checkout () _git_cherry () { + case "$cur" in + --*) + __gitcomp_builtin cherry + return + esac + __git_complete_refs } +__git_cherry_pick_inprogress_options="--continue --quit --abort" + _git_cherry_pick () { __git_find_repo_path if [ -f "$__git_repo_path"/CHERRY_PICK_HEAD ]; then - __gitcomp "--continue --quit --abort" + __gitcomp "$__git_cherry_pick_inprogress_options" return fi case "$cur" in --*) - __gitcomp "--edit --no-commit --signoff --strategy= --mainline" + __gitcomp_builtin cherry-pick "" \ + "$__git_cherry_pick_inprogress_options" ;; *) __git_complete_refs @@ -1292,7 +1375,7 @@ _git_clean () { case "$cur" in --*) - __gitcomp "--dry-run --quiet" + __gitcomp_builtin clean return ;; esac @@ -1305,26 +1388,7 @@ _git_clone () { case "$cur" in --*) - __gitcomp " - --local - --no-hardlinks - --shared - --reference - --quiet - --no-checkout - --bare - --mirror - --origin - --upload-pack - --template= - --depth - --single-branch - --no-tags - --branch - --recurse-submodules - --no-single-branch - --shallow-submodules - " + __gitcomp_builtin clone return ;; esac @@ -1357,16 +1421,7 @@ _git_commit () return ;; --*) - __gitcomp " - --all --author= --signoff --verify --no-verify - --edit --no-edit - --amend --include --only --interactive - --dry-run --reuse-message= --reedit-message= - --reset-author --file= --message= --template= - --cleanup= --untracked-files --untracked-files= - --verbose --quiet --fixup= --squash= - --patch --short --date --allow-empty - " + __gitcomp_builtin commit return esac @@ -1382,11 +1437,7 @@ _git_describe () { case "$cur" in --*) - __gitcomp " - --all --tags --contains --abbrev= --candidates= - --exact-match --debug --long --match --always --first-parent - --exclude --dirty --broken - " + __gitcomp_builtin describe return esac __git_complete_refs @@ -1400,7 +1451,7 @@ __git_diff_common_options="--stat --numstat --shortstat --summary --patch-with-stat --name-only --name-status --color --no-color --color-words --no-renames --check --full-index --binary --abbrev --diff-filter= - --find-copies-harder + --find-copies-harder --ignore-cr-at-eol --text --ignore-space-at-eol --ignore-space-change --ignore-all-space --ignore-blank-lines --exit-code --quiet --ext-diff --no-ext-diff @@ -1411,7 +1462,7 @@ __git_diff_common_options="--stat --numstat --shortstat --summary --dirstat --dirstat= --dirstat-by-file --dirstat-by-file= --cumulative --diff-algorithm= - --submodule --submodule= + --submodule --submodule= --ignore-submodules " _git_diff () @@ -1452,11 +1503,11 @@ _git_difftool () return ;; --*) - __gitcomp "--cached --staged --pickaxe-all --pickaxe-regex - --base --ours --theirs - --no-renames --diff-filter= --find-copies-harder - --relative --ignore-submodules - --tool=" + __gitcomp_builtin difftool "$__git_diff_common_options + --base --cached --ours --theirs + --pickaxe-all --pickaxe-regex + --relative --staged + " return ;; esac @@ -1465,12 +1516,6 @@ _git_difftool () __git_fetch_recurse_submodules="yes on-demand no" -__git_fetch_options=" - --quiet --verbose --append --upload-pack --force --keep --depth= - --tags --no-tags --all --prune --dry-run --recurse-submodules= - --unshallow --update-shallow -" - _git_fetch () { case "$cur" in @@ -1479,7 +1524,7 @@ _git_fetch () return ;; --*) - __gitcomp "$__git_fetch_options" + __gitcomp_builtin fetch return ;; esac @@ -1516,20 +1561,7 @@ _git_fsck () { case "$cur" in --*) - __gitcomp " - --tags --root --unreachable --cache --no-reflogs --full - --strict --verbose --lost-found --name-objects - " - return - ;; - esac -} - -_git_gc () -{ - case "$cur" in - --*) - __gitcomp "--prune --aggressive" + __gitcomp_builtin fsck return ;; esac @@ -1585,21 +1617,7 @@ _git_grep () case "$cur" in --*) - __gitcomp " - --cached - --text --ignore-case --word-regexp --invert-match - --full-name --line-number - --extended-regexp --basic-regexp --fixed-strings - --perl-regexp - --threads - --files-with-matches --name-only - --files-without-match - --max-depth - --count - --and --or --not --all-match - --break --heading --show-function --function-context - --untracked --no-index - " + __gitcomp_builtin grep return ;; esac @@ -1617,17 +1635,16 @@ _git_help () { case "$cur" in --*) - __gitcomp "--all --guides --info --man --web" + __gitcomp_builtin help return ;; esac - __git_compute_all_commands - __gitcomp "$__git_all_commands $(__git_aliases) - attributes cli core-tutorial cvs-migration - diffcore everyday gitk glossary hooks ignore modules - namespaces repository-layout revisions tutorial tutorial-2 - workflows - " + if test -n "$GIT_TESTING_ALL_COMMAND_LIST" + then + __gitcomp "$GIT_TESTING_ALL_COMMAND_LIST $(git --list-cmds=alias,list-guide) gitk" + else + __gitcomp "$(git --list-cmds=main,nohelpers,alias,list-guide) gitk" + fi } _git_init () @@ -1640,7 +1657,7 @@ _git_init () return ;; --*) - __gitcomp "--quiet --bare --template= --shared --shared=" + __gitcomp_builtin init return ;; esac @@ -1650,13 +1667,7 @@ _git_ls_files () { case "$cur" in --*) - __gitcomp "--cached --deleted --modified --others --ignored - --stage --directory --no-empty-directory --unmerged - --killed --exclude= --exclude-from= - --exclude-per-directory= --exclude-standard - --error-unmatch --with-tree= --full-name - --abbrev --ignored --exclude-per-directory - " + __gitcomp_builtin ls-files return ;; esac @@ -1670,7 +1681,7 @@ _git_ls_remote () { case "$cur" in --*) - __gitcomp "--heads --tags --refs --get-url --symref" + __gitcomp_builtin ls-remote return ;; esac @@ -1679,6 +1690,13 @@ _git_ls_remote () _git_ls_tree () { + case "$cur" in + --*) + __gitcomp_builtin ls-tree + return + ;; + esac + __git_complete_file } @@ -1794,22 +1812,13 @@ _git_log () __git_complete_revlist } -# Common merge options shared by git-merge(1) and git-pull(1). -__git_merge_options=" - --no-commit --no-stat --log --no-log --squash --strategy - --commit --stat --no-squash --ff --no-ff --ff-only --edit --no-edit - --verify-signatures --no-verify-signatures --gpg-sign - --quiet --verbose --progress --no-progress -" - _git_merge () { __git_complete_strategy && return case "$cur" in --*) - __gitcomp "$__git_merge_options - --rerere-autoupdate --no-rerere-autoupdate --abort --continue" + __gitcomp_builtin merge return esac __git_complete_refs @@ -1833,7 +1842,7 @@ _git_merge_base () { case "$cur" in --*) - __gitcomp "--octopus --independent --is-ancestor --fork-point" + __gitcomp_builtin merge-base return ;; esac @@ -1844,7 +1853,7 @@ _git_mv () { case "$cur" in --*) - __gitcomp "--dry-run" + __gitcomp_builtin mv return ;; esac @@ -1858,19 +1867,14 @@ _git_mv () fi } -_git_name_rev () -{ - __gitcomp "--tags --all --stdin" -} - _git_notes () { - local subcommands='add append copy edit list prune remove show' + local subcommands='add append copy edit get-ref list merge prune remove show' local subcommand="$(__git_find_on_cmdline "$subcommands")" case "$subcommand,$cur" in ,--*) - __gitcomp '--ref' + __gitcomp_builtin notes ;; ,*) case "$prev" in @@ -1882,21 +1886,14 @@ _git_notes () ;; esac ;; - add,--reuse-message=*|append,--reuse-message=*|\ - add,--reedit-message=*|append,--reedit-message=*) + *,--reuse-message=*|*,--reedit-message=*) __git_complete_refs --cur="${cur#*=}" ;; - add,--*|append,--*) - __gitcomp '--file= --message= --reedit-message= - --reuse-message=' - ;; - copy,--*) - __gitcomp '--stdin' + *,--*) + __gitcomp_builtin notes_$subcommand ;; - prune,--*) - __gitcomp '--dry-run --verbose' - ;; - prune,*) + prune,*|get-ref,*) + # this command does not take a ref, do not complete it ;; *) case "$prev" in @@ -1920,11 +1917,8 @@ _git_pull () return ;; --*) - __gitcomp " - --rebase --no-rebase - $__git_merge_options - $__git_fetch_options - " + __gitcomp_builtin pull + return ;; esac @@ -1975,12 +1969,7 @@ _git_push () return ;; --*) - __gitcomp " - --all --mirror --tags --dry-run --force --verbose - --quiet --prune --delete --follow-tags - --receive-pack= --repo= --set-upstream - --force-with-lease --force-with-lease= --recurse-submodules= - " + __gitcomp_builtin push return ;; esac @@ -1991,11 +1980,11 @@ _git_rebase () { __git_find_repo_path if [ -f "$__git_repo_path"/rebase-merge/interactive ]; then - __gitcomp "--continue --skip --abort --quit --edit-todo" + __gitcomp "--continue --skip --abort --quit --edit-todo --show-current-patch" return elif [ -d "$__git_repo_path"/rebase-apply ] || \ [ -d "$__git_repo_path"/rebase-merge ]; then - __gitcomp "--continue --skip --abort --quit" + __gitcomp "--continue --skip --abort --quit --show-current-patch" return fi __git_complete_strategy && return @@ -2007,7 +1996,7 @@ _git_rebase () --*) __gitcomp " --onto --merge --strategy --interactive - --preserve-merges --stat --no-stat + --rebase-merges --preserve-merges --stat --no-stat --committer-date-is-author-date --ignore-date --ignore-whitespace --whitespace= --autosquash --no-autosquash @@ -2015,6 +2004,7 @@ _git_rebase () --autostash --no-autostash --verify --no-verify --keep-empty --root --force-rebase --no-ff + --rerere-autoupdate --exec " @@ -2080,7 +2070,7 @@ _git_send_email () --compose --confirm= --dry-run --envelope-sender --from --identity --in-reply-to --no-chain-reply-to --no-signed-off-by-cc - --no-suppress-from --no-thread --quiet + --no-suppress-from --no-thread --quiet --reply-to --signed-off-by-cc --smtp-pass --smtp-server --smtp-server-port --smtp-encryption= --smtp-user --subject --suppress-cc= --suppress-from --thread --to @@ -2118,11 +2108,7 @@ _git_status () return ;; --*) - __gitcomp " - --short --branch --porcelain --long --verbose - --untracked-files= --ignore-submodules= --ignored - --column= --no-column - " + __gitcomp_builtin status return ;; esac @@ -2169,9 +2155,24 @@ __git_config_get_set_variables () __git config $config_file --name-only --list } +__git_config_vars= +__git_compute_config_vars () +{ + test -n "$__git_config_vars" || + __git_config_vars="$(git help --config-for-completion | sort | uniq)" +} + _git_config () { - case "$prev" in + local varname + + if [ "${BASH_VERSINFO[0]:-0}" -ge 4 ]; then + varname="${prev,,}" + else + varname="$(echo "$prev" |tr A-Z a-z)" + fi + + case "$varname" in branch.*.remote|branch.*.pushremote) __gitcomp_nl "$(__git_remotes)" return @@ -2181,7 +2182,7 @@ _git_config () return ;; branch.*.rebase) - __gitcomp "false true preserve interactive" + __gitcomp "false true merges preserve interactive" return ;; remote.pushdefault) @@ -2238,7 +2239,7 @@ _git_config () __gitcomp "$__git_log_date_formats" return ;; - sendemail.aliasesfiletype) + sendemail.aliasfiletype) __gitcomp "mutt mailrc pine elm gnus" return ;; @@ -2264,32 +2265,25 @@ _git_config () esac case "$cur" in --*) - __gitcomp " - --system --global --local --file= - --list --replace-all - --get --get-all --get-regexp - --add --unset --unset-all - --remove-section --rename-section - --name-only - " + __gitcomp_builtin config return ;; branch.*.*) local pfx="${cur%.*}." cur_="${cur##*.}" - __gitcomp "remote pushremote merge mergeoptions rebase" "$pfx" "$cur_" + __gitcomp "remote pushRemote merge mergeOptions rebase" "$pfx" "$cur_" return ;; branch.*) local pfx="${cur%.*}." cur_="${cur#*.}" __gitcomp_direct "$(__git_heads "$pfx" "$cur_" ".")" - __gitcomp_nl_append $'autosetupmerge\nautosetuprebase\n' "$pfx" "$cur_" + __gitcomp_nl_append $'autoSetupMerge\nautoSetupRebase\n' "$pfx" "$cur_" return ;; guitool.*.*) local pfx="${cur%.*}." cur_="${cur##*.}" __gitcomp " - argprompt cmd confirm needsfile noconsole norescan - prompt revprompt revunmerged title + argPrompt cmd confirm needsFile noConsole noRescan + prompt revPrompt revUnmerged title " "$pfx" "$cur_" return ;; @@ -2318,14 +2312,14 @@ _git_config () local pfx="${cur%.*}." cur_="${cur##*.}" __gitcomp " url proxy fetch push mirror skipDefaultUpdate - receivepack uploadpack tagopt pushurl + receivepack uploadpack tagOpt pushurl " "$pfx" "$cur_" return ;; remote.*) local pfx="${cur%.*}." cur_="${cur#*.}" __gitcomp_nl "$(__git_remotes)" "$pfx" "$cur_" "." - __gitcomp_nl_append "pushdefault" "$pfx" "$cur_" + __gitcomp_nl_append "pushDefault" "$pfx" "$cur_" return ;; url.*.*) @@ -2333,330 +2327,14 @@ _git_config () __gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur_" return ;; + *.*) + __git_compute_config_vars + __gitcomp "$__git_config_vars" + ;; + *) + __git_compute_config_vars + __gitcomp "$(echo "$__git_config_vars" | sed 's/\.[^ ]*/./g')" esac - __gitcomp " - add.ignoreErrors - advice.amWorkDir - advice.commitBeforeMerge - advice.detachedHead - advice.implicitIdentity - advice.pushAlreadyExists - advice.pushFetchFirst - advice.pushNeedsForce - advice.pushNonFFCurrent - advice.pushNonFFMatching - advice.pushUpdateRejected - advice.resolveConflict - advice.rmHints - advice.statusHints - advice.statusUoption - alias. - am.keepcr - am.threeWay - apply.ignorewhitespace - apply.whitespace - branch.autosetupmerge - branch.autosetuprebase - browser. - clean.requireForce - color.branch - color.branch.current - color.branch.local - color.branch.plain - color.branch.remote - color.decorate.HEAD - color.decorate.branch - color.decorate.remoteBranch - color.decorate.stash - color.decorate.tag - color.diff - color.diff.commit - color.diff.frag - color.diff.func - color.diff.meta - color.diff.new - color.diff.old - color.diff.plain - color.diff.whitespace - color.grep - color.grep.context - color.grep.filename - color.grep.function - color.grep.linenumber - color.grep.match - color.grep.selected - color.grep.separator - color.interactive - color.interactive.error - color.interactive.header - color.interactive.help - color.interactive.prompt - color.pager - color.showbranch - color.status - color.status.added - color.status.changed - color.status.header - color.status.localBranch - color.status.nobranch - color.status.remoteBranch - color.status.unmerged - color.status.untracked - color.status.updated - color.ui - commit.cleanup - commit.gpgSign - commit.status - commit.template - commit.verbose - core.abbrev - core.askpass - core.attributesfile - core.autocrlf - core.bare - core.bigFileThreshold - core.checkStat - core.commentChar - core.compression - core.createObject - core.deltaBaseCacheLimit - core.editor - core.eol - core.excludesfile - core.fileMode - core.fsyncobjectfiles - core.gitProxy - core.hideDotFiles - core.hooksPath - core.ignoreStat - core.ignorecase - core.logAllRefUpdates - core.loosecompression - core.notesRef - core.packedGitLimit - core.packedGitWindowSize - core.packedRefsTimeout - core.pager - core.precomposeUnicode - core.preferSymlinkRefs - core.preloadindex - core.protectHFS - core.protectNTFS - core.quotepath - core.repositoryFormatVersion - core.safecrlf - core.sharedRepository - core.sparseCheckout - core.splitIndex - core.sshCommand - core.symlinks - core.trustctime - core.untrackedCache - core.warnAmbiguousRefs - core.whitespace - core.worktree - credential.helper - credential.useHttpPath - credential.username - credentialCache.ignoreSIGHUP - diff.autorefreshindex - diff.external - diff.ignoreSubmodules - diff.mnemonicprefix - diff.noprefix - diff.renameLimit - diff.renames - diff.statGraphWidth - diff.submodule - diff.suppressBlankEmpty - diff.tool - diff.wordRegex - diff.algorithm - difftool. - difftool.prompt - fetch.recurseSubmodules - fetch.unpackLimit - format.attach - format.cc - format.coverLetter - format.from - format.headers - format.numbered - format.pretty - format.signature - format.signoff - format.subjectprefix - format.suffix - format.thread - format.to - gc. - gc.aggressiveDepth - gc.aggressiveWindow - gc.auto - gc.autoDetach - gc.autopacklimit - gc.logExpiry - gc.packrefs - gc.pruneexpire - gc.reflogexpire - gc.reflogexpireunreachable - gc.rerereresolved - gc.rerereunresolved - gc.worktreePruneExpire - gitcvs.allbinary - gitcvs.commitmsgannotation - gitcvs.dbTableNamePrefix - gitcvs.dbdriver - gitcvs.dbname - gitcvs.dbpass - gitcvs.dbuser - gitcvs.enabled - gitcvs.logfile - gitcvs.usecrlfattr - guitool. - gui.blamehistoryctx - gui.commitmsgwidth - gui.copyblamethreshold - gui.diffcontext - gui.encoding - gui.fastcopyblame - gui.matchtrackingbranch - gui.newbranchtemplate - gui.pruneduringfetch - gui.spellingdictionary - gui.trustmtime - help.autocorrect - help.browser - help.format - http.lowSpeedLimit - http.lowSpeedTime - http.maxRequests - http.minSessions - http.noEPSV - http.postBuffer - http.proxy - http.sslCipherList - http.sslVersion - http.sslCAInfo - http.sslCAPath - http.sslCert - http.sslCertPasswordProtected - http.sslKey - http.sslVerify - http.useragent - i18n.commitEncoding - i18n.logOutputEncoding - imap.authMethod - imap.folder - imap.host - imap.pass - imap.port - imap.preformattedHTML - imap.sslverify - imap.tunnel - imap.user - init.templatedir - instaweb.browser - instaweb.httpd - instaweb.local - instaweb.modulepath - instaweb.port - interactive.singlekey - log.date - log.decorate - log.showroot - mailmap.file - man. - man.viewer - merge. - merge.conflictstyle - merge.log - merge.renameLimit - merge.renormalize - merge.stat - merge.tool - merge.verbosity - mergetool. - mergetool.keepBackup - mergetool.keepTemporaries - mergetool.prompt - notes.displayRef - notes.rewrite. - notes.rewrite.amend - notes.rewrite.rebase - notes.rewriteMode - notes.rewriteRef - pack.compression - pack.deltaCacheLimit - pack.deltaCacheSize - pack.depth - pack.indexVersion - pack.packSizeLimit - pack.threads - pack.window - pack.windowMemory - pager. - pretty. - pull.octopus - pull.twohead - push.default - push.followTags - rebase.autosquash - rebase.stat - receive.autogc - receive.denyCurrentBranch - receive.denyDeleteCurrent - receive.denyDeletes - receive.denyNonFastForwards - receive.fsckObjects - receive.unpackLimit - receive.updateserverinfo - remote.pushdefault - remotes. - repack.usedeltabaseoffset - rerere.autoupdate - rerere.enabled - sendemail. - sendemail.aliasesfile - sendemail.aliasfiletype - sendemail.bcc - sendemail.cc - sendemail.cccmd - sendemail.chainreplyto - sendemail.confirm - sendemail.envelopesender - sendemail.from - sendemail.identity - sendemail.multiedit - sendemail.signedoffbycc - sendemail.smtpdomain - sendemail.smtpencryption - sendemail.smtppass - sendemail.smtpserver - sendemail.smtpserveroption - sendemail.smtpserverport - sendemail.smtpuser - sendemail.suppresscc - sendemail.suppressfrom - sendemail.thread - sendemail.to - sendemail.validate - sendemail.smtpbatchsize - sendemail.smtprelogindelay - showbranch.default - status.relativePaths - status.showUntrackedFiles - status.submodulesummary - submodule. - tar.umask - transfer.unpackLimit - url. - user.email - user.name - user.signingkey - web.browser - branch. remote. - " } _git_remote () @@ -2669,7 +2347,7 @@ _git_remote () if [ -z "$subcommand" ]; then case "$cur" in --*) - __gitcomp "--verbose" + __gitcomp_builtin remote ;; *) __gitcomp "$subcommands" @@ -2680,33 +2358,33 @@ _git_remote () case "$subcommand,$cur" in add,--*) - __gitcomp "--track --master --fetch --tags --no-tags --mirror=" + __gitcomp_builtin remote_add ;; add,*) ;; set-head,--*) - __gitcomp "--auto --delete" + __gitcomp_builtin remote_set-head ;; set-branches,--*) - __gitcomp "--add" + __gitcomp_builtin remote_set-branches ;; set-head,*|set-branches,*) __git_complete_remote_or_refspec ;; update,--*) - __gitcomp "--prune" + __gitcomp_builtin remote_update ;; update,*) - __gitcomp "$(__git_get_config_variables "remotes")" + __gitcomp "$(__git_remotes) $(__git_get_config_variables "remotes")" ;; set-url,--*) - __gitcomp "--push --add --delete" + __gitcomp_builtin remote_set-url ;; get-url,--*) - __gitcomp "--push --all" + __gitcomp_builtin remote_get-url ;; prune,--*) - __gitcomp "--dry-run" + __gitcomp_builtin remote_prune ;; *) __gitcomp_nl "$(__git_remotes)" @@ -2718,7 +2396,7 @@ _git_replace () { case "$cur" in --*) - __gitcomp "--edit --graft --format= --list --delete" + __gitcomp_builtin replace return ;; esac @@ -2742,26 +2420,26 @@ _git_reset () case "$cur" in --*) - __gitcomp "--merge --mixed --hard --soft --patch --keep" + __gitcomp_builtin reset return ;; esac __git_complete_refs } +__git_revert_inprogress_options="--continue --quit --abort" + _git_revert () { __git_find_repo_path if [ -f "$__git_repo_path"/REVERT_HEAD ]; then - __gitcomp "--continue --quit --abort" + __gitcomp "$__git_revert_inprogress_options" return fi case "$cur" in --*) - __gitcomp " - --edit --mainline --no-edit --no-commit --signoff - --strategy= --strategy-option= - " + __gitcomp_builtin revert "" \ + "$__git_revert_inprogress_options" return ;; esac @@ -2772,7 +2450,7 @@ _git_rm () { case "$cur" in --*) - __gitcomp "--cached --dry-run --ignore-unmatch --quiet" + __gitcomp_builtin rm return ;; esac @@ -2830,12 +2508,7 @@ _git_show_branch () { case "$cur" in --*) - __gitcomp " - --all --remotes --topo-order --date-order --current --more= - --list --independent --merge-base --no-name - --color --no-color - --sha1-name --sparse --topics --reflog - " + __gitcomp_builtin show-branch return ;; esac @@ -2845,13 +2518,21 @@ _git_show_branch () _git_stash () { local save_opts='--all --keep-index --no-keep-index --quiet --patch --include-untracked' - local subcommands='push save list show apply clear drop pop create branch' - local subcommand="$(__git_find_on_cmdline "$subcommands")" + local subcommands='push list show apply clear drop pop create branch' + local subcommand="$(__git_find_on_cmdline "$subcommands save")" + if [ -n "$(__git_find_on_cmdline "-p")" ]; then + subcommand="push" + fi if [ -z "$subcommand" ]; then case "$cur" in --*) __gitcomp "$save_opts" ;; + sa*) + if [ -z "$(__git_find_on_cmdline "$save_opts")" ]; then + __gitcomp "save" + fi + ;; *) if [ -z "$(__git_find_on_cmdline "$save_opts")" ]; then __gitcomp "$subcommands" @@ -3042,7 +2723,7 @@ _git_tag () while [ $c -lt $cword ]; do i="${words[c]}" case "$i" in - -d|-v) + -d|--delete|-v|--verify) __gitcomp_direct "$(__git_tags "" "$cur" " ")" return ;; @@ -3068,11 +2749,7 @@ _git_tag () case "$cur" in --*) - __gitcomp " - --list --delete --verify --annotate --message --file - --sign --cleanup --local-user --force --column --sort= - --contains --no-contains --points-at --merged --no-merged --create-reflog - " + __gitcomp_builtin tag ;; esac } @@ -3084,23 +2761,26 @@ _git_whatchanged () _git_worktree () { - local subcommands="add list lock prune unlock" + local subcommands="add list lock move prune remove unlock" local subcommand="$(__git_find_on_cmdline "$subcommands")" if [ -z "$subcommand" ]; then __gitcomp "$subcommands" else case "$subcommand,$cur" in add,--*) - __gitcomp "--detach" + __gitcomp_builtin worktree_add ;; list,--*) - __gitcomp "--porcelain" + __gitcomp_builtin worktree_list ;; lock,--*) - __gitcomp "--reason" + __gitcomp_builtin worktree_lock ;; prune,--*) - __gitcomp "--dry-run --expire --verbose" + __gitcomp_builtin worktree_prune + ;; + remove,--*) + __gitcomp "--force" ;; *) ;; @@ -3108,6 +2788,52 @@ _git_worktree () fi } +__git_complete_common () { + local command="$1" + + case "$cur" in + --*) + __gitcomp_builtin "$command" + ;; + esac +} + +__git_cmds_with_parseopt_helper= +__git_support_parseopt_helper () { + test -n "$__git_cmds_with_parseopt_helper" || + __git_cmds_with_parseopt_helper="$(__git --list-cmds=parseopt)" + + case " $__git_cmds_with_parseopt_helper " in + *" $1 "*) + return 0 + ;; + *) + return 1 + ;; + esac +} + +__git_complete_command () { + local command="$1" + local completion_func="_git_${command//-/_}" + if ! declare -f $completion_func >/dev/null 2>/dev/null && + declare -f _completion_loader >/dev/null 2>/dev/null + then + _completion_loader "git-$command" + fi + if declare -f $completion_func >/dev/null 2>/dev/null + then + $completion_func + return 0 + elif __git_support_parseopt_helper "$command" + then + __git_complete_common "$command" + return 0 + else + return 1 + fi +} + __git_main () { local i c=1 command __git_dir __git_repo_path @@ -3161,20 +2887,24 @@ __git_main () --help " ;; - *) __git_compute_porcelain_commands - __gitcomp "$__git_porcelain_commands $(__git_aliases)" ;; + *) + if test -n "$GIT_TESTING_PORCELAIN_COMMAND_LIST" + then + __gitcomp "$GIT_TESTING_PORCELAIN_COMMAND_LIST" + else + __gitcomp "$(git --list-cmds=list-mainporcelain,others,nohelpers,alias,list-complete,config)" + fi + ;; esac return fi - local completion_func="_git_${command//-/_}" - declare -f $completion_func >/dev/null 2>/dev/null && $completion_func && return + __git_complete_command "$command" && return local expansion=$(__git_aliased_command "$command") if [ -n "$expansion" ]; then words[1]=$expansion - completion_func="_git_${expansion//-/_}" - declare -f $completion_func >/dev/null 2>/dev/null && $completion_func + __git_complete_command "$expansion" fi } @@ -3202,7 +2932,10 @@ __gitk_main () __git_complete_revlist } -if [[ -n ${ZSH_VERSION-} ]]; then +if [[ -n ${ZSH_VERSION-} ]] && + # Don't define these functions when sourced from 'git-completion.zsh', + # it has its own implementations. + [[ -z ${GIT_SOURCING_ZSH_COMPLETION-} ]]; then echo "WARNING: this script is deprecated, please see git-completion.zsh" 1>&2 autoload -U +X compinit && compinit @@ -3251,6 +2984,15 @@ if [[ -n ${ZSH_VERSION-} ]]; then compadd -Q -S "${4- }" -p "${2-}" -- ${=1} && _ret=0 } + __gitcomp_file_direct () + { + emulate -L zsh + + local IFS=$'\n' + compset -P '*[=:]' + compadd -Q -f -- ${=1} && _ret=0 + } + __gitcomp_file () { emulate -L zsh diff --git a/contrib/completion/git-completion.zsh b/contrib/completion/git-completion.zsh index c3521fbfc4..049d6b80f6 100644 --- a/contrib/completion/git-completion.zsh +++ b/contrib/completion/git-completion.zsh @@ -39,7 +39,7 @@ if [ -z "$script" ]; then test -f $e && script="$e" && break done fi -ZSH_VERSION='' . "$script" +GIT_SOURCING_ZSH_COMPLETION=y . "$script" __gitcomp () { @@ -93,6 +93,15 @@ __gitcomp_nl_append () compadd -Q -S "${4- }" -p "${2-}" -- ${=1} && _ret=0 } +__gitcomp_file_direct () +{ + emulate -L zsh + + local IFS=$'\n' + compset -P '*[=:]' + compadd -Q -f -- ${=1} && _ret=0 +} + __gitcomp_file () { emulate -L zsh diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh index c6cbef38c2..983e419d2b 100644 --- a/contrib/completion/git-prompt.sh +++ b/contrib/completion/git-prompt.sh @@ -278,11 +278,12 @@ __git_ps1_colorize_gitstring () r="$c_clear$r" } +# Helper function to read the first line of a file into a variable. +# __git_eread requires 2 arguments, the file path and the name of the +# variable, in that order. __git_eread () { - local f="$1" - shift - test -r "$f" && read "$@" <"$f" + test -r "$1" && IFS=$'\r\n' read "$2" <"$1" } # __git_ps1 accepts 0 or 1 arguments (i.e., format string) diff --git a/contrib/convert-grafts-to-replace-refs.sh b/contrib/convert-grafts-to-replace-refs.sh deleted file mode 100755 index 0cbc917b8c..0000000000 --- a/contrib/convert-grafts-to-replace-refs.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/sh - -# You should execute this script in the repository where you -# want to convert grafts to replace refs. - -GRAFTS_FILE="${GIT_DIR:-.git}/info/grafts" - -. $(git --exec-path)/git-sh-setup - -test -f "$GRAFTS_FILE" || die "Could not find graft file: '$GRAFTS_FILE'" - -grep '^[^# ]' "$GRAFTS_FILE" | -while read definition -do - if test -n "$definition" - then - echo "Converting: $definition" - git replace --graft $definition || - die "Conversion failed for: $definition" - fi -done - -mv "$GRAFTS_FILE" "$GRAFTS_FILE.bak" || - die "Could not rename '$GRAFTS_FILE' to '$GRAFTS_FILE.bak'" - -echo "Success!" -echo "All the grafts in '$GRAFTS_FILE' have been converted to replace refs!" -echo "The grafts file '$GRAFTS_FILE' has been renamed: '$GRAFTS_FILE.bak'" diff --git a/contrib/credential/netrc/Makefile b/contrib/credential/netrc/Makefile index 51b76138a5..6174e3bb83 100644 --- a/contrib/credential/netrc/Makefile +++ b/contrib/credential/netrc/Makefile @@ -1,5 +1,8 @@ +# The default target of this Makefile is... +all:: + test: - ./test.pl + ./t-git-credential-netrc.sh testverbose: - ./test.pl -d -v + ./t-git-credential-netrc.sh -d -v diff --git a/contrib/credential/netrc/git-credential-netrc b/contrib/credential/netrc/git-credential-netrc index 1571a7b269..ebfc123ec6 100755 --- a/contrib/credential/netrc/git-credential-netrc +++ b/contrib/credential/netrc/git-credential-netrc @@ -5,8 +5,9 @@ use warnings; use Getopt::Long; use File::Basename; +use Git; -my $VERSION = "0.1"; +my $VERSION = "0.2"; my %options = ( help => 0, @@ -54,6 +55,7 @@ GetOptions(\%options, "insecure|k", "verbose|v", "file|f=s@", + 'gpg|g:s', ); if ($options{help}) { @@ -62,27 +64,31 @@ if ($options{help}) { print <<EOHIPPUS; -$0 [-f AUTHFILE1] [-f AUTHFILEN] [-d] [-v] [-k] get +$0 [(-f <authfile>)...] [-g <program>] [-d] [-v] [-k] get Version $VERSION by tzz\@lifelogs.com. License: BSD. Options: - -f|--file AUTHFILE : specify netrc-style files. Files with the .gpg extension - will be decrypted by GPG before parsing. Multiple -f - arguments are OK. They are processed in order, and the - first matching entry found is returned via the credential - helper protocol (see below). + -f|--file <authfile>: specify netrc-style files. Files with the .gpg + extension will be decrypted by GPG before parsing. + Multiple -f arguments are OK. They are processed in + order, and the first matching entry found is returned + via the credential helper protocol (see below). - When no -f option is given, .authinfo.gpg, .netrc.gpg, - .authinfo, and .netrc files in your home directory are used - in this order. + When no -f option is given, .authinfo.gpg, .netrc.gpg, + .authinfo, and .netrc files in your home directory are + used in this order. - -k|--insecure : ignore bad file ownership or permissions + -g|--gpg <program> : specify the program for GPG. By default, this is the + value of gpg.program in the git repository or global + option or gpg. - -d|--debug : turn on debugging (developer info) + -k|--insecure : ignore bad file ownership or permissions - -v|--verbose : be more verbose (show files and information found) + -d|--debug : turn on debugging (developer info) + + -v|--verbose : be more verbose (show files and information found) To enable this credential helper: @@ -99,8 +105,9 @@ in the path.) git config credential.helper '$shortname -f AUTHFILE -v' -Only "get" mode is supported by this credential helper. It opens every AUTHFILE -and looks for the first entry that matches the requested search criteria: +Only "get" mode is supported by this credential helper. It opens every +<authfile> and looks for the first entry that matches the requested search +criteria: 'port|protocol': The protocol that will be used (e.g., https). (protocol=X) @@ -120,7 +127,7 @@ host=github.com protocol=https username=tzz -this credential helper will look for the first entry in every AUTHFILE that +this credential helper will look for the first entry in every <authfile> that matches machine github.com port https login tzz @@ -137,8 +144,8 @@ Then, the helper will print out whatever tokens it got from the entry, including back to "protocol". Any redundant entry tokens (part of the original query) are skipped. -Again, note that only the first matching entry from all the AUTHFILEs, processed -in the sequence given on the command line, is used. +Again, note that only the first matching entry from all the <authfile>s, +processed in the sequence given on the command line, is used. Netrc/authinfo tokens can be quoted as 'STRING' or "STRING". @@ -152,7 +159,7 @@ EOHIPPUS my $mode = shift @ARGV; # Credentials must get a parameter, so die if it's missing. -die "Syntax: $0 [-f AUTHFILE1] [-f AUTHFILEN] [-d] get" unless defined $mode; +die "Syntax: $0 [(-f <authfile>)...] [-d] get" unless defined $mode; # Only support 'get' mode; with any other unsupported ones we just exit. exit 0 unless $mode eq 'get'; @@ -172,6 +179,8 @@ unless (scalar @$files) { $files = $options{file} = [ map { glob $_ } @candidates ]; } +load_config(\%options); + my $query = read_credential_data_from_stdin(); FILE: @@ -233,7 +242,7 @@ sub load_netrc { my $io; if ($gpgmode) { - my @cmd = (qw(gpg --decrypt), $file); + my @cmd = ($options{'gpg'}, qw(--decrypt), $file); log_verbose("Using GPG to open $file: [@cmd]"); open $io, "-|", @cmd; } else { @@ -410,6 +419,14 @@ sub print_credential_data { printf "%s=%s\n", $git_token, $entry->{$git_token}; } } +sub load_config { + # load settings from git config + my $options = shift; + # set from command argument, gpg.program option, or default to gpg + $options->{'gpg'} //= Git->repository()->config('gpg.program') + // 'gpg'; + log_verbose("using $options{'gpg'} for GPG operations"); +} sub log_verbose { return unless $options{verbose}; printf STDERR @_; diff --git a/contrib/credential/netrc/t-git-credential-netrc.sh b/contrib/credential/netrc/t-git-credential-netrc.sh new file mode 100755 index 0000000000..07227d0228 --- /dev/null +++ b/contrib/credential/netrc/t-git-credential-netrc.sh @@ -0,0 +1,32 @@ +#!/bin/sh +( + cd ../../../t + test_description='git-credential-netrc' + . ./test-lib.sh + + if ! test_have_prereq PERL; then + skip_all='skipping perl interface tests, perl not available' + test_done + fi + + perl -MTest::More -e 0 2>/dev/null || { + skip_all="Perl Test::More unavailable, skipping test" + test_done + } + + # set up test repository + + test_expect_success \ + 'set up test repository' \ + 'git config --add gpg.program test.git-config-gpg' + + # The external test will outputs its own plan + test_external_has_tap=1 + + export PERL5LIB="$GITPERLLIB" + test_external \ + 'git-credential-netrc' \ + perl "$GIT_BUILD_DIR"/contrib/credential/netrc/test.pl + + test_done +) diff --git a/contrib/credential/netrc/test.command-option-gpg b/contrib/credential/netrc/test.command-option-gpg new file mode 100755 index 0000000000..d8f1285d41 --- /dev/null +++ b/contrib/credential/netrc/test.command-option-gpg @@ -0,0 +1,2 @@ +#!/bin/sh +echo machine command-option-gpg login username password password diff --git a/contrib/credential/netrc/test.git-config-gpg b/contrib/credential/netrc/test.git-config-gpg new file mode 100755 index 0000000000..65cf594c20 --- /dev/null +++ b/contrib/credential/netrc/test.git-config-gpg @@ -0,0 +1,2 @@ +#!/bin/sh +echo machine git-config-gpg login username password password diff --git a/contrib/credential/netrc/test.netrc.gpg b/contrib/credential/netrc/test.netrc.gpg new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/contrib/credential/netrc/test.netrc.gpg diff --git a/contrib/credential/netrc/test.pl b/contrib/credential/netrc/test.pl index 169b6463c3..c0fb3718b2 100755 --- a/contrib/credential/netrc/test.pl +++ b/contrib/credential/netrc/test.pl @@ -2,82 +2,115 @@ use warnings; use strict; -use Test; +use Test::More qw(no_plan); +use File::Basename; +use File::Spec::Functions qw(:DEFAULT rel2abs); use IPC::Open2; -BEGIN { plan tests => 15 } +BEGIN { + # t-git-credential-netrc.sh kicks off our testing, so we have to go + # from there. + Test::More->builder->current_test(1); +} my @global_credential_args = @ARGV; -my $netrc = './test.netrc'; -print "# Testing insecure file, nothing should be found\n"; +my $scriptDir = dirname rel2abs $0; +my ($netrc, $netrcGpg, $gcNetrc) = map { catfile $scriptDir, $_; } + qw(test.netrc + test.netrc.gpg + git-credential-netrc); +local $ENV{PATH} = join ':' + , $scriptDir + , $ENV{PATH} + ? $ENV{PATH} + : (); + +diag "Testing insecure file, nothing should be found\n"; chmod 0644, $netrc; my $cred = run_credential(['-f', $netrc, 'get'], { host => 'github.com' }); -ok(scalar keys %$cred, 0, "Got 0 keys from insecure file"); +ok(scalar keys %$cred == 0, "Got 0 keys from insecure file"); -print "# Testing missing file, nothing should be found\n"; +diag "Testing missing file, nothing should be found\n"; chmod 0644, $netrc; $cred = run_credential(['-f', '///nosuchfile///', 'get'], { host => 'github.com' }); -ok(scalar keys %$cred, 0, "Got 0 keys from missing file"); +ok(scalar keys %$cred == 0, "Got 0 keys from missing file"); chmod 0600, $netrc; -print "# Testing with invalid data\n"; +diag "Testing with invalid data\n"; $cred = run_credential(['-f', $netrc, 'get'], "bad data"); -ok(scalar keys %$cred, 4, "Got first found keys with bad data"); +ok(scalar keys %$cred == 4, "Got first found keys with bad data"); -print "# Testing netrc file for a missing corovamilkbar entry\n"; +diag "Testing netrc file for a missing corovamilkbar entry\n"; $cred = run_credential(['-f', $netrc, 'get'], { host => 'corovamilkbar' }); -ok(scalar keys %$cred, 0, "Got no corovamilkbar keys"); +ok(scalar keys %$cred == 0, "Got no corovamilkbar keys"); -print "# Testing netrc file for a github.com entry\n"; +diag "Testing netrc file for a github.com entry\n"; $cred = run_credential(['-f', $netrc, 'get'], { host => 'github.com' }); -ok(scalar keys %$cred, 2, "Got 2 Github keys"); +ok(scalar keys %$cred == 2, "Got 2 Github keys"); -ok($cred->{password}, 'carolknows', "Got correct Github password"); -ok($cred->{username}, 'carol', "Got correct Github username"); +is($cred->{password}, 'carolknows', "Got correct Github password"); +is($cred->{username}, 'carol', "Got correct Github username"); -print "# Testing netrc file for a username-specific entry\n"; +diag "Testing netrc file for a username-specific entry\n"; $cred = run_credential(['-f', $netrc, 'get'], { host => 'imap', username => 'bob' }); -ok(scalar keys %$cred, 2, "Got 2 username-specific keys"); +ok(scalar keys %$cred == 2, "Got 2 username-specific keys"); -ok($cred->{password}, 'bobwillknow', "Got correct user-specific password"); -ok($cred->{protocol}, 'imaps', "Got correct user-specific protocol"); +is($cred->{password}, 'bobwillknow', "Got correct user-specific password"); +is($cred->{protocol}, 'imaps', "Got correct user-specific protocol"); -print "# Testing netrc file for a host:port-specific entry\n"; +diag "Testing netrc file for a host:port-specific entry\n"; $cred = run_credential(['-f', $netrc, 'get'], { host => 'imap2:1099' }); -ok(scalar keys %$cred, 2, "Got 2 host:port-specific keys"); +ok(scalar keys %$cred == 2, "Got 2 host:port-specific keys"); -ok($cred->{password}, 'tzzknow', "Got correct host:port-specific password"); -ok($cred->{username}, 'tzz', "Got correct host:port-specific username"); +is($cred->{password}, 'tzzknow', "Got correct host:port-specific password"); +is($cred->{username}, 'tzz', "Got correct host:port-specific username"); -print "# Testing netrc file that 'host:port kills host' entry\n"; +diag "Testing netrc file that 'host:port kills host' entry\n"; $cred = run_credential(['-f', $netrc, 'get'], { host => 'imap2' }); -ok(scalar keys %$cred, 2, "Got 2 'host:port kills host' keys"); +ok(scalar keys %$cred == 2, "Got 2 'host:port kills host' keys"); + +is($cred->{password}, 'bobwillknow', "Got correct 'host:port kills host' password"); +is($cred->{username}, 'bob', "Got correct 'host:port kills host' username"); + +diag 'Testing netrc file decryption by git config gpg.program setting\n'; +$cred = run_credential( ['-f', $netrcGpg, 'get'] + , { host => 'git-config-gpg' } + ); + +ok(scalar keys %$cred == 2, 'Got keys decrypted by git config option'); + +diag 'Testing netrc file decryption by gpg option\n'; +$cred = run_credential( ['-f', $netrcGpg, '-g', 'test.command-option-gpg', 'get'] + , { host => 'command-option-gpg' } + ); + +ok(scalar keys %$cred == 2, 'Got keys decrypted by command option'); -ok($cred->{password}, 'bobwillknow', "Got correct 'host:port kills host' password"); -ok($cred->{username}, 'bob', "Got correct 'host:port kills host' username"); +my $is_passing = eval { Test::More->is_passing }; +exit($is_passing ? 0 : 1) unless $@ =~ /Can't locate object method/; sub run_credential { my $args = shift @_; my $data = shift @_; my $pid = open2(my $chld_out, my $chld_in, - './git-credential-netrc', @global_credential_args, + $gcNetrc, @global_credential_args, @$args); die "Couldn't open pipe to netrc credential helper: $!" unless $pid; diff --git a/contrib/diff-highlight/DiffHighlight.pm b/contrib/diff-highlight/DiffHighlight.pm index 663992e530..536754583b 100644 --- a/contrib/diff-highlight/DiffHighlight.pm +++ b/contrib/diff-highlight/DiffHighlight.pm @@ -21,37 +21,82 @@ my $RESET = "\x1b[m"; my $COLOR = qr/\x1b\[[0-9;]*m/; my $BORING = qr/$COLOR|\s/; -# The patch portion of git log -p --graph should only ever have preceding | and -# not / or \ as merge history only shows up on the commit line. -my $GRAPH = qr/$COLOR?\|$COLOR?\s+/; - my @removed; my @added; my $in_hunk; +my $graph_indent = 0; our $line_cb = sub { print @_ }; our $flush_cb = sub { local $| = 1 }; -sub handle_line { +# Count the visible width of a string, excluding any terminal color sequences. +sub visible_width { local $_ = shift; + my $ret = 0; + while (length) { + if (s/^$COLOR//) { + # skip colors + } elsif (s/^.//) { + $ret++; + } + } + return $ret; +} + +# Return a substring of $str, omitting $len visible characters from the +# beginning, where terminal color sequences do not count as visible. +sub visible_substr { + my ($str, $len) = @_; + while ($len > 0) { + if ($str =~ s/^$COLOR//) { + next + } + $str =~ s/^.//; + $len--; + } + return $str; +} + +sub handle_line { + my $orig = shift; + local $_ = $orig; + + # match a graph line that begins a commit + if (/^(?:$COLOR?\|$COLOR?[ ])* # zero or more leading "|" with space + $COLOR?\*$COLOR?[ ] # a "*" with its trailing space + (?:$COLOR?\|$COLOR?[ ])* # zero or more trailing "|" + [ ]* # trailing whitespace for merges + /x) { + my $graph_prefix = $&; + + # We must flush before setting graph indent, since the + # new commit may be indented differently from what we + # queued. + flush(); + $graph_indent = visible_width($graph_prefix); + + } elsif ($graph_indent) { + if (length($_) < $graph_indent) { + $graph_indent = 0; + } else { + $_ = visible_substr($_, $graph_indent); + } + } if (!$in_hunk) { - $line_cb->($_); - $in_hunk = /^$GRAPH*$COLOR*\@\@ /; + $line_cb->($orig); + $in_hunk = /^$COLOR*\@\@ /; } - elsif (/^$GRAPH*$COLOR*-/) { - push @removed, $_; + elsif (/^$COLOR*-/) { + push @removed, $orig; } - elsif (/^$GRAPH*$COLOR*\+/) { - push @added, $_; + elsif (/^$COLOR*\+/) { + push @added, $orig; } else { - show_hunk(\@removed, \@added); - @removed = (); - @added = (); - - $line_cb->($_); - $in_hunk = /^$GRAPH*$COLOR*[\@ ]/; + flush(); + $line_cb->($orig); + $in_hunk = /^$COLOR*[\@ ]/; } # Most of the time there is enough output to keep things streaming, @@ -71,6 +116,8 @@ sub flush { # Flush any queued hunk (this can happen when there is no trailing # context in the final diff of the input). show_hunk(\@removed, \@added); + @removed = (); + @added = (); } sub highlight_stdin { @@ -226,8 +273,8 @@ sub is_pair_interesting { my $suffix_a = join('', @$a[($sa+1)..$#$a]); my $suffix_b = join('', @$b[($sb+1)..$#$b]); - return $prefix_a !~ /^$GRAPH*$COLOR*-$BORING*$/ || - $prefix_b !~ /^$GRAPH*$COLOR*\+$BORING*$/ || + return visible_substr($prefix_a, $graph_indent) !~ /^$COLOR*-$BORING*$/ || + visible_substr($prefix_b, $graph_indent) !~ /^$COLOR*\+$BORING*$/ || $suffix_a !~ /^$BORING*$/ || $suffix_b !~ /^$BORING*$/; } diff --git a/contrib/diff-highlight/t/t9400-diff-highlight.sh b/contrib/diff-highlight/t/t9400-diff-highlight.sh index 3b43dbed74..f6f5195d00 100755 --- a/contrib/diff-highlight/t/t9400-diff-highlight.sh +++ b/contrib/diff-highlight/t/t9400-diff-highlight.sh @@ -52,15 +52,17 @@ test_strip_patch_header () { # dh_test_setup_history generates a contrived graph such that we have at least # 1 nesting (E) and 2 nestings (F). # -# A branch -# / -# D---E---F master +# A---B master +# / +# D---E---F branch # # git log --all --graph # * commit -# | A +# | B # | * commit # | | F +# * | commit +# | | A # | * commit # |/ # | E @@ -68,24 +70,30 @@ test_strip_patch_header () { # D # dh_test_setup_history () { - echo "file1" >file1 && - echo "file2" >file2 && - echo "file3" >file3 && - - cat file1 >file && + echo file1 >file && git add file && + test_tick && git commit -m "D" && git checkout -b branch && - cat file2 >file && - git commit -a -m "A" && + echo file2 >file && + test_tick && + git commit -a -m "E" && git checkout master && - cat file2 >file && - git commit -a -m "E" && + echo file2 >file && + test_tick && + git commit -a -m "A" && - cat file3 >file && - git commit -a -m "F" + git checkout branch && + echo file3 >file && + test_tick && + git commit -a -m "F" && + + git checkout master && + echo file3 >file && + test_tick && + git commit -a -m "B" } left_trim () { @@ -246,16 +254,25 @@ test_expect_failure 'diff-highlight treats combining code points as a unit' ' test_expect_success 'diff-highlight works with the --graph option' ' dh_test_setup_history && - # topo-order so that the order of the commits is the same as with --graph + # date-order so that the commits are interleaved for both # trim graph elements so we can do a diff # trim leading space because our trim_graph is not perfect - git log --branches -p --topo-order | + git log --branches -p --date-order | "$DIFF_HIGHLIGHT" | left_trim >graph.exp && - git log --branches -p --graph | + git log --branches -p --date-order --graph | "$DIFF_HIGHLIGHT" | trim_graph | left_trim >graph.act && test_cmp graph.exp graph.act ' +# Just reuse the previous graph test, but with --color. Our trimming +# doesn't know about color, so just sanity check that something got +# highlighted. +test_expect_success 'diff-highlight works with color graph' ' + git log --branches -p --date-order --graph --color | + "$DIFF_HIGHLIGHT" | trim_graph | left_trim >graph && + grep "\[7m" graph +' + # Most combined diffs won't meet diff-highlight's line-number filter. So we # create one here where one side drops a line and the other modifies it. That # should result in a diff like: @@ -293,4 +310,32 @@ test_expect_success 'diff-highlight ignores combined diffs' ' test_cmp expect actual ' +test_expect_success 'diff-highlight handles --graph with leading dash' ' + cat >file <<-\EOF && + before + the old line + -leading dash + EOF + git add file && + git commit -m before && + + sed s/old/new/ <file >file.tmp && + mv file.tmp file && + git add file && + git commit -m after && + + cat >expect <<-EOF && + --- a/file + +++ b/file + @@ -1,3 +1,3 @@ + before + -the ${CW}old${CR} line + +the ${CW}new${CR} line + -leading dash + EOF + git log --graph -p -1 | "$DIFF_HIGHLIGHT" >actual.raw && + trim_graph <actual.raw | sed -n "/^---/,\$p" >actual && + test_cmp expect actual +' + test_done diff --git a/contrib/emacs/.gitignore b/contrib/emacs/.gitignore deleted file mode 100644 index c531d9867f..0000000000 --- a/contrib/emacs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.elc diff --git a/contrib/emacs/Makefile b/contrib/emacs/Makefile deleted file mode 100644 index 24d9312941..0000000000 --- a/contrib/emacs/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -## Build and install stuff - -EMACS = emacs - -ELC = git.elc git-blame.elc -INSTALL ?= install -INSTALL_ELC = $(INSTALL) -m 644 -prefix ?= $(HOME) -emacsdir = $(prefix)/share/emacs/site-lisp -RM ?= rm -f - -all: $(ELC) - -install: all - $(INSTALL) -d $(DESTDIR)$(emacsdir) - $(INSTALL_ELC) $(ELC:.elc=.el) $(ELC) $(DESTDIR)$(emacsdir) - -%.elc: %.el - $(EMACS) -batch -f batch-byte-compile $< - -clean:; $(RM) $(ELC) diff --git a/contrib/emacs/README b/contrib/emacs/README index 82368bdbff..977a16f1e3 100644 --- a/contrib/emacs/README +++ b/contrib/emacs/README @@ -1,30 +1,24 @@ -This directory contains various modules for Emacs support. +This directory used to contain various modules for Emacs support. -To make the modules available to Emacs, you should add this directory -to your load-path, and then require the modules you want. This can be -done by adding to your .emacs something like this: +These were added shortly after Git was first released. Since then +Emacs's own support for Git got better than what was offered by these +modes. There are also popular 3rd-party Git modes such as Magit which +offer replacements for these. - (add-to-list 'load-path ".../git/contrib/emacs") - (require 'git) - (require 'git-blame) - - -The following modules are available: +The following modules were available, and can be dug up from the Git +history: * git.el: - Status manager that displays the state of all the files of the - project, and provides easy access to the most frequently used git - commands. The user interface is as far as possible compatible with - the pcl-cvs mode. It can be started with `M-x git-status'. + Wrapper for "git status" that provided access to other git commands. + + Modern alternatives to this include Magit, and VC mode that ships + with Emacs. * git-blame.el: - Emacs implementation of incremental git-blame. When you turn it on - while viewing a file, the editor buffer will be updated by setting - the background of individual lines to a color that reflects which - commit it comes from. And when you move around the buffer, a - one-line summary will be shown in the echo area. + A wrapper for "git blame" written before Emacs's own vc-annotate + mode learned to invoke git-blame, which can be done via C-x v g. * vc-git.el: diff --git a/contrib/emacs/git-blame.el b/contrib/emacs/git-blame.el index 510e0f7103..6a8a2b8ff1 100644 --- a/contrib/emacs/git-blame.el +++ b/contrib/emacs/git-blame.el @@ -1,483 +1,6 @@ -;;; git-blame.el --- Minor mode for incremental blame for Git -*- coding: utf-8 -*- -;; -;; Copyright (C) 2007 David KÃ¥gedal -;; -;; Authors: David KÃ¥gedal <davidk@lysator.liu.se> -;; Created: 31 Jan 2007 -;; Message-ID: <87iren2vqx.fsf@morpheus.local> -;; License: GPL -;; Keywords: git, version control, release management -;; -;; Compatibility: Emacs21, Emacs22 and EmacsCVS -;; Git 1.5 and up - -;; This file is *NOT* part of GNU Emacs. -;; This file is distributed under the same terms as GNU Emacs. - -;; This program is free software; you can redistribute it and/or -;; modify it under the terms of the GNU General Public License as -;; published by the Free Software Foundation; either version 2 of -;; the License, or (at your option) any later version. - -;; This program is distributed in the hope that it will be -;; useful, but WITHOUT ANY WARRANTY; without even the implied -;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR -;; PURPOSE. See the GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public -;; License along with this program; if not, see -;; <http://www.gnu.org/licenses/>. - -;; http://www.fsf.org/copyleft/gpl.html - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; Commentary: -;; -;; Here is an Emacs implementation of incremental git-blame. When you -;; turn it on while viewing a file, the editor buffer will be updated by -;; setting the background of individual lines to a color that reflects -;; which commit it comes from. And when you move around the buffer, a -;; one-line summary will be shown in the echo area. - -;;; Installation: -;; -;; To use this package, put it somewhere in `load-path' (or add -;; directory with git-blame.el to `load-path'), and add the following -;; line to your .emacs: -;; -;; (require 'git-blame) -;; -;; If you do not want to load this package before it is necessary, you -;; can make use of the `autoload' feature, e.g. by adding to your .emacs -;; the following lines -;; -;; (autoload 'git-blame-mode "git-blame" -;; "Minor mode for incremental blame for Git." t) -;; -;; Then first use of `M-x git-blame-mode' would load the package. - -;;; Compatibility: -;; -;; It requires GNU Emacs 21 or later and Git 1.5.0 and up -;; -;; If you'are using Emacs 20, try changing this: -;; -;; (overlay-put ovl 'face (list :background -;; (cdr (assq 'color (cddddr info))))) -;; -;; to -;; -;; (overlay-put ovl 'face (cons 'background-color -;; (cdr (assq 'color (cddddr info))))) - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; Code: - -(eval-when-compile (require 'cl)) ; to use `push', `pop' -(require 'format-spec) - -(defface git-blame-prefix-face - '((((background dark)) (:foreground "gray" - :background "black")) - (((background light)) (:foreground "gray" - :background "white")) - (t (:weight bold))) - "The face used for the hash prefix." - :group 'git-blame) - -(defgroup git-blame nil - "A minor mode showing Git blame information." - :group 'git - :link '(function-link git-blame-mode)) - - -(defcustom git-blame-use-colors t - "Use colors to indicate commits in `git-blame-mode'." - :type 'boolean - :group 'git-blame) - -(defcustom git-blame-prefix-format - "%h %20A:" - "The format of the prefix added to each line in `git-blame' -mode. The format is passed to `format-spec' with the following format keys: - - %h - the abbreviated hash - %H - the full hash - %a - the author name - %A - the author email - %c - the committer name - %C - the committer email - %s - the commit summary -" - :group 'git-blame) - -(defcustom git-blame-mouseover-format - "%h %a %A: %s" - "The format of the description shown when pointing at a line in -`git-blame' mode. The format string is passed to `format-spec' -with the following format keys: - - %h - the abbreviated hash - %H - the full hash - %a - the author name - %A - the author email - %c - the committer name - %C - the committer email - %s - the commit summary -" - :group 'git-blame) - - -(defun git-blame-color-scale (&rest elements) - "Given a list, returns a list of triples formed with each -elements of the list. - -a b => bbb bba bab baa abb aba aaa aab" - (let (result) - (dolist (a elements) - (dolist (b elements) - (dolist (c elements) - (setq result (cons (format "#%s%s%s" a b c) result))))) - result)) - -;; (git-blame-color-scale "0c" "04" "24" "1c" "2c" "34" "14" "3c") => -;; ("#3c3c3c" "#3c3c14" "#3c3c34" "#3c3c2c" "#3c3c1c" "#3c3c24" -;; "#3c3c04" "#3c3c0c" "#3c143c" "#3c1414" "#3c1434" "#3c142c" ...) - -(defmacro git-blame-random-pop (l) - "Select a random element from L and returns it. Also remove -selected element from l." - ;; only works on lists with unique elements - `(let ((e (elt ,l (random (length ,l))))) - (setq ,l (remove e ,l)) - e)) - -(defvar git-blame-log-oneline-format - "format:[%cr] %cn: %s" - "*Formatting option used for describing current line in the minibuffer. - -This option is used to pass to git log --pretty= command-line option, -and describe which commit the current line was made.") - -(defvar git-blame-dark-colors - (git-blame-color-scale "0c" "04" "24" "1c" "2c" "34" "14" "3c") - "*List of colors (format #RGB) to use in a dark environment. - -To check out the list, evaluate (list-colors-display git-blame-dark-colors).") - -(defvar git-blame-light-colors - (git-blame-color-scale "c4" "d4" "cc" "dc" "f4" "e4" "fc" "ec") - "*List of colors (format #RGB) to use in a light environment. - -To check out the list, evaluate (list-colors-display git-blame-light-colors).") - -(defvar git-blame-colors '() - "Colors used by git-blame. The list is built once when activating git-blame -minor mode.") - -(defvar git-blame-ancient-color "dark green" - "*Color to be used for ancient commit.") - -(defvar git-blame-autoupdate t - "*Automatically update the blame display while editing") - -(defvar git-blame-proc nil - "The running git-blame process") -(make-variable-buffer-local 'git-blame-proc) - -(defvar git-blame-overlays nil - "The git-blame overlays used in the current buffer.") -(make-variable-buffer-local 'git-blame-overlays) - -(defvar git-blame-cache nil - "A cache of git-blame information for the current buffer") -(make-variable-buffer-local 'git-blame-cache) - -(defvar git-blame-idle-timer nil - "An idle timer that updates the blame") -(make-variable-buffer-local 'git-blame-cache) - -(defvar git-blame-update-queue nil - "A queue of update requests") -(make-variable-buffer-local 'git-blame-update-queue) - -;; FIXME: docstrings -(defvar git-blame-file nil) -(defvar git-blame-current nil) - -(defvar git-blame-mode nil) -(make-variable-buffer-local 'git-blame-mode) - -(defvar git-blame-mode-line-string " blame" - "String to display on the mode line when git-blame is active.") - -(or (assq 'git-blame-mode minor-mode-alist) - (setq minor-mode-alist - (cons '(git-blame-mode git-blame-mode-line-string) minor-mode-alist))) - -;;;###autoload -(defun git-blame-mode (&optional arg) - "Toggle minor mode for displaying Git blame - -With prefix ARG, turn the mode on if ARG is positive." - (interactive "P") - (cond - ((null arg) - (if git-blame-mode (git-blame-mode-off) (git-blame-mode-on))) - ((> (prefix-numeric-value arg) 0) (git-blame-mode-on)) - (t (git-blame-mode-off)))) - -(defun git-blame-mode-on () - "Turn on git-blame mode. - -See also function `git-blame-mode'." - (make-local-variable 'git-blame-colors) - (if git-blame-autoupdate - (add-hook 'after-change-functions 'git-blame-after-change nil t) - (remove-hook 'after-change-functions 'git-blame-after-change t)) - (git-blame-cleanup) - (let ((bgmode (cdr (assoc 'background-mode (frame-parameters))))) - (if (eq bgmode 'dark) - (setq git-blame-colors git-blame-dark-colors) - (setq git-blame-colors git-blame-light-colors))) - (setq git-blame-cache (make-hash-table :test 'equal)) - (setq git-blame-mode t) - (git-blame-run)) - -(defun git-blame-mode-off () - "Turn off git-blame mode. - -See also function `git-blame-mode'." - (git-blame-cleanup) - (if git-blame-idle-timer (cancel-timer git-blame-idle-timer)) - (setq git-blame-mode nil)) - -;;;###autoload -(defun git-reblame () - "Recalculate all blame information in the current buffer" - (interactive) - (unless git-blame-mode - (error "Git-blame is not active")) - - (git-blame-cleanup) - (git-blame-run)) - -(defun git-blame-run (&optional startline endline) - (if git-blame-proc - ;; Should maybe queue up a new run here - (message "Already running git blame") - (let ((display-buf (current-buffer)) - (blame-buf (get-buffer-create - (concat " git blame for " (buffer-name)))) - (args '("--incremental" "--contents" "-"))) - (if startline - (setq args (append args - (list "-L" (format "%d,%d" startline endline))))) - (setq args (append args - (list (file-name-nondirectory buffer-file-name)))) - (setq git-blame-proc - (apply 'start-process - "git-blame" blame-buf - "git" "blame" - args)) - (with-current-buffer blame-buf - (erase-buffer) - (make-local-variable 'git-blame-file) - (make-local-variable 'git-blame-current) - (setq git-blame-file display-buf) - (setq git-blame-current nil)) - (set-process-filter git-blame-proc 'git-blame-filter) - (set-process-sentinel git-blame-proc 'git-blame-sentinel) - (process-send-region git-blame-proc (point-min) (point-max)) - (process-send-eof git-blame-proc)))) - -(defun remove-git-blame-text-properties (start end) - (let ((modified (buffer-modified-p)) - (inhibit-read-only t)) - (remove-text-properties start end '(point-entered nil)) - (set-buffer-modified-p modified))) - -(defun git-blame-cleanup () - "Remove all blame properties" - (mapc 'delete-overlay git-blame-overlays) - (setq git-blame-overlays nil) - (remove-git-blame-text-properties (point-min) (point-max))) - -(defun git-blame-update-region (start end) - "Rerun blame to get updates between START and END" - (let ((overlays (overlays-in start end))) - (while overlays - (let ((overlay (pop overlays))) - (if (< (overlay-start overlay) start) - (setq start (overlay-start overlay))) - (if (> (overlay-end overlay) end) - (setq end (overlay-end overlay))) - (setq git-blame-overlays (delete overlay git-blame-overlays)) - (delete-overlay overlay)))) - (remove-git-blame-text-properties start end) - ;; We can be sure that start and end are at line breaks - (git-blame-run (1+ (count-lines (point-min) start)) - (count-lines (point-min) end))) - -(defun git-blame-sentinel (proc status) - (with-current-buffer (process-buffer proc) - (with-current-buffer git-blame-file - (setq git-blame-proc nil) - (if git-blame-update-queue - (git-blame-delayed-update)))) - ;;(kill-buffer (process-buffer proc)) - ;;(message "git blame finished") - ) - -(defvar in-blame-filter nil) - -(defun git-blame-filter (proc str) - (with-current-buffer (process-buffer proc) - (save-excursion - (goto-char (process-mark proc)) - (insert-before-markers str) - (goto-char (point-min)) - (unless in-blame-filter - (let ((more t) - (in-blame-filter t)) - (while more - (setq more (git-blame-parse)))))))) - -(defun git-blame-parse () - (cond ((looking-at "\\([0-9a-f]\\{40\\}\\) \\([0-9]+\\) \\([0-9]+\\) \\([0-9]+\\)\n") - (let ((hash (match-string 1)) - (src-line (string-to-number (match-string 2))) - (res-line (string-to-number (match-string 3))) - (num-lines (string-to-number (match-string 4)))) - (delete-region (point) (match-end 0)) - (setq git-blame-current (list (git-blame-new-commit hash) - src-line res-line num-lines))) - t) - ((looking-at "\\([a-z-]+\\) \\(.+\\)\n") - (let ((key (match-string 1)) - (value (match-string 2))) - (delete-region (point) (match-end 0)) - (git-blame-add-info (car git-blame-current) key value) - (when (string= key "filename") - (git-blame-create-overlay (car git-blame-current) - (caddr git-blame-current) - (cadddr git-blame-current)) - (setq git-blame-current nil))) - t) - (t - nil))) - -(defun git-blame-new-commit (hash) - (with-current-buffer git-blame-file - (or (gethash hash git-blame-cache) - ;; Assign a random color to each new commit info - ;; Take care not to select the same color multiple times - (let* ((color (if git-blame-colors - (git-blame-random-pop git-blame-colors) - git-blame-ancient-color)) - (info `(,hash (color . ,color)))) - (puthash hash info git-blame-cache) - info)))) - -(defun git-blame-create-overlay (info start-line num-lines) - (with-current-buffer git-blame-file - (save-excursion - (let ((inhibit-point-motion-hooks t) - (inhibit-modification-hooks t)) - (goto-char (point-min)) - (forward-line (1- start-line)) - (let* ((start (point)) - (end (progn (forward-line num-lines) (point))) - (ovl (make-overlay start end)) - (hash (car info)) - (spec `((?h . ,(substring hash 0 6)) - (?H . ,hash) - (?a . ,(git-blame-get-info info 'author)) - (?A . ,(git-blame-get-info info 'author-mail)) - (?c . ,(git-blame-get-info info 'committer)) - (?C . ,(git-blame-get-info info 'committer-mail)) - (?s . ,(git-blame-get-info info 'summary))))) - (push ovl git-blame-overlays) - (overlay-put ovl 'git-blame info) - (overlay-put ovl 'help-echo - (format-spec git-blame-mouseover-format spec)) - (if git-blame-use-colors - (overlay-put ovl 'face (list :background - (cdr (assq 'color (cdr info)))))) - (overlay-put ovl 'line-prefix - (propertize (format-spec git-blame-prefix-format spec) - 'face 'git-blame-prefix-face))))))) - -(defun git-blame-add-info (info key value) - (nconc info (list (cons (intern key) value)))) - -(defun git-blame-get-info (info key) - (cdr (assq key (cdr info)))) - -(defun git-blame-current-commit () - (let ((info (get-char-property (point) 'git-blame))) - (if info - (car info) - (error "No commit info")))) - -(defun git-describe-commit (hash) - (with-temp-buffer - (call-process "git" nil t nil - "log" "-1" - (concat "--pretty=" git-blame-log-oneline-format) - hash) - (buffer-substring (point-min) (point-max)))) - -(defvar git-blame-last-identification nil) -(make-variable-buffer-local 'git-blame-last-identification) -(defun git-blame-identify (&optional hash) - (interactive) - (let ((info (gethash (or hash (git-blame-current-commit)) git-blame-cache))) - (when (and info (not (eq info git-blame-last-identification))) - (message "%s" (nth 4 info)) - (setq git-blame-last-identification info)))) - -;; (defun git-blame-after-save () -;; (when git-blame-mode -;; (git-blame-cleanup) -;; (git-blame-run))) -;; (add-hook 'after-save-hook 'git-blame-after-save) - -(defun git-blame-after-change (start end length) - (when git-blame-mode - (git-blame-enq-update start end))) - -(defvar git-blame-last-update nil) -(make-variable-buffer-local 'git-blame-last-update) -(defun git-blame-enq-update (start end) - "Mark the region between START and END as needing blame update" - ;; Try to be smart and avoid multiple callouts for sequential - ;; editing - (cond ((and git-blame-last-update - (= start (cdr git-blame-last-update))) - (setcdr git-blame-last-update end)) - ((and git-blame-last-update - (= end (car git-blame-last-update))) - (setcar git-blame-last-update start)) - (t - (setq git-blame-last-update (cons start end)) - (setq git-blame-update-queue (nconc git-blame-update-queue - (list git-blame-last-update))))) - (unless (or git-blame-proc git-blame-idle-timer) - (setq git-blame-idle-timer - (run-with-idle-timer 0.5 nil 'git-blame-delayed-update)))) - -(defun git-blame-delayed-update () - (setq git-blame-idle-timer nil) - (if git-blame-update-queue - (let ((first (pop git-blame-update-queue)) - (inhibit-point-motion-hooks t)) - (git-blame-update-region (car first) (cdr first))))) - -(provide 'git-blame) - -;;; git-blame.el ends here +(error "git-blame.el no longer ships with git. It's recommended +to replace its use with Emacs's own vc-annotate. See +contrib/emacs/README in git's +sources (https://github.com/git/git/blob/master/contrib/emacs/README) +for more info on suggested alternatives and for why this +happened.") diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 97919f2d73..03f926281f 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1,1704 +1,6 @@ -;;; git.el --- A user interface for git - -;; Copyright (C) 2005, 2006, 2007, 2008, 2009 Alexandre Julliard <julliard@winehq.org> - -;; Version: 1.0 - -;; This program is free software; you can redistribute it and/or -;; modify it under the terms of the GNU General Public License as -;; published by the Free Software Foundation; either version 2 of -;; the License, or (at your option) any later version. -;; -;; This program is distributed in the hope that it will be -;; useful, but WITHOUT ANY WARRANTY; without even the implied -;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR -;; PURPOSE. See the GNU General Public License for more details. -;; -;; You should have received a copy of the GNU General Public -;; License along with this program; if not, see -;; <http://www.gnu.org/licenses/>. - -;;; Commentary: - -;; This file contains an interface for the git version control -;; system. It provides easy access to the most frequently used git -;; commands. The user interface is as far as possible identical to -;; that of the PCL-CVS mode. -;; -;; To install: put this file on the load-path and place the following -;; in your .emacs file: -;; -;; (require 'git) -;; -;; To start: `M-x git-status' -;; -;; TODO -;; - diff against other branch -;; - renaming files from the status buffer -;; - creating tags -;; - fetch/pull -;; - revlist browser -;; - git-show-branch browser -;; - -;;; Compatibility: -;; -;; This file works on GNU Emacs 21 or later. It may work on older -;; versions but this is not guaranteed. -;; -;; It may work on XEmacs 21, provided that you first install the ewoc -;; and log-edit packages. -;; - -(eval-when-compile (require 'cl)) -(require 'ewoc) -(require 'log-edit) -(require 'easymenu) - - -;;;; Customizations -;;;; ------------------------------------------------------------ - -(defgroup git nil - "A user interface for the git versioning system." - :group 'tools) - -(defcustom git-committer-name nil - "User name to use for commits. -The default is to fall back to the repository config, -then to `add-log-full-name' and then to `user-full-name'." - :group 'git - :type '(choice (const :tag "Default" nil) - (string :tag "Name"))) - -(defcustom git-committer-email nil - "Email address to use for commits. -The default is to fall back to the git repository config, -then to `add-log-mailing-address' and then to `user-mail-address'." - :group 'git - :type '(choice (const :tag "Default" nil) - (string :tag "Email"))) - -(defcustom git-commits-coding-system nil - "Default coding system for the log message of git commits." - :group 'git - :type '(choice (const :tag "From repository config" nil) - (coding-system))) - -(defcustom git-append-signed-off-by nil - "Whether to append a Signed-off-by line to the commit message before editing." - :group 'git - :type 'boolean) - -(defcustom git-reuse-status-buffer t - "Whether `git-status' should try to reuse an existing buffer -if there is already one that displays the same directory." - :group 'git - :type 'boolean) - -(defcustom git-per-dir-ignore-file ".gitignore" - "Name of the per-directory ignore file." - :group 'git - :type 'string) - -(defcustom git-show-uptodate nil - "Whether to display up-to-date files." - :group 'git - :type 'boolean) - -(defcustom git-show-ignored nil - "Whether to display ignored files." - :group 'git - :type 'boolean) - -(defcustom git-show-unknown t - "Whether to display unknown files." - :group 'git - :type 'boolean) - - -(defface git-status-face - '((((class color) (background light)) (:foreground "purple")) - (((class color) (background dark)) (:foreground "salmon"))) - "Git mode face used to highlight added and modified files." - :group 'git) - -(defface git-unmerged-face - '((((class color) (background light)) (:foreground "red" :bold t)) - (((class color) (background dark)) (:foreground "red" :bold t))) - "Git mode face used to highlight unmerged files." - :group 'git) - -(defface git-unknown-face - '((((class color) (background light)) (:foreground "goldenrod" :bold t)) - (((class color) (background dark)) (:foreground "goldenrod" :bold t))) - "Git mode face used to highlight unknown files." - :group 'git) - -(defface git-uptodate-face - '((((class color) (background light)) (:foreground "grey60")) - (((class color) (background dark)) (:foreground "grey40"))) - "Git mode face used to highlight up-to-date files." - :group 'git) - -(defface git-ignored-face - '((((class color) (background light)) (:foreground "grey60")) - (((class color) (background dark)) (:foreground "grey40"))) - "Git mode face used to highlight ignored files." - :group 'git) - -(defface git-mark-face - '((((class color) (background light)) (:foreground "red" :bold t)) - (((class color) (background dark)) (:foreground "tomato" :bold t))) - "Git mode face used for the file marks." - :group 'git) - -(defface git-header-face - '((((class color) (background light)) (:foreground "blue")) - (((class color) (background dark)) (:foreground "blue"))) - "Git mode face used for commit headers." - :group 'git) - -(defface git-separator-face - '((((class color) (background light)) (:foreground "brown")) - (((class color) (background dark)) (:foreground "brown"))) - "Git mode face used for commit separator." - :group 'git) - -(defface git-permission-face - '((((class color) (background light)) (:foreground "green" :bold t)) - (((class color) (background dark)) (:foreground "green" :bold t))) - "Git mode face used for permission changes." - :group 'git) - - -;;;; Utilities -;;;; ------------------------------------------------------------ - -(defconst git-log-msg-separator "--- log message follows this line ---") - -(defvar git-log-edit-font-lock-keywords - `(("^\\(Author:\\|Date:\\|Merge:\\|Signed-off-by:\\)\\(.*\\)$" - (1 font-lock-keyword-face) - (2 font-lock-function-name-face)) - (,(concat "^\\(" (regexp-quote git-log-msg-separator) "\\)$") - (1 font-lock-comment-face)))) - -(defun git-get-env-strings (env) - "Build a list of NAME=VALUE strings from a list of environment strings." - (mapcar (lambda (entry) (concat (car entry) "=" (cdr entry))) env)) - -(defun git-call-process (buffer &rest args) - "Wrapper for call-process that sets environment strings." - (apply #'call-process "git" nil buffer nil args)) - -(defun git-call-process-display-error (&rest args) - "Wrapper for call-process that displays error messages." - (let* ((dir default-directory) - (buffer (get-buffer-create "*Git Command Output*")) - (ok (with-current-buffer buffer - (let ((default-directory dir) - (buffer-read-only nil)) - (erase-buffer) - (eq 0 (apply #'git-call-process (list buffer t) args)))))) - (unless ok (display-message-or-buffer buffer)) - ok)) - -(defun git-call-process-string (&rest args) - "Wrapper for call-process that returns the process output as a string, -or nil if the git command failed." - (with-temp-buffer - (and (eq 0 (apply #'git-call-process t args)) - (buffer-string)))) - -(defun git-call-process-string-display-error (&rest args) - "Wrapper for call-process that displays error message and returns -the process output as a string, or nil if the git command failed." - (with-temp-buffer - (if (eq 0 (apply #'git-call-process (list t t) args)) - (buffer-string) - (display-message-or-buffer (current-buffer)) - nil))) - -(defun git-run-process-region (buffer start end program args) - "Run a git process with a buffer region as input." - (let ((output-buffer (current-buffer)) - (dir default-directory)) - (with-current-buffer buffer - (cd dir) - (apply #'call-process-region start end program - nil (list output-buffer t) nil args)))) - -(defun git-run-command-buffer (buffer-name &rest args) - "Run a git command, sending the output to a buffer named BUFFER-NAME." - (let ((dir default-directory) - (buffer (get-buffer-create buffer-name))) - (message "Running git %s..." (car args)) - (with-current-buffer buffer - (let ((default-directory dir) - (buffer-read-only nil)) - (erase-buffer) - (apply #'git-call-process buffer args))) - (message "Running git %s...done" (car args)) - buffer)) - -(defun git-run-command-region (buffer start end env &rest args) - "Run a git command with specified buffer region as input." - (with-temp-buffer - (if (eq 0 (if env - (git-run-process-region - buffer start end "env" - (append (git-get-env-strings env) (list "git") args)) - (git-run-process-region buffer start end "git" args))) - (buffer-string) - (display-message-or-buffer (current-buffer)) - nil))) - -(defun git-run-hook (hook env &rest args) - "Run a git hook and display its output if any." - (let ((dir default-directory) - (hook-name (expand-file-name (concat ".git/hooks/" hook)))) - (or (not (file-executable-p hook-name)) - (let (status (buffer (get-buffer-create "*Git Hook Output*"))) - (with-current-buffer buffer - (erase-buffer) - (cd dir) - (setq status - (if env - (apply #'call-process "env" nil (list buffer t) nil - (append (git-get-env-strings env) (list hook-name) args)) - (apply #'call-process hook-name nil (list buffer t) nil args)))) - (display-message-or-buffer buffer) - (eq 0 status))))) - -(defun git-get-string-sha1 (string) - "Read a SHA1 from the specified string." - (and string - (string-match "[0-9a-f]\\{40\\}" string) - (match-string 0 string))) - -(defun git-get-committer-name () - "Return the name to use as GIT_COMMITTER_NAME." - ; copied from log-edit - (or git-committer-name - (git-config "user.name") - (and (boundp 'add-log-full-name) add-log-full-name) - (and (fboundp 'user-full-name) (user-full-name)) - (and (boundp 'user-full-name) user-full-name))) - -(defun git-get-committer-email () - "Return the email address to use as GIT_COMMITTER_EMAIL." - ; copied from log-edit - (or git-committer-email - (git-config "user.email") - (and (boundp 'add-log-mailing-address) add-log-mailing-address) - (and (fboundp 'user-mail-address) (user-mail-address)) - (and (boundp 'user-mail-address) user-mail-address))) - -(defun git-get-commits-coding-system () - "Return the coding system to use for commits." - (let ((repo-config (git-config "i18n.commitencoding"))) - (or git-commits-coding-system - (and repo-config - (fboundp 'locale-charset-to-coding-system) - (locale-charset-to-coding-system repo-config)) - 'utf-8))) - -(defun git-get-logoutput-coding-system () - "Return the coding system used for git-log output." - (let ((repo-config (or (git-config "i18n.logoutputencoding") - (git-config "i18n.commitencoding")))) - (or git-commits-coding-system - (and repo-config - (fboundp 'locale-charset-to-coding-system) - (locale-charset-to-coding-system repo-config)) - 'utf-8))) - -(defun git-escape-file-name (name) - "Escape a file name if necessary." - (if (string-match "[\n\t\"\\]" name) - (concat "\"" - (mapconcat (lambda (c) - (case c - (?\n "\\n") - (?\t "\\t") - (?\\ "\\\\") - (?\" "\\\"") - (t (char-to-string c)))) - name "") - "\"") - name)) - -(defun git-success-message (text files) - "Print a success message after having handled FILES." - (let ((n (length files))) - (if (equal n 1) - (message "%s %s" text (car files)) - (message "%s %d files" text n)))) - -(defun git-get-top-dir (dir) - "Retrieve the top-level directory of a git tree." - (let ((cdup (with-output-to-string - (with-current-buffer standard-output - (cd dir) - (unless (eq 0 (git-call-process t "rev-parse" "--show-cdup")) - (error "cannot find top-level git tree for %s." dir)))))) - (expand-file-name (concat (file-name-as-directory dir) - (car (split-string cdup "\n")))))) - -;stolen from pcl-cvs -(defun git-append-to-ignore (file) - "Add a file name to the ignore file in its directory." - (let* ((fullname (expand-file-name file)) - (dir (file-name-directory fullname)) - (name (file-name-nondirectory fullname)) - (ignore-name (expand-file-name git-per-dir-ignore-file dir)) - (created (not (file-exists-p ignore-name)))) - (save-window-excursion - (set-buffer (find-file-noselect ignore-name)) - (goto-char (point-max)) - (unless (zerop (current-column)) (insert "\n")) - (insert "/" name "\n") - (sort-lines nil (point-min) (point-max)) - (save-buffer)) - (when created - (git-call-process nil "update-index" "--add" "--" (file-relative-name ignore-name))) - (git-update-status-files (list (file-relative-name ignore-name))))) - -; propertize definition for XEmacs, stolen from erc-compat -(eval-when-compile - (unless (fboundp 'propertize) - (defun propertize (string &rest props) - (let ((string (copy-sequence string))) - (while props - (put-text-property 0 (length string) (nth 0 props) (nth 1 props) string) - (setq props (cddr props))) - string)))) - -;;;; Wrappers for basic git commands -;;;; ------------------------------------------------------------ - -(defun git-rev-parse (rev) - "Parse a revision name and return its SHA1." - (git-get-string-sha1 - (git-call-process-string "rev-parse" rev))) - -(defun git-config (key) - "Retrieve the value associated to KEY in the git repository config file." - (let ((str (git-call-process-string "config" key))) - (and str (car (split-string str "\n"))))) - -(defun git-symbolic-ref (ref) - "Wrapper for the git-symbolic-ref command." - (let ((str (git-call-process-string "symbolic-ref" ref))) - (and str (car (split-string str "\n"))))) - -(defun git-update-ref (ref newval &optional oldval reason) - "Update a reference by calling git-update-ref." - (let ((args (and oldval (list oldval)))) - (when newval (push newval args)) - (push ref args) - (when reason - (push reason args) - (push "-m" args)) - (unless newval (push "-d" args)) - (apply 'git-call-process-display-error "update-ref" args))) - -(defun git-for-each-ref (&rest specs) - "Return a list of refs using git-for-each-ref. -Each entry is a cons of (SHORT-NAME . FULL-NAME)." - (let (refs) - (with-temp-buffer - (apply #'git-call-process t "for-each-ref" "--format=%(refname)" specs) - (goto-char (point-min)) - (while (re-search-forward "^[^/\n]+/[^/\n]+/\\(.+\\)$" nil t) - (push (cons (match-string 1) (match-string 0)) refs))) - (nreverse refs))) - -(defun git-read-tree (tree &optional index-file) - "Read a tree into the index file." - (let ((process-environment - (append (and index-file (list (concat "GIT_INDEX_FILE=" index-file))) process-environment))) - (apply 'git-call-process-display-error "read-tree" (if tree (list tree))))) - -(defun git-write-tree (&optional index-file) - "Call git-write-tree and return the resulting tree SHA1 as a string." - (let ((process-environment - (append (and index-file (list (concat "GIT_INDEX_FILE=" index-file))) process-environment))) - (git-get-string-sha1 - (git-call-process-string-display-error "write-tree")))) - -(defun git-commit-tree (buffer tree parent) - "Create a commit and possibly update HEAD. -Create a commit with the message in BUFFER using the tree with hash TREE. -Use PARENT as the parent of the new commit. If PARENT is the current \"HEAD\", -update the \"HEAD\" reference to the new commit." - (let ((author-name (git-get-committer-name)) - (author-email (git-get-committer-email)) - (subject "commit (initial): ") - author-date log-start log-end args coding-system-for-write) - (when parent - (setq subject "commit: ") - (push "-p" args) - (push parent args)) - (with-current-buffer buffer - (goto-char (point-min)) - (if - (setq log-start (re-search-forward (concat "^" (regexp-quote git-log-msg-separator) "\n") nil t)) - (save-restriction - (narrow-to-region (point-min) log-start) - (goto-char (point-min)) - (when (re-search-forward "^Author: +\\(.*?\\) *<\\(.*\\)> *$" nil t) - (setq author-name (match-string 1) - author-email (match-string 2))) - (goto-char (point-min)) - (when (re-search-forward "^Date: +\\(.*\\)$" nil t) - (setq author-date (match-string 1))) - (goto-char (point-min)) - (when (re-search-forward "^Merge: +\\(.*\\)" nil t) - (setq subject "commit (merge): ") - (dolist (parent (split-string (match-string 1) " +" t)) - (push "-p" args) - (push parent args)))) - (setq log-start (point-min))) - (setq log-end (point-max)) - (goto-char log-start) - (when (re-search-forward ".*$" nil t) - (setq subject (concat subject (match-string 0)))) - (setq coding-system-for-write buffer-file-coding-system)) - (let ((commit - (git-get-string-sha1 - (let ((env `(("GIT_AUTHOR_NAME" . ,author-name) - ("GIT_AUTHOR_EMAIL" . ,author-email) - ("GIT_COMMITTER_NAME" . ,(git-get-committer-name)) - ("GIT_COMMITTER_EMAIL" . ,(git-get-committer-email))))) - (when author-date (push `("GIT_AUTHOR_DATE" . ,author-date) env)) - (apply #'git-run-command-region - buffer log-start log-end env - "commit-tree" tree (nreverse args)))))) - (when commit (git-update-ref "HEAD" commit parent subject)) - commit))) - -(defun git-empty-db-p () - "Check if the git db is empty (no commit done yet)." - (not (eq 0 (git-call-process nil "rev-parse" "--verify" "HEAD")))) - -(defun git-get-merge-heads () - "Retrieve the merge heads from the MERGE_HEAD file if present." - (let (heads) - (when (file-readable-p ".git/MERGE_HEAD") - (with-temp-buffer - (insert-file-contents ".git/MERGE_HEAD" nil nil nil t) - (goto-char (point-min)) - (while (re-search-forward "[0-9a-f]\\{40\\}" nil t) - (push (match-string 0) heads)))) - (nreverse heads))) - -(defun git-get-commit-description (commit) - "Get a one-line description of COMMIT." - (let ((coding-system-for-read (git-get-logoutput-coding-system))) - (let ((descr (git-call-process-string "log" "--max-count=1" "--pretty=oneline" commit))) - (if (and descr (string-match "\\`\\([0-9a-f]\\{40\\}\\) *\\(.*\\)$" descr)) - (concat (substring (match-string 1 descr) 0 10) " - " (match-string 2 descr)) - descr)))) - -;;;; File info structure -;;;; ------------------------------------------------------------ - -; fileinfo structure stolen from pcl-cvs -(defstruct (git-fileinfo - (:copier nil) - (:constructor git-create-fileinfo (state name &optional old-perm new-perm rename-state orig-name marked)) - (:conc-name git-fileinfo->)) - marked ;; t/nil - state ;; current state - name ;; file name - old-perm new-perm ;; permission flags - rename-state ;; rename or copy state - orig-name ;; original name for renames or copies - needs-update ;; whether file needs to be updated - needs-refresh) ;; whether file needs to be refreshed - -(defvar git-status nil) - -(defun git-set-fileinfo-state (info state) - "Set the state of a file info." - (unless (eq (git-fileinfo->state info) state) - (setf (git-fileinfo->state info) state - (git-fileinfo->new-perm info) (git-fileinfo->old-perm info) - (git-fileinfo->rename-state info) nil - (git-fileinfo->orig-name info) nil - (git-fileinfo->needs-update info) nil - (git-fileinfo->needs-refresh info) t))) - -(defun git-status-filenames-map (status func files &rest args) - "Apply FUNC to the status files names in the FILES list. -The list must be sorted." - (when files - (let ((file (pop files)) - (node (ewoc-nth status 0))) - (while (and file node) - (let* ((info (ewoc-data node)) - (name (git-fileinfo->name info))) - (if (string-lessp name file) - (setq node (ewoc-next status node)) - (if (string-equal name file) - (apply func info args)) - (setq file (pop files)))))))) - -(defun git-set-filenames-state (status files state) - "Set the state of a list of named files. The list must be sorted" - (when files - (git-status-filenames-map status #'git-set-fileinfo-state files state) - (unless state ;; delete files whose state has been set to nil - (ewoc-filter status (lambda (info) (git-fileinfo->state info)))))) - -(defun git-state-code (code) - "Convert from a string to a added/deleted/modified state." - (case (string-to-char code) - (?M 'modified) - (?? 'unknown) - (?A 'added) - (?D 'deleted) - (?U 'unmerged) - (?T 'modified) - (t nil))) - -(defun git-status-code-as-string (code) - "Format a git status code as string." - (case code - ('modified (propertize "Modified" 'face 'git-status-face)) - ('unknown (propertize "Unknown " 'face 'git-unknown-face)) - ('added (propertize "Added " 'face 'git-status-face)) - ('deleted (propertize "Deleted " 'face 'git-status-face)) - ('unmerged (propertize "Unmerged" 'face 'git-unmerged-face)) - ('uptodate (propertize "Uptodate" 'face 'git-uptodate-face)) - ('ignored (propertize "Ignored " 'face 'git-ignored-face)) - (t "? "))) - -(defun git-file-type-as-string (old-perm new-perm) - "Return a string describing the file type based on its permissions." - (let* ((old-type (lsh (or old-perm 0) -9)) - (new-type (lsh (or new-perm 0) -9)) - (str (case new-type - (64 ;; file - (case old-type - (64 nil) - (80 " (type change symlink -> file)") - (112 " (type change subproject -> file)"))) - (80 ;; symlink - (case old-type - (64 " (type change file -> symlink)") - (112 " (type change subproject -> symlink)") - (t " (symlink)"))) - (112 ;; subproject - (case old-type - (64 " (type change file -> subproject)") - (80 " (type change symlink -> subproject)") - (t " (subproject)"))) - (72 nil) ;; directory (internal, not a real git state) - (0 ;; deleted or unknown - (case old-type - (80 " (symlink)") - (112 " (subproject)"))) - (t (format " (unknown type %o)" new-type))))) - (cond (str (propertize str 'face 'git-status-face)) - ((eq new-type 72) "/") - (t "")))) - -(defun git-rename-as-string (info) - "Return a string describing the copy or rename associated with INFO, or an empty string if none." - (let ((state (git-fileinfo->rename-state info))) - (if state - (propertize - (concat " (" - (if (eq state 'copy) "copied from " - (if (eq (git-fileinfo->state info) 'added) "renamed from " - "renamed to ")) - (git-escape-file-name (git-fileinfo->orig-name info)) - ")") 'face 'git-status-face) - ""))) - -(defun git-permissions-as-string (old-perm new-perm) - "Format a permission change as string." - (propertize - (if (or (not old-perm) - (not new-perm) - (eq 0 (logand ?\111 (logxor old-perm new-perm)))) - " " - (if (eq 0 (logand ?\111 old-perm)) "+x" "-x")) - 'face 'git-permission-face)) - -(defun git-fileinfo-prettyprint (info) - "Pretty-printer for the git-fileinfo structure." - (let ((old-perm (git-fileinfo->old-perm info)) - (new-perm (git-fileinfo->new-perm info))) - (insert (concat " " (if (git-fileinfo->marked info) (propertize "*" 'face 'git-mark-face) " ") - " " (git-status-code-as-string (git-fileinfo->state info)) - " " (git-permissions-as-string old-perm new-perm) - " " (git-escape-file-name (git-fileinfo->name info)) - (git-file-type-as-string old-perm new-perm) - (git-rename-as-string info))))) - -(defun git-update-node-fileinfo (node info) - "Update the fileinfo of the specified node. The names are assumed to match already." - (let ((data (ewoc-data node))) - (setf - ;; preserve the marked flag - (git-fileinfo->marked info) (git-fileinfo->marked data) - (git-fileinfo->needs-update data) nil) - (when (not (equal info data)) - (setf (git-fileinfo->needs-refresh info) t - (ewoc-data node) info)))) - -(defun git-insert-info-list (status infolist files) - "Insert a sorted list of file infos in the status buffer, replacing existing ones if any." - (let* ((info (pop infolist)) - (node (ewoc-nth status 0)) - (name (and info (git-fileinfo->name info))) - remaining) - (while info - (let ((nodename (and node (git-fileinfo->name (ewoc-data node))))) - (while (and files (string-lessp (car files) name)) - (push (pop files) remaining)) - (when (and files (string-equal (car files) name)) - (setq files (cdr files))) - (cond ((not nodename) - (setq node (ewoc-enter-last status info)) - (setq info (pop infolist)) - (setq name (and info (git-fileinfo->name info)))) - ((string-lessp nodename name) - (setq node (ewoc-next status node))) - ((string-equal nodename name) - ;; preserve the marked flag - (git-update-node-fileinfo node info) - (setq info (pop infolist)) - (setq name (and info (git-fileinfo->name info)))) - (t - (setq node (ewoc-enter-before status node info)) - (setq info (pop infolist)) - (setq name (and info (git-fileinfo->name info))))))) - (nconc (nreverse remaining) files))) - -(defun git-run-diff-index (status files) - "Run git-diff-index on FILES and parse the results into STATUS. -Return the list of files that haven't been handled." - (let (infolist) - (with-temp-buffer - (apply #'git-call-process t "diff-index" "-z" "-M" "HEAD" "--" files) - (goto-char (point-min)) - (while (re-search-forward - ":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMUT]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0" - nil t 1) - (let ((old-perm (string-to-number (match-string 1) 8)) - (new-perm (string-to-number (match-string 2) 8)) - (state (or (match-string 4) (match-string 6))) - (name (or (match-string 5) (match-string 7))) - (new-name (match-string 8))) - (if new-name ; copy or rename - (if (eq ?C (string-to-char state)) - (push (git-create-fileinfo 'added new-name old-perm new-perm 'copy name) infolist) - (push (git-create-fileinfo 'deleted name 0 0 'rename new-name) infolist) - (push (git-create-fileinfo 'added new-name old-perm new-perm 'rename name) infolist)) - (push (git-create-fileinfo (git-state-code state) name old-perm new-perm) infolist))))) - (setq infolist (sort (nreverse infolist) - (lambda (info1 info2) - (string-lessp (git-fileinfo->name info1) - (git-fileinfo->name info2))))) - (git-insert-info-list status infolist files))) - -(defun git-find-status-file (status file) - "Find a given file in the status ewoc and return its node." - (let ((node (ewoc-nth status 0))) - (while (and node (not (string= file (git-fileinfo->name (ewoc-data node))))) - (setq node (ewoc-next status node))) - node)) - -(defun git-run-ls-files (status files default-state &rest options) - "Run git-ls-files on FILES and parse the results into STATUS. -Return the list of files that haven't been handled." - (let (infolist) - (with-temp-buffer - (apply #'git-call-process t "ls-files" "-z" (append options (list "--") files)) - (goto-char (point-min)) - (while (re-search-forward "\\([^\0]*?\\)\\(/?\\)\0" nil t 1) - (let ((name (match-string 1))) - (push (git-create-fileinfo default-state name 0 - (if (string-equal "/" (match-string 2)) (lsh ?\110 9) 0)) - infolist)))) - (setq infolist (nreverse infolist)) ;; assume it is sorted already - (git-insert-info-list status infolist files))) - -(defun git-run-ls-files-cached (status files default-state) - "Run git-ls-files -c on FILES and parse the results into STATUS. -Return the list of files that haven't been handled." - (let (infolist) - (with-temp-buffer - (apply #'git-call-process t "ls-files" "-z" "-s" "-c" "--" files) - (goto-char (point-min)) - (while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} 0\t\\([^\0]+\\)\0" nil t) - (let* ((new-perm (string-to-number (match-string 1) 8)) - (old-perm (if (eq default-state 'added) 0 new-perm)) - (name (match-string 2))) - (push (git-create-fileinfo default-state name old-perm new-perm) infolist)))) - (setq infolist (nreverse infolist)) ;; assume it is sorted already - (git-insert-info-list status infolist files))) - -(defun git-run-ls-unmerged (status files) - "Run git-ls-files -u on FILES and parse the results into STATUS." - (with-temp-buffer - (apply #'git-call-process t "ls-files" "-z" "-u" "--" files) - (goto-char (point-min)) - (let (unmerged-files) - (while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t) - (push (match-string 1) unmerged-files)) - (setq unmerged-files (nreverse unmerged-files)) ;; assume it is sorted already - (git-set-filenames-state status unmerged-files 'unmerged)))) - -(defun git-get-exclude-files () - "Get the list of exclude files to pass to git-ls-files." - (let (files - (config (git-config "core.excludesfile"))) - (when (file-readable-p ".git/info/exclude") - (push ".git/info/exclude" files)) - (when (and config (file-readable-p config)) - (push config files)) - files)) - -(defun git-run-ls-files-with-excludes (status files default-state &rest options) - "Run git-ls-files on FILES with appropriate --exclude-from options." - (let ((exclude-files (git-get-exclude-files))) - (apply #'git-run-ls-files status files default-state "--directory" "--no-empty-directory" - (concat "--exclude-per-directory=" git-per-dir-ignore-file) - (append options (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files))))) - -(defun git-update-status-files (&optional files mark-files) - "Update the status of FILES from the index. -The FILES list must be sorted." - (unless git-status (error "Not in git-status buffer.")) - ;; set the needs-update flag on existing files - (if files - (git-status-filenames-map - git-status (lambda (info) (setf (git-fileinfo->needs-update info) t)) files) - (ewoc-map (lambda (info) (setf (git-fileinfo->needs-update info) t) nil) git-status) - (git-call-process nil "update-index" "--refresh") - (when git-show-uptodate - (git-run-ls-files-cached git-status nil 'uptodate))) - (let ((remaining-files - (if (git-empty-db-p) ; we need some special handling for an empty db - (git-run-ls-files-cached git-status files 'added) - (git-run-diff-index git-status files)))) - (git-run-ls-unmerged git-status files) - (when (or remaining-files (and git-show-unknown (not files))) - (setq remaining-files (git-run-ls-files-with-excludes git-status remaining-files 'unknown "-o"))) - (when (or remaining-files (and git-show-ignored (not files))) - (setq remaining-files (git-run-ls-files-with-excludes git-status remaining-files 'ignored "-o" "-i"))) - (unless files - (setq remaining-files (git-get-filenames (ewoc-collect git-status #'git-fileinfo->needs-update)))) - (when remaining-files - (setq remaining-files (git-run-ls-files-cached git-status remaining-files 'uptodate))) - (git-set-filenames-state git-status remaining-files nil) - (when mark-files (git-mark-files git-status files)) - (git-refresh-files) - (git-refresh-ewoc-hf git-status))) - -(defun git-mark-files (status files) - "Mark all the specified FILES, and unmark the others." - (let ((file (and files (pop files))) - (node (ewoc-nth status 0))) - (while node - (let ((info (ewoc-data node))) - (if (and file (string-equal (git-fileinfo->name info) file)) - (progn - (unless (git-fileinfo->marked info) - (setf (git-fileinfo->marked info) t) - (setf (git-fileinfo->needs-refresh info) t)) - (setq file (pop files)) - (setq node (ewoc-next status node))) - (when (git-fileinfo->marked info) - (setf (git-fileinfo->marked info) nil) - (setf (git-fileinfo->needs-refresh info) t)) - (if (and file (string-lessp file (git-fileinfo->name info))) - (setq file (pop files)) - (setq node (ewoc-next status node)))))))) - -(defun git-marked-files () - "Return a list of all marked files, or if none a list containing just the file at cursor position." - (unless git-status (error "Not in git-status buffer.")) - (or (ewoc-collect git-status (lambda (info) (git-fileinfo->marked info))) - (list (ewoc-data (ewoc-locate git-status))))) - -(defun git-marked-files-state (&rest states) - "Return a sorted list of marked files that are in the specified states." - (let ((files (git-marked-files)) - result) - (dolist (info files) - (when (memq (git-fileinfo->state info) states) - (push info result))) - (nreverse result))) - -(defun git-refresh-files () - "Refresh all files that need it and clear the needs-refresh flag." - (unless git-status (error "Not in git-status buffer.")) - (ewoc-map - (lambda (info) - (let ((refresh (git-fileinfo->needs-refresh info))) - (setf (git-fileinfo->needs-refresh info) nil) - refresh)) - git-status) - ; move back to goal column - (when goal-column (move-to-column goal-column))) - -(defun git-refresh-ewoc-hf (status) - "Refresh the ewoc header and footer." - (let ((branch (git-symbolic-ref "HEAD")) - (head (if (git-empty-db-p) "Nothing committed yet" - (git-get-commit-description "HEAD"))) - (merge-heads (git-get-merge-heads))) - (ewoc-set-hf status - (format "Directory: %s\nBranch: %s\nHead: %s%s\n" - default-directory - (if branch - (if (string-match "^refs/heads/" branch) - (substring branch (match-end 0)) - branch) - "none (detached HEAD)") - head - (if merge-heads - (concat "\nMerging: " - (mapconcat (lambda (str) (git-get-commit-description str)) merge-heads "\n ")) - "")) - (if (ewoc-nth status 0) "" " No changes.")))) - -(defun git-get-filenames (files) - (mapcar (lambda (info) (git-fileinfo->name info)) files)) - -(defun git-update-index (index-file files) - "Run git-update-index on a list of files." - (let ((process-environment (append (and index-file (list (concat "GIT_INDEX_FILE=" index-file))) - process-environment)) - added deleted modified) - (dolist (info files) - (case (git-fileinfo->state info) - ('added (push info added)) - ('deleted (push info deleted)) - ('modified (push info modified)))) - (and - (or (not added) (apply #'git-call-process-display-error "update-index" "--add" "--" (git-get-filenames added))) - (or (not deleted) (apply #'git-call-process-display-error "update-index" "--remove" "--" (git-get-filenames deleted))) - (or (not modified) (apply #'git-call-process-display-error "update-index" "--" (git-get-filenames modified)))))) - -(defun git-run-pre-commit-hook () - "Run the pre-commit hook if any." - (unless git-status (error "Not in git-status buffer.")) - (let ((files (git-marked-files-state 'added 'deleted 'modified))) - (or (not files) - (not (file-executable-p ".git/hooks/pre-commit")) - (let ((index-file (make-temp-file "gitidx"))) - (unwind-protect - (let ((head-tree (unless (git-empty-db-p) (git-rev-parse "HEAD^{tree}")))) - (git-read-tree head-tree index-file) - (git-update-index index-file files) - (git-run-hook "pre-commit" `(("GIT_INDEX_FILE" . ,index-file)))) - (delete-file index-file)))))) - -(defun git-do-commit () - "Perform the actual commit using the current buffer as log message." - (interactive) - (let ((buffer (current-buffer)) - (index-file (make-temp-file "gitidx"))) - (with-current-buffer log-edit-parent-buffer - (if (git-marked-files-state 'unmerged) - (message "You cannot commit unmerged files, resolve them first.") - (unwind-protect - (let ((files (git-marked-files-state 'added 'deleted 'modified)) - head tree head-tree) - (unless (git-empty-db-p) - (setq head (git-rev-parse "HEAD") - head-tree (git-rev-parse "HEAD^{tree}"))) - (message "Running git commit...") - (when - (and - (git-read-tree head-tree index-file) - (git-update-index nil files) ;update both the default index - (git-update-index index-file files) ;and the temporary one - (setq tree (git-write-tree index-file))) - (if (or (not (string-equal tree head-tree)) - (yes-or-no-p "The tree was not modified, do you really want to perform an empty commit? ")) - (let ((commit (git-commit-tree buffer tree head))) - (when commit - (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil)) - (condition-case nil (delete-file ".git/MERGE_MSG") (error nil)) - (with-current-buffer buffer (erase-buffer)) - (git-update-status-files (git-get-filenames files)) - (git-call-process nil "rerere") - (git-call-process nil "gc" "--auto") - (message "Committed %s." commit) - (git-run-hook "post-commit" nil))) - (message "Commit aborted.")))) - (delete-file index-file)))))) - - -;;;; Interactive functions -;;;; ------------------------------------------------------------ - -(defun git-mark-file () - "Mark the file that the cursor is on and move to the next one." - (interactive) - (unless git-status (error "Not in git-status buffer.")) - (let* ((pos (ewoc-locate git-status)) - (info (ewoc-data pos))) - (setf (git-fileinfo->marked info) t) - (ewoc-invalidate git-status pos) - (ewoc-goto-next git-status 1))) - -(defun git-unmark-file () - "Unmark the file that the cursor is on and move to the next one." - (interactive) - (unless git-status (error "Not in git-status buffer.")) - (let* ((pos (ewoc-locate git-status)) - (info (ewoc-data pos))) - (setf (git-fileinfo->marked info) nil) - (ewoc-invalidate git-status pos) - (ewoc-goto-next git-status 1))) - -(defun git-unmark-file-up () - "Unmark the file that the cursor is on and move to the previous one." - (interactive) - (unless git-status (error "Not in git-status buffer.")) - (let* ((pos (ewoc-locate git-status)) - (info (ewoc-data pos))) - (setf (git-fileinfo->marked info) nil) - (ewoc-invalidate git-status pos) - (ewoc-goto-prev git-status 1))) - -(defun git-mark-all () - "Mark all files." - (interactive) - (unless git-status (error "Not in git-status buffer.")) - (ewoc-map (lambda (info) (unless (git-fileinfo->marked info) - (setf (git-fileinfo->marked info) t))) git-status) - ; move back to goal column after invalidate - (when goal-column (move-to-column goal-column))) - -(defun git-unmark-all () - "Unmark all files." - (interactive) - (unless git-status (error "Not in git-status buffer.")) - (ewoc-map (lambda (info) (when (git-fileinfo->marked info) - (setf (git-fileinfo->marked info) nil) - t)) git-status) - ; move back to goal column after invalidate - (when goal-column (move-to-column goal-column))) - -(defun git-toggle-all-marks () - "Toggle all file marks." - (interactive) - (unless git-status (error "Not in git-status buffer.")) - (ewoc-map (lambda (info) (setf (git-fileinfo->marked info) (not (git-fileinfo->marked info))) t) git-status) - ; move back to goal column after invalidate - (when goal-column (move-to-column goal-column))) - -(defun git-next-file (&optional n) - "Move the selection down N files." - (interactive "p") - (unless git-status (error "Not in git-status buffer.")) - (ewoc-goto-next git-status n)) - -(defun git-prev-file (&optional n) - "Move the selection up N files." - (interactive "p") - (unless git-status (error "Not in git-status buffer.")) - (ewoc-goto-prev git-status n)) - -(defun git-next-unmerged-file (&optional n) - "Move the selection down N unmerged files." - (interactive "p") - (unless git-status (error "Not in git-status buffer.")) - (let* ((last (ewoc-locate git-status)) - (node (ewoc-next git-status last))) - (while (and node (> n 0)) - (when (eq 'unmerged (git-fileinfo->state (ewoc-data node))) - (setq n (1- n)) - (setq last node)) - (setq node (ewoc-next git-status node))) - (ewoc-goto-node git-status last))) - -(defun git-prev-unmerged-file (&optional n) - "Move the selection up N unmerged files." - (interactive "p") - (unless git-status (error "Not in git-status buffer.")) - (let* ((last (ewoc-locate git-status)) - (node (ewoc-prev git-status last))) - (while (and node (> n 0)) - (when (eq 'unmerged (git-fileinfo->state (ewoc-data node))) - (setq n (1- n)) - (setq last node)) - (setq node (ewoc-prev git-status node))) - (ewoc-goto-node git-status last))) - -(defun git-insert-file (file) - "Insert file(s) into the git-status buffer." - (interactive "fInsert file: ") - (git-update-status-files (list (file-relative-name file)))) - -(defun git-add-file () - "Add marked file(s) to the index cache." - (interactive) - (let ((files (git-get-filenames (git-marked-files-state 'unknown 'ignored 'unmerged)))) - ;; FIXME: add support for directories - (unless files - (push (file-relative-name (read-file-name "File to add: " nil nil t)) files)) - (when (apply 'git-call-process-display-error "update-index" "--add" "--" files) - (git-update-status-files files) - (git-success-message "Added" files)))) - -(defun git-ignore-file () - "Add marked file(s) to the ignore list." - (interactive) - (let ((files (git-get-filenames (git-marked-files-state 'unknown)))) - (unless files - (push (file-relative-name (read-file-name "File to ignore: " nil nil t)) files)) - (dolist (f files) (git-append-to-ignore f)) - (git-update-status-files files) - (git-success-message "Ignored" files))) - -(defun git-remove-file () - "Remove the marked file(s)." - (interactive) - (let ((files (git-get-filenames (git-marked-files-state 'added 'modified 'unknown 'uptodate 'ignored)))) - (unless files - (push (file-relative-name (read-file-name "File to remove: " nil nil t)) files)) - (if (yes-or-no-p - (if (cdr files) - (format "Remove %d files? " (length files)) - (format "Remove %s? " (car files)))) - (progn - (dolist (name files) - (ignore-errors - (if (file-directory-p name) - (delete-directory name) - (delete-file name)))) - (when (apply 'git-call-process-display-error "update-index" "--remove" "--" files) - (git-update-status-files files) - (git-success-message "Removed" files))) - (message "Aborting")))) - -(defun git-revert-file () - "Revert changes to the marked file(s)." - (interactive) - (let ((files (git-marked-files-state 'added 'deleted 'modified 'unmerged)) - added modified) - (when (and files - (yes-or-no-p - (if (cdr files) - (format "Revert %d files? " (length files)) - (format "Revert %s? " (git-fileinfo->name (car files)))))) - (dolist (info files) - (case (git-fileinfo->state info) - ('added (push (git-fileinfo->name info) added)) - ('deleted (push (git-fileinfo->name info) modified)) - ('unmerged (push (git-fileinfo->name info) modified)) - ('modified (push (git-fileinfo->name info) modified)))) - ;; check if a buffer contains one of the files and isn't saved - (dolist (file modified) - (let ((buffer (get-file-buffer file))) - (when (and buffer (buffer-modified-p buffer)) - (error "Buffer %s is modified. Please kill or save modified buffers before reverting." (buffer-name buffer))))) - (let ((ok (and - (or (not added) - (apply 'git-call-process-display-error "update-index" "--force-remove" "--" added)) - (or (not modified) - (apply 'git-call-process-display-error "checkout" "HEAD" modified)))) - (names (git-get-filenames files))) - (git-update-status-files names) - (when ok - (dolist (file modified) - (let ((buffer (get-file-buffer file))) - (when buffer (with-current-buffer buffer (revert-buffer t t t))))) - (git-success-message "Reverted" names)))))) - -(defun git-remove-handled () - "Remove handled files from the status list." - (interactive) - (ewoc-filter git-status - (lambda (info) - (case (git-fileinfo->state info) - ('ignored git-show-ignored) - ('uptodate git-show-uptodate) - ('unknown git-show-unknown) - (t t)))) - (unless (ewoc-nth git-status 0) ; refresh header if list is empty - (git-refresh-ewoc-hf git-status))) - -(defun git-toggle-show-uptodate () - "Toogle the option for showing up-to-date files." - (interactive) - (if (setq git-show-uptodate (not git-show-uptodate)) - (git-refresh-status) - (git-remove-handled))) - -(defun git-toggle-show-ignored () - "Toogle the option for showing ignored files." - (interactive) - (if (setq git-show-ignored (not git-show-ignored)) - (progn - (message "Inserting ignored files...") - (git-run-ls-files-with-excludes git-status nil 'ignored "-o" "-i") - (git-refresh-files) - (git-refresh-ewoc-hf git-status) - (message "Inserting ignored files...done")) - (git-remove-handled))) - -(defun git-toggle-show-unknown () - "Toogle the option for showing unknown files." - (interactive) - (if (setq git-show-unknown (not git-show-unknown)) - (progn - (message "Inserting unknown files...") - (git-run-ls-files-with-excludes git-status nil 'unknown "-o") - (git-refresh-files) - (git-refresh-ewoc-hf git-status) - (message "Inserting unknown files...done")) - (git-remove-handled))) - -(defun git-expand-directory (info) - "Expand the directory represented by INFO to list its files." - (when (eq (lsh (git-fileinfo->new-perm info) -9) ?\110) - (let ((dir (git-fileinfo->name info))) - (git-set-filenames-state git-status (list dir) nil) - (git-run-ls-files-with-excludes git-status (list (concat dir "/")) 'unknown "-o") - (git-refresh-files) - (git-refresh-ewoc-hf git-status) - t))) - -(defun git-setup-diff-buffer (buffer) - "Setup a buffer for displaying a diff." - (let ((dir default-directory)) - (with-current-buffer buffer - (diff-mode) - (goto-char (point-min)) - (setq default-directory dir) - (setq buffer-read-only t))) - (display-buffer buffer) - ; shrink window only if it displays the status buffer - (when (eq (window-buffer) (current-buffer)) - (shrink-window-if-larger-than-buffer))) - -(defun git-diff-file () - "Diff the marked file(s) against HEAD." - (interactive) - (let ((files (git-marked-files))) - (git-setup-diff-buffer - (apply #'git-run-command-buffer "*git-diff*" "diff-index" "-p" "-M" "HEAD" "--" (git-get-filenames files))))) - -(defun git-diff-file-merge-head (arg) - "Diff the marked file(s) against the first merge head (or the nth one with a numeric prefix)." - (interactive "p") - (let ((files (git-marked-files)) - (merge-heads (git-get-merge-heads))) - (unless merge-heads (error "No merge in progress")) - (git-setup-diff-buffer - (apply #'git-run-command-buffer "*git-diff*" "diff-index" "-p" "-M" - (or (nth (1- arg) merge-heads) "HEAD") "--" (git-get-filenames files))))) - -(defun git-diff-unmerged-file (stage) - "Diff the marked unmerged file(s) against the specified stage." - (let ((files (git-marked-files))) - (git-setup-diff-buffer - (apply #'git-run-command-buffer "*git-diff*" "diff-files" "-p" stage "--" (git-get-filenames files))))) - -(defun git-diff-file-base () - "Diff the marked unmerged file(s) against the common base file." - (interactive) - (git-diff-unmerged-file "-1")) - -(defun git-diff-file-mine () - "Diff the marked unmerged file(s) against my pre-merge version." - (interactive) - (git-diff-unmerged-file "-2")) - -(defun git-diff-file-other () - "Diff the marked unmerged file(s) against the other's pre-merge version." - (interactive) - (git-diff-unmerged-file "-3")) - -(defun git-diff-file-combined () - "Do a combined diff of the marked unmerged file(s)." - (interactive) - (git-diff-unmerged-file "-c")) - -(defun git-diff-file-idiff () - "Perform an interactive diff on the current file." - (interactive) - (let ((files (git-marked-files-state 'added 'deleted 'modified))) - (unless (eq 1 (length files)) - (error "Cannot perform an interactive diff on multiple files.")) - (let* ((filename (car (git-get-filenames files))) - (buff1 (find-file-noselect filename)) - (buff2 (git-run-command-buffer (concat filename ".~HEAD~") "cat-file" "blob" (concat "HEAD:" filename)))) - (ediff-buffers buff1 buff2)))) - -(defun git-log-file () - "Display a log of changes to the marked file(s)." - (interactive) - (let* ((files (git-marked-files)) - (coding-system-for-read git-commits-coding-system) - (buffer (apply #'git-run-command-buffer "*git-log*" "rev-list" "--pretty" "HEAD" "--" (git-get-filenames files)))) - (with-current-buffer buffer - ; (git-log-mode) FIXME: implement log mode - (goto-char (point-min)) - (setq buffer-read-only t)) - (display-buffer buffer))) - -(defun git-log-edit-files () - "Return a list of marked files for use in the log-edit buffer." - (with-current-buffer log-edit-parent-buffer - (git-get-filenames (git-marked-files-state 'added 'deleted 'modified)))) - -(defun git-log-edit-diff () - "Run a diff of the current files being committed from a log-edit buffer." - (with-current-buffer log-edit-parent-buffer - (git-diff-file))) - -(defun git-append-sign-off (name email) - "Append a Signed-off-by entry to the current buffer, avoiding duplicates." - (let ((sign-off (format "Signed-off-by: %s <%s>" name email)) - (case-fold-search t)) - (goto-char (point-min)) - (unless (re-search-forward (concat "^" (regexp-quote sign-off)) nil t) - (goto-char (point-min)) - (unless (re-search-forward "^Signed-off-by: " nil t) - (setq sign-off (concat "\n" sign-off))) - (goto-char (point-max)) - (insert sign-off "\n")))) - -(defun git-setup-log-buffer (buffer &optional merge-heads author-name author-email subject date msg) - "Setup the log buffer for a commit." - (unless git-status (error "Not in git-status buffer.")) - (let ((dir default-directory) - (committer-name (git-get-committer-name)) - (committer-email (git-get-committer-email)) - (sign-off git-append-signed-off-by)) - (with-current-buffer buffer - (cd dir) - (erase-buffer) - (insert - (propertize - (format "Author: %s <%s>\n%s%s" - (or author-name committer-name) - (or author-email committer-email) - (if date (format "Date: %s\n" date) "") - (if merge-heads - (format "Merge: %s\n" - (mapconcat 'identity merge-heads " ")) - "")) - 'face 'git-header-face) - (propertize git-log-msg-separator 'face 'git-separator-face) - "\n") - (when subject (insert subject "\n\n")) - (cond (msg (insert msg "\n")) - ((file-readable-p ".git/rebase-apply/msg") - (insert-file-contents ".git/rebase-apply/msg")) - ((file-readable-p ".git/MERGE_MSG") - (insert-file-contents ".git/MERGE_MSG"))) - ; delete empty lines at end - (goto-char (point-min)) - (when (re-search-forward "\n+\\'" nil t) - (replace-match "\n" t t)) - (when sign-off (git-append-sign-off committer-name committer-email))) - buffer)) - -(define-derived-mode git-log-edit-mode log-edit-mode "Git-Log-Edit" - "Major mode for editing git log messages. - -Set up git-specific `font-lock-keywords' for `log-edit-mode'." - (set (make-local-variable 'font-lock-defaults) - '(git-log-edit-font-lock-keywords t t))) - -(defun git-commit-file () - "Commit the marked file(s), asking for a commit message." - (interactive) - (unless git-status (error "Not in git-status buffer.")) - (when (git-run-pre-commit-hook) - (let ((buffer (get-buffer-create "*git-commit*")) - (coding-system (git-get-commits-coding-system)) - author-name author-email subject date) - (when (eq 0 (buffer-size buffer)) - (when (file-readable-p ".git/rebase-apply/info") - (with-temp-buffer - (insert-file-contents ".git/rebase-apply/info") - (goto-char (point-min)) - (when (re-search-forward "^Author: \\(.*\\)\nEmail: \\(.*\\)$" nil t) - (setq author-name (match-string 1)) - (setq author-email (match-string 2))) - (goto-char (point-min)) - (when (re-search-forward "^Subject: \\(.*\\)$" nil t) - (setq subject (match-string 1))) - (goto-char (point-min)) - (when (re-search-forward "^Date: \\(.*\\)$" nil t) - (setq date (match-string 1))))) - (git-setup-log-buffer buffer (git-get-merge-heads) author-name author-email subject date)) - (if (boundp 'log-edit-diff-function) - (log-edit 'git-do-commit nil '((log-edit-listfun . git-log-edit-files) - (log-edit-diff-function . git-log-edit-diff)) buffer 'git-log-edit-mode) - (log-edit 'git-do-commit nil 'git-log-edit-files buffer - 'git-log-edit-mode)) - (setq paragraph-separate (concat (regexp-quote git-log-msg-separator) "$\\|Author: \\|Date: \\|Merge: \\|Signed-off-by: \\|\f\\|[ ]*$")) - (setq buffer-file-coding-system coding-system) - (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t)))) - -(defun git-setup-commit-buffer (commit) - "Setup the commit buffer with the contents of COMMIT." - (let (parents author-name author-email subject date msg) - (with-temp-buffer - (let ((coding-system (git-get-logoutput-coding-system))) - (git-call-process t "log" "-1" "--pretty=medium" "--abbrev=40" commit) - (goto-char (point-min)) - (when (re-search-forward "^Merge: *\\(.*\\)$" nil t) - (setq parents (cdr (split-string (match-string 1) " +")))) - (when (re-search-forward "^Author: *\\(.*\\) <\\(.*\\)>$" nil t) - (setq author-name (match-string 1)) - (setq author-email (match-string 2))) - (when (re-search-forward "^Date: *\\(.*\\)$" nil t) - (setq date (match-string 1))) - (while (re-search-forward "^ \\(.*\\)$" nil t) - (push (match-string 1) msg)) - (setq msg (nreverse msg)) - (setq subject (pop msg)) - (while (and msg (zerop (length (car msg))) (pop msg))))) - (git-setup-log-buffer (get-buffer-create "*git-commit*") - parents author-name author-email subject date - (mapconcat #'identity msg "\n")))) - -(defun git-get-commit-files (commit) - "Retrieve a sorted list of files modified by COMMIT." - (let (files) - (with-temp-buffer - (git-call-process t "diff-tree" "-m" "-r" "-z" "--name-only" "--no-commit-id" "--root" commit) - (goto-char (point-min)) - (while (re-search-forward "\\([^\0]*\\)\0" nil t 1) - (push (match-string 1) files))) - (sort files #'string-lessp))) - -(defun git-read-commit-name (prompt &optional default) - "Ask for a commit name, with completion for local branch, remote branch and tag." - (completing-read prompt - (list* "HEAD" "ORIG_HEAD" "FETCH_HEAD" (mapcar #'car (git-for-each-ref))) - nil nil nil nil default)) - -(defun git-checkout (branch &optional merge) - "Checkout a branch, tag, or any commit. -Use a prefix arg if git should merge while checking out." - (interactive - (list (git-read-commit-name "Checkout: ") - current-prefix-arg)) - (unless git-status (error "Not in git-status buffer.")) - (let ((args (list branch "--"))) - (when merge (push "-m" args)) - (when (apply #'git-call-process-display-error "checkout" args) - (git-update-status-files)))) - -(defun git-branch (branch) - "Create a branch from the current HEAD and switch to it." - (interactive (list (git-read-commit-name "Branch: "))) - (unless git-status (error "Not in git-status buffer.")) - (if (git-rev-parse (concat "refs/heads/" branch)) - (if (yes-or-no-p (format "Branch %s already exists, replace it? " branch)) - (and (git-call-process-display-error "branch" "-f" branch) - (git-call-process-display-error "checkout" branch)) - (message "Canceled.")) - (git-call-process-display-error "checkout" "-b" branch)) - (git-refresh-ewoc-hf git-status)) - -(defun git-amend-commit () - "Undo the last commit on HEAD, and set things up to commit an -amended version of it." - (interactive) - (unless git-status (error "Not in git-status buffer.")) - (when (git-empty-db-p) (error "No commit to amend.")) - (let* ((commit (git-rev-parse "HEAD")) - (files (git-get-commit-files commit))) - (when (if (git-rev-parse "HEAD^") - (git-call-process-display-error "reset" "--soft" "HEAD^") - (and (git-update-ref "ORIG_HEAD" commit) - (git-update-ref "HEAD" nil commit))) - (git-update-status-files files t) - (git-setup-commit-buffer commit) - (git-commit-file)))) - -(defun git-cherry-pick-commit (arg) - "Cherry-pick a commit." - (interactive (list (git-read-commit-name "Cherry-pick commit: "))) - (unless git-status (error "Not in git-status buffer.")) - (let ((commit (git-rev-parse (concat arg "^0")))) - (unless commit (error "Not a valid commit '%s'." arg)) - (when (git-rev-parse (concat commit "^2")) - (error "Cannot cherry-pick a merge commit.")) - (let ((files (git-get-commit-files commit)) - (ok (git-call-process-display-error "cherry-pick" "-n" commit))) - (git-update-status-files files ok) - (with-current-buffer (git-setup-commit-buffer commit) - (goto-char (point-min)) - (if (re-search-forward "^\n*Signed-off-by:" nil t 1) - (goto-char (match-beginning 0)) - (goto-char (point-max))) - (insert "(cherry picked from commit " commit ")\n")) - (when ok (git-commit-file))))) - -(defun git-revert-commit (arg) - "Revert a commit." - (interactive (list (git-read-commit-name "Revert commit: "))) - (unless git-status (error "Not in git-status buffer.")) - (let ((commit (git-rev-parse (concat arg "^0")))) - (unless commit (error "Not a valid commit '%s'." arg)) - (when (git-rev-parse (concat commit "^2")) - (error "Cannot revert a merge commit.")) - (let ((files (git-get-commit-files commit)) - (subject (git-get-commit-description commit)) - (ok (git-call-process-display-error "revert" "-n" commit))) - (git-update-status-files files ok) - (when (string-match "^[0-9a-f]+ - \\(.*\\)$" subject) - (setq subject (match-string 1 subject))) - (git-setup-log-buffer (get-buffer-create "*git-commit*") - (git-get-merge-heads) nil nil (format "Revert \"%s\"" subject) nil - (format "This reverts commit %s.\n" commit)) - (when ok (git-commit-file))))) - -(defun git-find-file () - "Visit the current file in its own buffer." - (interactive) - (unless git-status (error "Not in git-status buffer.")) - (let ((info (ewoc-data (ewoc-locate git-status)))) - (unless (git-expand-directory info) - (find-file (git-fileinfo->name info)) - (when (eq 'unmerged (git-fileinfo->state info)) - (smerge-mode 1))))) - -(defun git-find-file-other-window () - "Visit the current file in its own buffer in another window." - (interactive) - (unless git-status (error "Not in git-status buffer.")) - (let ((info (ewoc-data (ewoc-locate git-status)))) - (find-file-other-window (git-fileinfo->name info)) - (when (eq 'unmerged (git-fileinfo->state info)) - (smerge-mode)))) - -(defun git-find-file-imerge () - "Visit the current file in interactive merge mode." - (interactive) - (unless git-status (error "Not in git-status buffer.")) - (let ((info (ewoc-data (ewoc-locate git-status)))) - (find-file (git-fileinfo->name info)) - (smerge-ediff))) - -(defun git-view-file () - "View the current file in its own buffer." - (interactive) - (unless git-status (error "Not in git-status buffer.")) - (let ((info (ewoc-data (ewoc-locate git-status)))) - (view-file (git-fileinfo->name info)))) - -(defun git-refresh-status () - "Refresh the git status buffer." - (interactive) - (unless git-status (error "Not in git-status buffer.")) - (message "Refreshing git status...") - (git-update-status-files) - (message "Refreshing git status...done")) - -(defun git-status-quit () - "Quit git-status mode." - (interactive) - (bury-buffer)) - -;;;; Major Mode -;;;; ------------------------------------------------------------ - -(defvar git-status-mode-hook nil - "Run after `git-status-mode' is setup.") - -(defvar git-status-mode-map nil - "Keymap for git major mode.") - -(defvar git-status nil - "List of all files managed by the git-status mode.") - -(unless git-status-mode-map - (let ((map (make-keymap)) - (commit-map (make-sparse-keymap)) - (diff-map (make-sparse-keymap)) - (toggle-map (make-sparse-keymap))) - (suppress-keymap map) - (define-key map "?" 'git-help) - (define-key map "h" 'git-help) - (define-key map " " 'git-next-file) - (define-key map "a" 'git-add-file) - (define-key map "c" 'git-commit-file) - (define-key map "\C-c" commit-map) - (define-key map "d" diff-map) - (define-key map "=" 'git-diff-file) - (define-key map "f" 'git-find-file) - (define-key map "\r" 'git-find-file) - (define-key map "g" 'git-refresh-status) - (define-key map "i" 'git-ignore-file) - (define-key map "I" 'git-insert-file) - (define-key map "l" 'git-log-file) - (define-key map "m" 'git-mark-file) - (define-key map "M" 'git-mark-all) - (define-key map "n" 'git-next-file) - (define-key map "N" 'git-next-unmerged-file) - (define-key map "o" 'git-find-file-other-window) - (define-key map "p" 'git-prev-file) - (define-key map "P" 'git-prev-unmerged-file) - (define-key map "q" 'git-status-quit) - (define-key map "r" 'git-remove-file) - (define-key map "t" toggle-map) - (define-key map "T" 'git-toggle-all-marks) - (define-key map "u" 'git-unmark-file) - (define-key map "U" 'git-revert-file) - (define-key map "v" 'git-view-file) - (define-key map "x" 'git-remove-handled) - (define-key map "\C-?" 'git-unmark-file-up) - (define-key map "\M-\C-?" 'git-unmark-all) - ; the commit submap - (define-key commit-map "\C-a" 'git-amend-commit) - (define-key commit-map "\C-b" 'git-branch) - (define-key commit-map "\C-o" 'git-checkout) - (define-key commit-map "\C-p" 'git-cherry-pick-commit) - (define-key commit-map "\C-v" 'git-revert-commit) - ; the diff submap - (define-key diff-map "b" 'git-diff-file-base) - (define-key diff-map "c" 'git-diff-file-combined) - (define-key diff-map "=" 'git-diff-file) - (define-key diff-map "e" 'git-diff-file-idiff) - (define-key diff-map "E" 'git-find-file-imerge) - (define-key diff-map "h" 'git-diff-file-merge-head) - (define-key diff-map "m" 'git-diff-file-mine) - (define-key diff-map "o" 'git-diff-file-other) - ; the toggle submap - (define-key toggle-map "u" 'git-toggle-show-uptodate) - (define-key toggle-map "i" 'git-toggle-show-ignored) - (define-key toggle-map "k" 'git-toggle-show-unknown) - (define-key toggle-map "m" 'git-toggle-all-marks) - (setq git-status-mode-map map)) - (easy-menu-define git-menu git-status-mode-map - "Git Menu" - `("Git" - ["Refresh" git-refresh-status t] - ["Commit" git-commit-file t] - ["Checkout..." git-checkout t] - ["New Branch..." git-branch t] - ["Cherry-pick Commit..." git-cherry-pick-commit t] - ["Revert Commit..." git-revert-commit t] - ("Merge" - ["Next Unmerged File" git-next-unmerged-file t] - ["Prev Unmerged File" git-prev-unmerged-file t] - ["Interactive Merge File" git-find-file-imerge t] - ["Diff Against Common Base File" git-diff-file-base t] - ["Diff Combined" git-diff-file-combined t] - ["Diff Against Merge Head" git-diff-file-merge-head t] - ["Diff Against Mine" git-diff-file-mine t] - ["Diff Against Other" git-diff-file-other t]) - "--------" - ["Add File" git-add-file t] - ["Revert File" git-revert-file t] - ["Ignore File" git-ignore-file t] - ["Remove File" git-remove-file t] - ["Insert File" git-insert-file t] - "--------" - ["Find File" git-find-file t] - ["View File" git-view-file t] - ["Diff File" git-diff-file t] - ["Interactive Diff File" git-diff-file-idiff t] - ["Log" git-log-file t] - "--------" - ["Mark" git-mark-file t] - ["Mark All" git-mark-all t] - ["Unmark" git-unmark-file t] - ["Unmark All" git-unmark-all t] - ["Toggle All Marks" git-toggle-all-marks t] - ["Hide Handled Files" git-remove-handled t] - "--------" - ["Show Uptodate Files" git-toggle-show-uptodate :style toggle :selected git-show-uptodate] - ["Show Ignored Files" git-toggle-show-ignored :style toggle :selected git-show-ignored] - ["Show Unknown Files" git-toggle-show-unknown :style toggle :selected git-show-unknown] - "--------" - ["Quit" git-status-quit t]))) - - -;; git mode should only run in the *git status* buffer -(put 'git-status-mode 'mode-class 'special) - -(defun git-status-mode () - "Major mode for interacting with Git. -Commands: -\\{git-status-mode-map}" - (kill-all-local-variables) - (buffer-disable-undo) - (setq mode-name "git status" - major-mode 'git-status-mode - goal-column 17 - buffer-read-only t) - (use-local-map git-status-mode-map) - (let ((buffer-read-only nil)) - (erase-buffer) - (let ((status (ewoc-create 'git-fileinfo-prettyprint "" ""))) - (set (make-local-variable 'git-status) status)) - (set (make-local-variable 'list-buffers-directory) default-directory) - (make-local-variable 'git-show-uptodate) - (make-local-variable 'git-show-ignored) - (make-local-variable 'git-show-unknown) - (run-hooks 'git-status-mode-hook))) - -(defun git-find-status-buffer (dir) - "Find the git status buffer handling a specified directory." - (let ((list (buffer-list)) - (fulldir (expand-file-name dir)) - found) - (while (and list (not found)) - (let ((buffer (car list))) - (with-current-buffer buffer - (when (and list-buffers-directory - (string-equal fulldir (expand-file-name list-buffers-directory)) - (eq major-mode 'git-status-mode)) - (setq found buffer)))) - (setq list (cdr list))) - found)) - -(defun git-status (dir) - "Entry point into git-status mode." - (interactive "DSelect directory: ") - (setq dir (git-get-top-dir dir)) - (if (file-exists-p (concat (file-name-as-directory dir) ".git")) - (let ((buffer (or (and git-reuse-status-buffer (git-find-status-buffer dir)) - (create-file-buffer (expand-file-name "*git-status*" dir))))) - (switch-to-buffer buffer) - (cd dir) - (git-status-mode) - (git-refresh-status) - (goto-char (point-min)) - (add-hook 'after-save-hook 'git-update-saved-file)) - (message "%s is not a git working tree." dir))) - -(defun git-update-saved-file () - "Update the corresponding git-status buffer when a file is saved. -Meant to be used in `after-save-hook'." - (let* ((file (expand-file-name buffer-file-name)) - (dir (condition-case nil (git-get-top-dir (file-name-directory file)) (error nil))) - (buffer (and dir (git-find-status-buffer dir)))) - (when buffer - (with-current-buffer buffer - (let ((filename (file-relative-name file dir))) - ; skip files located inside the .git directory - (unless (string-match "^\\.git/" filename) - (git-call-process nil "add" "--refresh" "--" filename) - (git-update-status-files (list filename)))))))) - -(defun git-help () - "Display help for Git mode." - (interactive) - (describe-function 'git-status-mode)) - -(provide 'git) -;;; git.el ends here +(error "git.el no longer ships with git. It's recommended to +replace its use with Magit, or simply delete references to git.el +in your initialization file(s). See contrib/emacs/README in git's +sources (https://github.com/git/git/blob/master/contrib/emacs/README) +for suggested alternatives and for why this happened. Emacs's own +VC mode and Magit are viable alternatives.") diff --git a/contrib/examples/README b/contrib/examples/README index 6946f3dd2a..18bc60b021 100644 --- a/contrib/examples/README +++ b/contrib/examples/README @@ -1,3 +1,20 @@ -These are original scripted implementations, kept primarily for their -reference value to any aspiring plumbing users who want to learn how -pieces can be fit together. +This directory used to contain scripted implementations of builtins +that have since been rewritten in C. + +They have now been removed, but can be retrieved from an older commit +that removed them from this directory. + +They're interesting for their reference value to any aspiring plumbing +users who want to learn how pieces can be fit together, but in many +cases have drifted enough from the actual implementations Git uses to +be instructive. + +Other things that can be useful: + + * Some commands such as git-gc wrap other commands, and what they're + doing behind the scenes can be seen by running them under + GIT_TRACE=1 + + * Doing `git log` on paths matching '*--helper.c' will show + incremental effort in the direction of moving existing shell + scripts to C. diff --git a/contrib/examples/builtin-fetch--tool.c b/contrib/examples/builtin-fetch--tool.c deleted file mode 100644 index a3eb19de04..0000000000 --- a/contrib/examples/builtin-fetch--tool.c +++ /dev/null @@ -1,575 +0,0 @@ -#include "builtin.h" -#include "cache.h" -#include "refs.h" -#include "commit.h" -#include "sigchain.h" - -static char *get_stdin(void) -{ - struct strbuf buf = STRBUF_INIT; - if (strbuf_read(&buf, 0, 1024) < 0) { - die_errno("error reading standard input"); - } - return strbuf_detach(&buf, NULL); -} - -static void show_new(enum object_type type, unsigned char *sha1_new) -{ - fprintf(stderr, " %s: %s\n", typename(type), - find_unique_abbrev(sha1_new, DEFAULT_ABBREV)); -} - -static int update_ref_env(const char *action, - const char *refname, - unsigned char *sha1, - unsigned char *oldval) -{ - char msg[1024]; - const char *rla = getenv("GIT_REFLOG_ACTION"); - - if (!rla) - rla = "(reflog update)"; - if (snprintf(msg, sizeof(msg), "%s: %s", rla, action) >= sizeof(msg)) - warning("reflog message too long: %.*s...", 50, msg); - return update_ref(msg, refname, sha1, oldval, 0, - UPDATE_REFS_QUIET_ON_ERR); -} - -static int update_local_ref(const char *name, - const char *new_head, - const char *note, - int verbose, int force) -{ - unsigned char sha1_old[20], sha1_new[20]; - char oldh[41], newh[41]; - struct commit *current, *updated; - enum object_type type; - - if (get_sha1_hex(new_head, sha1_new)) - die("malformed object name %s", new_head); - - type = sha1_object_info(sha1_new, NULL); - if (type < 0) - die("object %s not found", new_head); - - if (!*name) { - /* Not storing */ - if (verbose) { - fprintf(stderr, "* fetched %s\n", note); - show_new(type, sha1_new); - } - return 0; - } - - if (get_sha1(name, sha1_old)) { - const char *msg; - just_store: - /* new ref */ - if (!strncmp(name, "refs/tags/", 10)) - msg = "storing tag"; - else - msg = "storing head"; - fprintf(stderr, "* %s: storing %s\n", - name, note); - show_new(type, sha1_new); - return update_ref_env(msg, name, sha1_new, NULL); - } - - if (!hashcmp(sha1_old, sha1_new)) { - if (verbose) { - fprintf(stderr, "* %s: same as %s\n", name, note); - show_new(type, sha1_new); - } - return 0; - } - - if (!strncmp(name, "refs/tags/", 10)) { - fprintf(stderr, "* %s: updating with %s\n", name, note); - show_new(type, sha1_new); - return update_ref_env("updating tag", name, sha1_new, NULL); - } - - current = lookup_commit_reference(sha1_old); - updated = lookup_commit_reference(sha1_new); - if (!current || !updated) - goto just_store; - - strcpy(oldh, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV)); - strcpy(newh, find_unique_abbrev(sha1_new, DEFAULT_ABBREV)); - - if (in_merge_bases(current, updated)) { - fprintf(stderr, "* %s: fast-forward to %s\n", - name, note); - fprintf(stderr, " old..new: %s..%s\n", oldh, newh); - return update_ref_env("fast-forward", name, sha1_new, sha1_old); - } - if (!force) { - fprintf(stderr, - "* %s: not updating to non-fast-forward %s\n", - name, note); - fprintf(stderr, - " old...new: %s...%s\n", oldh, newh); - return 1; - } - fprintf(stderr, - "* %s: forcing update to non-fast-forward %s\n", - name, note); - fprintf(stderr, " old...new: %s...%s\n", oldh, newh); - return update_ref_env("forced-update", name, sha1_new, sha1_old); -} - -static int append_fetch_head(FILE *fp, - const char *head, const char *remote, - const char *remote_name, const char *remote_nick, - const char *local_name, int not_for_merge, - int verbose, int force) -{ - struct commit *commit; - int remote_len, i, note_len; - unsigned char sha1[20]; - char note[1024]; - const char *what, *kind; - - if (get_sha1(head, sha1)) - return error("Not a valid object name: %s", head); - commit = lookup_commit_reference_gently(sha1, 1); - if (!commit) - not_for_merge = 1; - - if (!strcmp(remote_name, "HEAD")) { - kind = ""; - what = ""; - } - else if (!strncmp(remote_name, "refs/heads/", 11)) { - kind = "branch"; - what = remote_name + 11; - } - else if (!strncmp(remote_name, "refs/tags/", 10)) { - kind = "tag"; - what = remote_name + 10; - } - else if (!strncmp(remote_name, "refs/remotes/", 13)) { - kind = "remote-tracking branch"; - what = remote_name + 13; - } - else { - kind = ""; - what = remote_name; - } - - remote_len = strlen(remote); - for (i = remote_len - 1; remote[i] == '/' && 0 <= i; i--) - ; - remote_len = i + 1; - if (4 < i && !strncmp(".git", remote + i - 3, 4)) - remote_len = i - 3; - - note_len = 0; - if (*what) { - if (*kind) - note_len += sprintf(note + note_len, "%s ", kind); - note_len += sprintf(note + note_len, "'%s' of ", what); - } - note_len += sprintf(note + note_len, "%.*s", remote_len, remote); - fprintf(fp, "%s\t%s\t%s\n", - sha1_to_hex(commit ? commit->object.sha1 : sha1), - not_for_merge ? "not-for-merge" : "", - note); - return update_local_ref(local_name, head, note, verbose, force); -} - -static char *keep; -static void remove_keep(void) -{ - if (keep && *keep) - unlink(keep); -} - -static void remove_keep_on_signal(int signo) -{ - remove_keep(); - sigchain_pop(signo); - raise(signo); -} - -static char *find_local_name(const char *remote_name, const char *refs, - int *force_p, int *not_for_merge_p) -{ - const char *ref = refs; - int len = strlen(remote_name); - - while (ref) { - const char *next; - int single_force, not_for_merge; - - while (*ref == '\n') - ref++; - if (!*ref) - break; - next = strchr(ref, '\n'); - - single_force = not_for_merge = 0; - if (*ref == '+') { - single_force = 1; - ref++; - } - if (*ref == '.') { - not_for_merge = 1; - ref++; - if (*ref == '+') { - single_force = 1; - ref++; - } - } - if (!strncmp(remote_name, ref, len) && ref[len] == ':') { - const char *local_part = ref + len + 1; - int retlen; - - if (!next) - retlen = strlen(local_part); - else - retlen = next - local_part; - *force_p = single_force; - *not_for_merge_p = not_for_merge; - return xmemdupz(local_part, retlen); - } - ref = next; - } - return NULL; -} - -static int fetch_native_store(FILE *fp, - const char *remote, - const char *remote_nick, - const char *refs, - int verbose, int force) -{ - char buffer[1024]; - int err = 0; - - sigchain_push_common(remove_keep_on_signal); - atexit(remove_keep); - - while (fgets(buffer, sizeof(buffer), stdin)) { - int len; - char *cp; - char *local_name; - int single_force, not_for_merge; - - for (cp = buffer; *cp && !isspace(*cp); cp++) - ; - if (*cp) - *cp++ = 0; - len = strlen(cp); - if (len && cp[len-1] == '\n') - cp[--len] = 0; - if (!strcmp(buffer, "failed")) - die("Fetch failure: %s", remote); - if (!strcmp(buffer, "pack")) - continue; - if (!strcmp(buffer, "keep")) { - char *od = get_object_directory(); - int len = strlen(od) + strlen(cp) + 50; - keep = xmalloc(len); - sprintf(keep, "%s/pack/pack-%s.keep", od, cp); - continue; - } - - local_name = find_local_name(cp, refs, - &single_force, ¬_for_merge); - if (!local_name) - continue; - err |= append_fetch_head(fp, - buffer, remote, cp, remote_nick, - local_name, not_for_merge, - verbose, force || single_force); - } - return err; -} - -static int parse_reflist(const char *reflist) -{ - const char *ref; - - printf("refs='"); - for (ref = reflist; ref; ) { - const char *next; - while (*ref && isspace(*ref)) - ref++; - if (!*ref) - break; - for (next = ref; *next && !isspace(*next); next++) - ; - printf("\n%.*s", (int)(next - ref), ref); - ref = next; - } - printf("'\n"); - - printf("rref='"); - for (ref = reflist; ref; ) { - const char *next, *colon; - while (*ref && isspace(*ref)) - ref++; - if (!*ref) - break; - for (next = ref; *next && !isspace(*next); next++) - ; - if (*ref == '.') - ref++; - if (*ref == '+') - ref++; - colon = strchr(ref, ':'); - putchar('\n'); - printf("%.*s", (int)((colon ? colon : next) - ref), ref); - ref = next; - } - printf("'\n"); - return 0; -} - -static int expand_refs_wildcard(const char *ls_remote_result, int numrefs, - const char **refs) -{ - int i, matchlen, replacelen; - int found_one = 0; - const char *remote = *refs++; - numrefs--; - - if (numrefs == 0) { - fprintf(stderr, "Nothing specified for fetching with remote.%s.fetch\n", - remote); - printf("empty\n"); - } - - for (i = 0; i < numrefs; i++) { - const char *ref = refs[i]; - const char *lref = ref; - const char *colon; - const char *tail; - const char *ls; - const char *next; - - if (*lref == '+') - lref++; - colon = strchr(lref, ':'); - tail = lref + strlen(lref); - if (!(colon && - 2 < colon - lref && - colon[-1] == '*' && - colon[-2] == '/' && - 2 < tail - (colon + 1) && - tail[-1] == '*' && - tail[-2] == '/')) { - /* not a glob */ - if (!found_one++) - printf("explicit\n"); - printf("%s\n", ref); - continue; - } - - /* glob */ - if (!found_one++) - printf("glob\n"); - - /* lref to colon-2 is remote hierarchy name; - * colon+1 to tail-2 is local. - */ - matchlen = (colon-1) - lref; - replacelen = (tail-1) - (colon+1); - for (ls = ls_remote_result; ls; ls = next) { - const char *eol; - unsigned char sha1[20]; - int namelen; - - while (*ls && isspace(*ls)) - ls++; - next = strchr(ls, '\n'); - eol = !next ? (ls + strlen(ls)) : next; - if (!memcmp("^{}", eol-3, 3)) - continue; - if (eol - ls < 40) - continue; - if (get_sha1_hex(ls, sha1)) - continue; - ls += 40; - while (ls < eol && isspace(*ls)) - ls++; - /* ls to next (or eol) is the name. - * is it identical to lref to colon-2? - */ - if ((eol - ls) <= matchlen || - strncmp(ls, lref, matchlen)) - continue; - - /* Yes, it is a match */ - namelen = eol - ls; - if (lref != ref) - putchar('+'); - printf("%.*s:%.*s%.*s\n", - namelen, ls, - replacelen, colon + 1, - namelen - matchlen, ls + matchlen); - } - } - return 0; -} - -static int pick_rref(int sha1_only, const char *rref, const char *ls_remote_result) -{ - int err = 0; - int lrr_count = lrr_count, i, pass; - const char *cp; - struct lrr { - const char *line; - const char *name; - int namelen; - int shown; - } *lrr_list = lrr_list; - - for (pass = 0; pass < 2; pass++) { - /* pass 0 counts and allocates, pass 1 fills... */ - cp = ls_remote_result; - i = 0; - while (1) { - const char *np; - while (*cp && isspace(*cp)) - cp++; - if (!*cp) - break; - np = strchrnul(cp, '\n'); - if (pass) { - lrr_list[i].line = cp; - lrr_list[i].name = cp + 41; - lrr_list[i].namelen = np - (cp + 41); - } - i++; - cp = np; - } - if (!pass) { - lrr_count = i; - lrr_list = xcalloc(lrr_count, sizeof(*lrr_list)); - } - } - - while (1) { - const char *next; - int rreflen; - int i; - - while (*rref && isspace(*rref)) - rref++; - if (!*rref) - break; - next = strchrnul(rref, '\n'); - rreflen = next - rref; - - for (i = 0; i < lrr_count; i++) { - struct lrr *lrr = &(lrr_list[i]); - - if (rreflen == lrr->namelen && - !memcmp(lrr->name, rref, rreflen)) { - if (!lrr->shown) - printf("%.*s\n", - sha1_only ? 40 : lrr->namelen + 41, - lrr->line); - lrr->shown = 1; - break; - } - } - if (lrr_count <= i) { - error("pick-rref: %.*s not found", rreflen, rref); - err = 1; - } - rref = next; - } - free(lrr_list); - return err; -} - -int cmd_fetch__tool(int argc, const char **argv, const char *prefix) -{ - int verbose = 0; - int force = 0; - int sopt = 0; - - while (1 < argc) { - const char *arg = argv[1]; - if (!strcmp("-v", arg)) - verbose = 1; - else if (!strcmp("-f", arg)) - force = 1; - else if (!strcmp("-s", arg)) - sopt = 1; - else - break; - argc--; - argv++; - } - - if (argc <= 1) - return error("Missing subcommand"); - - if (!strcmp("append-fetch-head", argv[1])) { - int result; - FILE *fp; - char *filename; - - if (argc != 8) - return error("append-fetch-head takes 6 args"); - filename = git_path_fetch_head(); - fp = fopen(filename, "a"); - if (!fp) - return error("cannot open %s: %s", filename, strerror(errno)); - result = append_fetch_head(fp, argv[2], argv[3], - argv[4], argv[5], - argv[6], !!argv[7][0], - verbose, force); - fclose(fp); - return result; - } - if (!strcmp("native-store", argv[1])) { - int result; - FILE *fp; - char *filename; - - if (argc != 5) - return error("fetch-native-store takes 3 args"); - filename = git_path_fetch_head(); - fp = fopen(filename, "a"); - if (!fp) - return error("cannot open %s: %s", filename, strerror(errno)); - result = fetch_native_store(fp, argv[2], argv[3], argv[4], - verbose, force); - fclose(fp); - return result; - } - if (!strcmp("parse-reflist", argv[1])) { - const char *reflist; - if (argc != 3) - return error("parse-reflist takes 1 arg"); - reflist = argv[2]; - if (!strcmp(reflist, "-")) - reflist = get_stdin(); - return parse_reflist(reflist); - } - if (!strcmp("pick-rref", argv[1])) { - const char *ls_remote_result; - if (argc != 4) - return error("pick-rref takes 2 args"); - ls_remote_result = argv[3]; - if (!strcmp(ls_remote_result, "-")) - ls_remote_result = get_stdin(); - return pick_rref(sopt, argv[2], ls_remote_result); - } - if (!strcmp("expand-refs-wildcard", argv[1])) { - const char *reflist; - if (argc < 4) - return error("expand-refs-wildcard takes at least 2 args"); - reflist = argv[2]; - if (!strcmp(reflist, "-")) - reflist = get_stdin(); - return expand_refs_wildcard(reflist, argc - 3, argv + 3); - } - - return error("Unknown subcommand: %s", argv[1]); -} diff --git a/contrib/examples/git-am.sh b/contrib/examples/git-am.sh deleted file mode 100755 index dd539f1a8a..0000000000 --- a/contrib/examples/git-am.sh +++ /dev/null @@ -1,975 +0,0 @@ -#!/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-checkout.sh b/contrib/examples/git-checkout.sh deleted file mode 100755 index 683cae7c3f..0000000000 --- a/contrib/examples/git-checkout.sh +++ /dev/null @@ -1,302 +0,0 @@ -#!/bin/sh - -OPTIONS_KEEPDASHDASH=t -OPTIONS_SPEC="\ -git-checkout [options] [<branch>] [<paths>...] --- -b= create a new branch started at <branch> -l create the new branch's reflog -track arrange that the new branch tracks the remote branch -f proceed even if the index or working tree is not HEAD -m merge local modifications into the new branch -q,quiet be quiet -" -SUBDIRECTORY_OK=Sometimes -. git-sh-setup -require_work_tree - -old_name=HEAD -old=$(git rev-parse --verify $old_name 2>/dev/null) -oldbranch=$(git symbolic-ref $old_name 2>/dev/null) -new= -new_name= -force= -branch= -track= -newbranch= -newbranch_log= -merge= -quiet= -v=-v -LF=' -' - -while test $# != 0; do - case "$1" in - -b) - shift - newbranch="$1" - [ -z "$newbranch" ] && - die "git checkout: -b needs a branch name" - git show-ref --verify --quiet -- "refs/heads/$newbranch" && - die "git checkout: branch $newbranch already exists" - git check-ref-format "heads/$newbranch" || - die "git checkout: we do not like '$newbranch' as a branch name." - ;; - -l) - newbranch_log=-l - ;; - --track|--no-track) - track="$1" - ;; - -f) - force=1 - ;; - -m) - merge=1 - ;; - -q|--quiet) - quiet=1 - v= - ;; - --) - shift - break - ;; - *) - usage - ;; - esac - shift -done - -arg="$1" -rev=$(git rev-parse --verify "$arg" 2>/dev/null) -if rev=$(git rev-parse --verify "$rev^0" 2>/dev/null) -then - [ -z "$rev" ] && die "unknown flag $arg" - new_name="$arg" - if git show-ref --verify --quiet -- "refs/heads/$arg" - then - rev=$(git rev-parse --verify "refs/heads/$arg^0") - branch="$arg" - fi - new="$rev" - shift -elif rev=$(git rev-parse --verify "$rev^{tree}" 2>/dev/null) -then - # checking out selected paths from a tree-ish. - new="$rev" - new_name="$rev^{tree}" - shift -fi -[ "$1" = "--" ] && shift - -case "$newbranch,$track" in -,--*) - die "git checkout: --track and --no-track require -b" -esac - -case "$force$merge" in -11) - die "git checkout: -f and -m are incompatible" -esac - -# The behaviour of the command with and without explicit path -# parameters is quite different. -# -# Without paths, we are checking out everything in the work tree, -# possibly switching branches. This is the traditional behaviour. -# -# With paths, we are _never_ switching branch, but checking out -# the named paths from either index (when no rev is given), -# or the named tree-ish (when rev is given). - -if test "$#" -ge 1 -then - hint= - if test "$#" -eq 1 - then - hint=" -Did you intend to checkout '$@' which can not be resolved as commit?" - fi - if test '' != "$newbranch$force$merge" - then - die "git checkout: updating paths is incompatible with switching branches/forcing$hint" - fi - if test '' != "$new" - then - # from a specific tree-ish; note that this is for - # rescuing paths and is never meant to remove what - # is not in the named tree-ish. - git ls-tree --full-name -r "$new" "$@" | - git update-index --index-info || exit $? - fi - - # Make sure the request is about existing paths. - git ls-files --full-name --error-unmatch -- "$@" >/dev/null || exit - git ls-files --full-name -- "$@" | - (cd_to_toplevel && git checkout-index -f -u --stdin) - - # Run a post-checkout hook -- the HEAD does not change so the - # current HEAD is passed in for both args - if test -x "$GIT_DIR"/hooks/post-checkout; then - "$GIT_DIR"/hooks/post-checkout $old $old 0 - fi - - exit $? -else - # Make sure we did not fall back on $arg^{tree} codepath - # since we are not checking out from an arbitrary tree-ish, - # but switching branches. - if test '' != "$new" - then - git rev-parse --verify "$new^{commit}" >/dev/null 2>&1 || - die "Cannot switch branch to a non-commit." - fi -fi - -# We are switching branches and checking out trees, so -# we *NEED* to be at the toplevel. -cd_to_toplevel - -[ -z "$new" ] && new=$old && new_name="$old_name" - -# If we don't have an existing branch that we're switching to, -# and we don't have a new branch name for the target we -# are switching to, then we are detaching our HEAD from any -# branch. However, if "git checkout HEAD" detaches the HEAD -# from the current branch, even though that may be logically -# correct, it feels somewhat funny. More importantly, we do not -# want "git checkout" or "git checkout -f" to detach HEAD. - -detached= -detach_warn= - -describe_detached_head () { - test -n "$quiet" || { - printf >&2 "$1 " - GIT_PAGER= git log >&2 -1 --pretty=oneline --abbrev-commit "$2" -- - } -} - -if test -z "$branch$newbranch" && test "$new_name" != "$old_name" -then - detached="$new" - if test -n "$oldbranch" && test -z "$quiet" - then - detach_warn="Note: moving to \"$new_name\" which isn't a local branch -If you want to create a new branch from this checkout, you may do so -(now or later) by using -b with the checkout command again. Example: - git checkout -b <new_branch_name>" - fi -elif test -z "$oldbranch" && test "$new" != "$old" -then - describe_detached_head 'Previous HEAD position was' "$old" -fi - -if [ "X$old" = X ] -then - if test -z "$quiet" - then - echo >&2 "warning: You appear to be on a branch yet to be born." - echo >&2 "warning: Forcing checkout of $new_name." - fi - force=1 -fi - -if [ "$force" ] -then - git read-tree $v --reset -u $new -else - git update-index --refresh >/dev/null - git read-tree $v -m -u --exclude-per-directory=.gitignore $old $new || ( - case "$merge,$v" in - ,*) - exit 1 ;; - 1,) - ;; # quiet - *) - echo >&2 "Falling back to 3-way merge..." ;; - esac - - # Match the index to the working tree, and do a three-way. - git diff-files --name-only | git update-index --remove --stdin && - work=$(git write-tree) && - git read-tree $v --reset -u $new || exit - - eval GITHEAD_$new='${new_name:-${branch:-$new}}' && - eval GITHEAD_$work=local && - export GITHEAD_$new GITHEAD_$work && - git merge-recursive $old -- $new $work - - # Do not register the cleanly merged paths in the index yet. - # this is not a real merge before committing, but just carrying - # the working tree changes along. - unmerged=$(git ls-files -u) - git read-tree $v --reset $new - case "$unmerged" in - '') ;; - *) - ( - z40=0000000000000000000000000000000000000000 - echo "$unmerged" | - sed -e 's/^[0-7]* [0-9a-f]* /'"0 $z40 /" - echo "$unmerged" - ) | git update-index --index-info - ;; - esac - exit 0 - ) - saved_err=$? - if test "$saved_err" = 0 && test -z "$quiet" - then - git diff-index --name-status "$new" - fi - (exit $saved_err) -fi - -# -# Switch the HEAD pointer to the new branch if we -# checked out a branch head, and remove any potential -# old MERGE_HEAD's (subsequent commits will clearly not -# be based on them, since we re-set the index) -# -if [ "$?" -eq 0 ]; then - if [ "$newbranch" ]; then - git branch $track $newbranch_log "$newbranch" "$new_name" || exit - branch="$newbranch" - fi - if test -n "$branch" - then - old_branch_name=$(expr "z$oldbranch" : 'zrefs/heads/\(.*\)') - GIT_DIR="$GIT_DIR" git symbolic-ref -m "checkout: moving from ${old_branch_name:-$old} to $branch" HEAD "refs/heads/$branch" - if test -n "$quiet" - then - true # nothing - elif test "refs/heads/$branch" = "$oldbranch" - then - echo >&2 "Already on branch \"$branch\"" - else - echo >&2 "Switched to${newbranch:+ a new} branch \"$branch\"" - fi - elif test -n "$detached" - then - old_branch_name=$(expr "z$oldbranch" : 'zrefs/heads/\(.*\)') - git update-ref --no-deref -m "checkout: moving from ${old_branch_name:-$old} to $arg" HEAD "$detached" || - die "Cannot detach HEAD" - if test -n "$detach_warn" - then - echo >&2 "$detach_warn" - fi - describe_detached_head 'HEAD is now at' HEAD - fi - rm -f "$GIT_DIR/MERGE_HEAD" -else - exit 1 -fi - -# Run a post-checkout hook -if test -x "$GIT_DIR"/hooks/post-checkout; then - "$GIT_DIR"/hooks/post-checkout $old $new 1 -fi diff --git a/contrib/examples/git-clean.sh b/contrib/examples/git-clean.sh deleted file mode 100755 index 01c95e9fe8..0000000000 --- a/contrib/examples/git-clean.sh +++ /dev/null @@ -1,118 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2005-2006 Pavel Roskin -# - -OPTIONS_KEEPDASHDASH= -OPTIONS_SPEC="\ -git-clean [options] <paths>... - -Clean untracked files from the working directory - -When optional <paths>... arguments are given, the paths -affected are further limited to those that match them. --- -d remove directories as well -f override clean.requireForce and clean anyway -n don't remove anything, just show what would be done -q be quiet, only report errors -x remove ignored files as well -X remove only ignored files" - -SUBDIRECTORY_OK=Yes -. git-sh-setup -require_work_tree - -ignored= -ignoredonly= -cleandir= -rmf="rm -f --" -rmrf="rm -rf --" -rm_refuse="echo Not removing" -echo1="echo" - -disabled=$(git config --bool clean.requireForce) - -while test $# != 0 -do - case "$1" in - -d) - cleandir=1 - ;; - -f) - disabled=false - ;; - -n) - disabled=false - rmf="echo Would remove" - rmrf="echo Would remove" - rm_refuse="echo Would not remove" - echo1=":" - ;; - -q) - echo1=":" - ;; - -x) - ignored=1 - ;; - -X) - ignoredonly=1 - ;; - --) - shift - break - ;; - *) - usage # should not happen - ;; - esac - shift -done - -# requireForce used to default to false but now it defaults to true. -# IOW, lack of explicit "clean.requireForce = false" is taken as -# "clean.requireForce = true". -case "$disabled" in -"") - die "clean.requireForce not set and -n or -f not given; refusing to clean" - ;; -"true") - die "clean.requireForce set and -n or -f not given; refusing to clean" - ;; -esac - -if [ "$ignored,$ignoredonly" = "1,1" ]; then - die "-x and -X cannot be set together" -fi - -if [ -z "$ignored" ]; then - excl="--exclude-per-directory=.gitignore" - excl_info= excludes_file= - if [ -f "$GIT_DIR/info/exclude" ]; then - excl_info="--exclude-from=$GIT_DIR/info/exclude" - fi - if cfg_excl=$(git config core.excludesfile) && test -f "$cfg_excl" - then - excludes_file="--exclude-from=$cfg_excl" - fi - if [ "$ignoredonly" ]; then - excl="$excl --ignored" - fi -fi - -git ls-files --others --directory \ - $excl ${excl_info:+"$excl_info"} ${excludes_file:+"$excludes_file"} \ - -- "$@" | -while read -r file; do - if [ -d "$file" -a ! -L "$file" ]; then - if [ -z "$cleandir" ]; then - $rm_refuse "$file" - continue - fi - $echo1 "Removing $file" - $rmrf "$file" - else - $echo1 "Removing $file" - $rmf "$file" - fi -done diff --git a/contrib/examples/git-clone.sh b/contrib/examples/git-clone.sh deleted file mode 100755 index 08cf246bbb..0000000000 --- a/contrib/examples/git-clone.sh +++ /dev/null @@ -1,525 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2005, Linus Torvalds -# Copyright (c) 2005, Junio C Hamano -# -# Clone a repository into a different directory that does not yet exist. - -# See git-sh-setup why. -unset CDPATH - -OPTIONS_SPEC="\ -git-clone [options] [--] <repo> [<dir>] --- -n,no-checkout don't create a checkout -bare create a bare repository -naked create a bare repository -l,local to clone from a local repository -no-hardlinks don't use local hardlinks, always copy -s,shared setup as a shared repository -template= path to the template directory -q,quiet be quiet -reference= reference repository -o,origin= use <name> instead of 'origin' to track upstream -u,upload-pack= path to git-upload-pack on the remote -depth= create a shallow clone of that depth - -use-separate-remote compatibility, do not use -no-separate-remote compatibility, do not use" - -die() { - echo >&2 "$@" - exit 1 -} - -usage() { - exec "$0" -h -} - -eval "$(echo "$OPTIONS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?)" - -get_repo_base() { - ( - cd "$(/bin/pwd)" && - cd "$1" || cd "$1.git" && - { - cd .git - pwd - } - ) 2>/dev/null -} - -if [ -n "$GIT_SSL_NO_VERIFY" -o \ - "$(git config --bool http.sslVerify)" = false ]; then - curl_extra_args="-k" -fi - -http_fetch () { - # $1 = Remote, $2 = Local - curl -nsfL $curl_extra_args "$1" >"$2" - curl_exit_status=$? - case $curl_exit_status in - 126|127) exit ;; - *) return $curl_exit_status ;; - esac -} - -clone_dumb_http () { - # $1 - remote, $2 - local - cd "$2" && - clone_tmp="$GIT_DIR/clone-tmp" && - mkdir -p "$clone_tmp" || exit 1 - if [ -n "$GIT_CURL_FTP_NO_EPSV" -o \ - "$(git config --bool http.noEPSV)" = true ]; then - curl_extra_args="${curl_extra_args} --disable-epsv" - fi - http_fetch "$1/info/refs" "$clone_tmp/refs" || - die "Cannot get remote repository information. -Perhaps git-update-server-info needs to be run there?" - test "z$quiet" = z && v=-v || v= - while read sha1 refname - do - name=$(expr "z$refname" : 'zrefs/\(.*\)') && - case "$name" in - *^*) continue;; - esac - case "$bare,$name" in - yes,* | ,heads/* | ,tags/*) ;; - *) continue ;; - esac - if test -n "$use_separate_remote" && - branch_name=$(expr "z$name" : 'zheads/\(.*\)') - then - tname="remotes/$origin/$branch_name" - else - tname=$name - fi - git-http-fetch $v -a -w "$tname" "$sha1" "$1" || exit 1 - done <"$clone_tmp/refs" - rm -fr "$clone_tmp" - http_fetch "$1/HEAD" "$GIT_DIR/REMOTE_HEAD" || - rm -f "$GIT_DIR/REMOTE_HEAD" - if test -f "$GIT_DIR/REMOTE_HEAD"; then - head_sha1=$(cat "$GIT_DIR/REMOTE_HEAD") - case "$head_sha1" in - 'ref: refs/'*) - ;; - *) - git-http-fetch $v -a "$head_sha1" "$1" || - rm -f "$GIT_DIR/REMOTE_HEAD" - ;; - esac - fi -} - -quiet= -local=no -use_local_hardlink=yes -local_shared=no -unset template -no_checkout= -upload_pack= -bare= -reference= -origin= -origin_override= -use_separate_remote=t -depth= -no_progress= -local_explicitly_asked_for= -test -t 1 || no_progress=--no-progress - -while test $# != 0 -do - case "$1" in - -n|--no-checkout) - no_checkout=yes ;; - --naked|--bare) - bare=yes ;; - -l|--local) - local_explicitly_asked_for=yes - use_local_hardlink=yes - ;; - --no-hardlinks) - use_local_hardlink=no ;; - -s|--shared) - local_shared=yes ;; - --template) - shift; template="--template=$1" ;; - -q|--quiet) - quiet=-q ;; - --use-separate-remote|--no-separate-remote) - die "clones are always made with separate-remote layout" ;; - --reference) - shift; reference="$1" ;; - -o|--origin) - shift; - case "$1" in - '') - usage ;; - */*) - die "'$1' is not suitable for an origin name" - esac - git check-ref-format "heads/$1" || - die "'$1' is not suitable for a branch name" - test -z "$origin_override" || - die "Do not give more than one --origin options." - origin_override=yes - origin="$1" - ;; - -u|--upload-pack) - shift - upload_pack="--upload-pack=$1" ;; - --depth) - shift - depth="--depth=$1" ;; - --) - shift - break ;; - *) - usage ;; - esac - shift -done - -repo="$1" -test -n "$repo" || - die 'you must specify a repository to clone.' - -# --bare implies --no-checkout and --no-separate-remote -if test yes = "$bare" -then - if test yes = "$origin_override" - then - die '--bare and --origin $origin options are incompatible.' - fi - no_checkout=yes - use_separate_remote= -fi - -if test -z "$origin" -then - origin=origin -fi - -# Turn the source into an absolute path if -# it is local -if base=$(get_repo_base "$repo"); then - repo="$base" - if test -z "$depth" - then - local=yes - fi -elif test -f "$repo" -then - case "$repo" in /*) ;; *) repo="$PWD/$repo" ;; esac -fi - -# Decide the directory name of the new repository -if test -n "$2" -then - dir="$2" - test $# = 2 || die "excess parameter to git-clone" -else - # Derive one from the repository name - # Try using "humanish" part of source repo if user didn't specify one - if test -f "$repo" - then - # Cloning from a bundle - dir=$(echo "$repo" | sed -e 's|/*\.bundle$||' -e 's|.*/||g') - else - dir=$(echo "$repo" | - sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g') - fi -fi - -[ -e "$dir" ] && die "destination directory '$dir' already exists." -[ yes = "$bare" ] && unset GIT_WORK_TREE -[ -n "$GIT_WORK_TREE" ] && [ -e "$GIT_WORK_TREE" ] && -die "working tree '$GIT_WORK_TREE' already exists." -D= -W= -cleanup() { - test -z "$D" && rm -rf "$dir" - test -z "$W" && test -n "$GIT_WORK_TREE" && rm -rf "$GIT_WORK_TREE" - cd .. - test -n "$D" && rm -rf "$D" - test -n "$W" && rm -rf "$W" - exit $err -} -trap 'err=$?; cleanup' 0 -mkdir -p "$dir" && D=$(cd "$dir" && pwd) || usage -test -n "$GIT_WORK_TREE" && mkdir -p "$GIT_WORK_TREE" && -W=$(cd "$GIT_WORK_TREE" && pwd) && GIT_WORK_TREE="$W" && export GIT_WORK_TREE -if test yes = "$bare" || test -n "$GIT_WORK_TREE"; then - GIT_DIR="$D" -else - GIT_DIR="$D/.git" -fi && -export GIT_DIR && -GIT_CONFIG="$GIT_DIR/config" git-init $quiet ${template+"$template"} || usage - -if test -n "$bare" -then - GIT_CONFIG="$GIT_DIR/config" git config core.bare true -fi - -if test -n "$reference" -then - ref_git= - if test -d "$reference" - then - if test -d "$reference/.git/objects" - then - ref_git="$reference/.git" - elif test -d "$reference/objects" - then - ref_git="$reference" - fi - fi - if test -n "$ref_git" - then - ref_git=$(cd "$ref_git" && pwd) - echo "$ref_git/objects" >"$GIT_DIR/objects/info/alternates" - ( - GIT_DIR="$ref_git" git for-each-ref \ - --format='%(objectname) %(*objectname)' - ) | - while read a b - do - test -z "$a" || - git update-ref "refs/reference-tmp/$a" "$a" - test -z "$b" || - git update-ref "refs/reference-tmp/$b" "$b" - done - else - die "reference repository '$reference' is not a local directory." - fi -fi - -rm -f "$GIT_DIR/CLONE_HEAD" - -# We do local magic only when the user tells us to. -case "$local" in -yes) - ( cd "$repo/objects" ) || - die "cannot chdir to local '$repo/objects'." - - if test "$local_shared" = yes - then - mkdir -p "$GIT_DIR/objects/info" - echo "$repo/objects" >>"$GIT_DIR/objects/info/alternates" - else - cpio_quiet_flag="" - cpio --help 2>&1 | grep -- --quiet >/dev/null && \ - cpio_quiet_flag=--quiet - l= && - if test "$use_local_hardlink" = yes - then - # See if we can hardlink and drop "l" if not. - sample_file=$(cd "$repo" && \ - find objects -type f -print | sed -e 1q) - # objects directory should not be empty because - # we are cloning! - test -f "$repo/$sample_file" || - die "fatal: cannot clone empty repository" - if ln "$repo/$sample_file" "$GIT_DIR/objects/sample" 2>/dev/null - then - rm -f "$GIT_DIR/objects/sample" - l=l - elif test -n "$local_explicitly_asked_for" - then - echo >&2 "Warning: -l asked but cannot hardlink to $repo" - fi - fi && - cd "$repo" && - # Create dirs using umask and permissions and destination - find objects -type d -print | (cd "$GIT_DIR" && xargs mkdir -p) && - # Copy existing 0444 permissions on content - find objects ! -type d -print | cpio $cpio_quiet_flag -pumd$l "$GIT_DIR/" || \ - exit 1 - fi - git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD" || exit 1 - ;; -*) - case "$repo" in - rsync://*) - case "$depth" in - "") ;; - *) die "shallow over rsync not supported" ;; - esac - rsync $quiet -av --ignore-existing \ - --exclude info "$repo/objects/" "$GIT_DIR/objects/" || - exit - # Look at objects/info/alternates for rsync -- http will - # support it natively and git native ones will do it on the - # remote end. Not having that file is not a crime. - rsync -q "$repo/objects/info/alternates" \ - "$GIT_DIR/TMP_ALT" 2>/dev/null || - rm -f "$GIT_DIR/TMP_ALT" - if test -f "$GIT_DIR/TMP_ALT" - then - ( cd "$D" && - . git-parse-remote && - resolve_alternates "$repo" <"$GIT_DIR/TMP_ALT" ) | - while read alt - do - case "$alt" in 'bad alternate: '*) die "$alt";; esac - case "$quiet" in - '') echo >&2 "Getting alternate: $alt" ;; - esac - rsync $quiet -av --ignore-existing \ - --exclude info "$alt" "$GIT_DIR/objects" || exit - done - rm -f "$GIT_DIR/TMP_ALT" - fi - git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD" || exit 1 - ;; - https://*|http://*|ftp://*) - case "$depth" in - "") ;; - *) die "shallow over http or ftp not supported" ;; - esac - if test -z "@@NO_CURL@@" - then - clone_dumb_http "$repo" "$D" - else - die "http transport not supported, rebuild Git with curl support" - fi - ;; - *) - if [ -f "$repo" ] ; then - git bundle unbundle "$repo" > "$GIT_DIR/CLONE_HEAD" || - die "unbundle from '$repo' failed." - else - case "$upload_pack" in - '') git-fetch-pack --all -k $quiet $depth $no_progress "$repo";; - *) git-fetch-pack --all -k \ - $quiet "$upload_pack" $depth $no_progress "$repo" ;; - esac >"$GIT_DIR/CLONE_HEAD" || - die "fetch-pack from '$repo' failed." - fi - ;; - esac - ;; -esac -test -d "$GIT_DIR/refs/reference-tmp" && rm -fr "$GIT_DIR/refs/reference-tmp" - -if test -f "$GIT_DIR/CLONE_HEAD" -then - # Read git-fetch-pack -k output and store the remote branches. - if [ -n "$use_separate_remote" ] - then - branch_top="remotes/$origin" - else - branch_top="heads" - fi - tag_top="tags" - while read sha1 name - do - case "$name" in - *'^{}') - continue ;; - HEAD) - destname="REMOTE_HEAD" ;; - refs/heads/*) - destname="refs/$branch_top/${name#refs/heads/}" ;; - refs/tags/*) - destname="refs/$tag_top/${name#refs/tags/}" ;; - *) - continue ;; - esac - git update-ref -m "clone: from $repo" "$destname" "$sha1" "" - done < "$GIT_DIR/CLONE_HEAD" -fi - -if test -n "$W"; then - cd "$W" || exit -else - cd "$D" || exit -fi - -if test -z "$bare" -then - # a non-bare repository is always in separate-remote layout - remote_top="refs/remotes/$origin" - head_sha1= - test ! -r "$GIT_DIR/REMOTE_HEAD" || head_sha1=$(cat "$GIT_DIR/REMOTE_HEAD") - case "$head_sha1" in - 'ref: refs/'*) - # Uh-oh, the remote told us (http transport done against - # new style repository with a symref HEAD). - # Ideally we should skip the guesswork but for now - # opt for minimum change. - head_sha1=$(expr "z$head_sha1" : 'zref: refs/heads/\(.*\)') - head_sha1=$(cat "$GIT_DIR/$remote_top/$head_sha1") - ;; - esac - - # The name under $remote_top the remote HEAD seems to point at. - head_points_at=$( - ( - test -f "$GIT_DIR/$remote_top/master" && echo "master" - cd "$GIT_DIR/$remote_top" && - find . -type f -print | sed -e 's/^\.\///' - ) | ( - done=f - while read name - do - test t = $done && continue - branch_tip=$(cat "$GIT_DIR/$remote_top/$name") - if test "$head_sha1" = "$branch_tip" - then - echo "$name" - done=t - fi - done - ) - ) - - # Upstream URL - git config remote."$origin".url "$repo" && - - # Set up the mappings to track the remote branches. - git config remote."$origin".fetch \ - "+refs/heads/*:$remote_top/*" '^$' && - - # Write out remote.$origin config, and update our "$head_points_at". - case "$head_points_at" in - ?*) - # Local default branch - git symbolic-ref HEAD "refs/heads/$head_points_at" && - - # Tracking branch for the primary branch at the remote. - git update-ref HEAD "$head_sha1" && - - rm -f "refs/remotes/$origin/HEAD" - git symbolic-ref "refs/remotes/$origin/HEAD" \ - "refs/remotes/$origin/$head_points_at" && - - git config branch."$head_points_at".remote "$origin" && - git config branch."$head_points_at".merge "refs/heads/$head_points_at" - ;; - '') - if test -z "$head_sha1" - then - # Source had nonexistent ref in HEAD - echo >&2 "Warning: Remote HEAD refers to nonexistent ref, unable to checkout." - no_checkout=t - else - # Source had detached HEAD pointing nowhere - git update-ref --no-deref HEAD "$head_sha1" && - rm -f "refs/remotes/$origin/HEAD" - fi - ;; - esac - - case "$no_checkout" in - '') - test "z$quiet" = z && test "z$no_progress" = z && v=-v || v= - git read-tree -m -u $v HEAD HEAD - esac -fi -rm -f "$GIT_DIR/CLONE_HEAD" "$GIT_DIR/REMOTE_HEAD" - -trap - 0 diff --git a/contrib/examples/git-commit.sh b/contrib/examples/git-commit.sh deleted file mode 100755 index 86c9cfa0c7..0000000000 --- a/contrib/examples/git-commit.sh +++ /dev/null @@ -1,639 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2005 Linus Torvalds -# Copyright (c) 2006 Junio C Hamano - -USAGE='[-a | --interactive] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit> | --amend] [-u] [-e] [--author <author>] [--template <file>] [[-i | -o] <path>...]' -SUBDIRECTORY_OK=Yes -OPTIONS_SPEC= -. git-sh-setup -require_work_tree - -git rev-parse --verify HEAD >/dev/null 2>&1 || initial_commit=t - -case "$0" in -*status) - status_only=t - ;; -*commit) - status_only= - ;; -esac - -refuse_partial () { - echo >&2 "$1" - echo >&2 "You might have meant to say 'git commit -i paths...', perhaps?" - exit 1 -} - -TMP_INDEX= -THIS_INDEX="${GIT_INDEX_FILE:-$GIT_DIR/index}" -NEXT_INDEX="$GIT_DIR/next-index$$" -rm -f "$NEXT_INDEX" -save_index () { - cp -p "$THIS_INDEX" "$NEXT_INDEX" -} - -run_status () { - # If TMP_INDEX is defined, that means we are doing - # "--only" partial commit, and that index file is used - # to build the tree for the commit. Otherwise, if - # NEXT_INDEX exists, that is the index file used to - # make the commit. Otherwise we are using as-is commit - # so the regular index file is what we use to compare. - if test '' != "$TMP_INDEX" - then - GIT_INDEX_FILE="$TMP_INDEX" - export GIT_INDEX_FILE - elif test -f "$NEXT_INDEX" - then - GIT_INDEX_FILE="$NEXT_INDEX" - export GIT_INDEX_FILE - fi - - if test "$status_only" = "t" || test "$use_status_color" = "t"; then - color= - else - color=--nocolor - fi - git runstatus ${color} \ - ${verbose:+--verbose} \ - ${amend:+--amend} \ - ${untracked_files:+--untracked} -} - -trap ' - test -z "$TMP_INDEX" || { - test -f "$TMP_INDEX" && rm -f "$TMP_INDEX" - } - rm -f "$NEXT_INDEX" -' 0 - -################################################################ -# Command line argument parsing and sanity checking - -all= -also= -allow_empty=f -interactive= -only= -logfile= -use_commit= -amend= -edit_flag= -no_edit= -log_given= -log_message= -verify=t -quiet= -verbose= -signoff= -force_author= -only_include_assumed= -untracked_files= -templatefile="$(git config commit.template)" -while test $# != 0 -do - case "$1" in - -F|--F|-f|--f|--fi|--fil|--file) - case "$#" in 1) usage ;; esac - shift - no_edit=t - log_given=t$log_given - logfile="$1" - ;; - -F*|-f*) - no_edit=t - log_given=t$log_given - logfile="${1#-[Ff]}" - ;; - --F=*|--f=*|--fi=*|--fil=*|--file=*) - no_edit=t - log_given=t$log_given - logfile="${1#*=}" - ;; - -a|--a|--al|--all) - all=t - ;; - --allo|--allow|--allow-|--allow-e|--allow-em|--allow-emp|\ - --allow-empt|--allow-empty) - allow_empty=t - ;; - --au=*|--aut=*|--auth=*|--autho=*|--author=*) - force_author="${1#*=}" - ;; - --au|--aut|--auth|--autho|--author) - case "$#" in 1) usage ;; esac - shift - force_author="$1" - ;; - -e|--e|--ed|--edi|--edit) - edit_flag=t - ;; - -i|--i|--in|--inc|--incl|--inclu|--includ|--include) - also=t - ;; - --int|--inte|--inter|--intera|--interac|--interact|--interacti|\ - --interactiv|--interactive) - interactive=t - ;; - -o|--o|--on|--onl|--only) - only=t - ;; - -m|--m|--me|--mes|--mess|--messa|--messag|--message) - case "$#" in 1) usage ;; esac - shift - log_given=m$log_given - log_message="${log_message:+${log_message} - -}$1" - no_edit=t - ;; - -m*) - log_given=m$log_given - log_message="${log_message:+${log_message} - -}${1#-m}" - no_edit=t - ;; - --m=*|--me=*|--mes=*|--mess=*|--messa=*|--messag=*|--message=*) - log_given=m$log_given - log_message="${log_message:+${log_message} - -}${1#*=}" - no_edit=t - ;; - -n|--n|--no|--no-|--no-v|--no-ve|--no-ver|--no-veri|--no-verif|\ - --no-verify) - verify= - ;; - --a|--am|--ame|--amen|--amend) - amend=t - use_commit=HEAD - ;; - -c) - case "$#" in 1) usage ;; esac - shift - log_given=t$log_given - use_commit="$1" - no_edit= - ;; - --ree=*|--reed=*|--reedi=*|--reedit=*|--reedit-=*|--reedit-m=*|\ - --reedit-me=*|--reedit-mes=*|--reedit-mess=*|--reedit-messa=*|\ - --reedit-messag=*|--reedit-message=*) - log_given=t$log_given - use_commit="${1#*=}" - no_edit= - ;; - --ree|--reed|--reedi|--reedit|--reedit-|--reedit-m|--reedit-me|\ - --reedit-mes|--reedit-mess|--reedit-messa|--reedit-messag|\ - --reedit-message) - case "$#" in 1) usage ;; esac - shift - log_given=t$log_given - use_commit="$1" - no_edit= - ;; - -C) - case "$#" in 1) usage ;; esac - shift - log_given=t$log_given - use_commit="$1" - no_edit=t - ;; - --reu=*|--reus=*|--reuse=*|--reuse-=*|--reuse-m=*|--reuse-me=*|\ - --reuse-mes=*|--reuse-mess=*|--reuse-messa=*|--reuse-messag=*|\ - --reuse-message=*) - log_given=t$log_given - use_commit="${1#*=}" - no_edit=t - ;; - --reu|--reus|--reuse|--reuse-|--reuse-m|--reuse-me|--reuse-mes|\ - --reuse-mess|--reuse-messa|--reuse-messag|--reuse-message) - case "$#" in 1) usage ;; esac - shift - log_given=t$log_given - use_commit="$1" - no_edit=t - ;; - -s|--s|--si|--sig|--sign|--signo|--signof|--signoff) - signoff=t - ;; - -t|--t|--te|--tem|--temp|--templ|--templa|--templat|--template) - case "$#" in 1) usage ;; esac - shift - templatefile="$1" - no_edit= - ;; - -q|--q|--qu|--qui|--quie|--quiet) - quiet=t - ;; - -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose) - verbose=t - ;; - -u|--u|--un|--unt|--untr|--untra|--untrac|--untrack|--untracke|\ - --untracked|--untracked-|--untracked-f|--untracked-fi|--untracked-fil|\ - --untracked-file|--untracked-files) - untracked_files=t - ;; - --) - shift - break - ;; - -*) - usage - ;; - *) - break - ;; - esac - shift -done -case "$edit_flag" in t) no_edit= ;; esac - -################################################################ -# Sanity check options - -case "$amend,$initial_commit" in -t,t) - die "You do not have anything to amend." ;; -t,) - if [ -f "$GIT_DIR/MERGE_HEAD" ]; then - die "You are in the middle of a merge -- cannot amend." - fi ;; -esac - -case "$log_given" in -tt*) - die "Only one of -c/-C/-F can be used." ;; -*tm*|*mt*) - die "Option -m cannot be combined with -c/-C/-F." ;; -esac - -case "$#,$also,$only,$amend" in -*,t,t,*) - die "Only one of --include/--only can be used." ;; -0,t,,* | 0,,t,) - die "No paths with --include/--only does not make sense." ;; -0,,t,t) - only_include_assumed="# Clever... amending the last one with dirty index." ;; -0,,,*) - ;; -*,,,*) - only_include_assumed="# Explicit paths specified without -i or -o; assuming --only paths..." - also= - ;; -esac -unset only -case "$all,$interactive,$also,$#" in -*t,*t,*) - die "Cannot use -a, --interactive or -i at the same time." ;; -t,,,[1-9]*) - die "Paths with -a does not make sense." ;; -,t,,[1-9]*) - die "Paths with --interactive does not make sense." ;; -,,t,0) - die "No paths with -i does not make sense." ;; -esac - -if test ! -z "$templatefile" && test -z "$log_given" -then - if test ! -f "$templatefile" - then - die "Commit template file does not exist." - fi -fi - -################################################################ -# Prepare index to have a tree to be committed - -case "$all,$also" in -t,) - if test ! -f "$THIS_INDEX" - then - die 'nothing to commit (use "git add file1 file2" to include for commit)' - fi - save_index && - ( - cd_to_toplevel && - GIT_INDEX_FILE="$NEXT_INDEX" && - export GIT_INDEX_FILE && - git diff-files --name-only -z | - git update-index --remove -z --stdin - ) || exit - ;; -,t) - save_index && - git ls-files --error-unmatch -- "$@" >/dev/null || exit - - git diff-files --name-only -z -- "$@" | - ( - cd_to_toplevel && - GIT_INDEX_FILE="$NEXT_INDEX" && - export GIT_INDEX_FILE && - git update-index --remove -z --stdin - ) || exit - ;; -,) - if test "$interactive" = t; then - git add --interactive || exit - fi - case "$#" in - 0) - ;; # commit as-is - *) - if test -f "$GIT_DIR/MERGE_HEAD" - then - refuse_partial "Cannot do a partial commit during a merge." - fi - - TMP_INDEX="$GIT_DIR/tmp-index$$" - W= - test -z "$initial_commit" && W=--with-tree=HEAD - commit_only=$(git ls-files --error-unmatch $W -- "$@") || exit - - # Build a temporary index and update the real index - # the same way. - if test -z "$initial_commit" - then - GIT_INDEX_FILE="$THIS_INDEX" \ - git read-tree --index-output="$TMP_INDEX" -i -m HEAD - else - rm -f "$TMP_INDEX" - fi || exit - - printf '%s\n' "$commit_only" | - GIT_INDEX_FILE="$TMP_INDEX" \ - git update-index --add --remove --stdin && - - save_index && - printf '%s\n' "$commit_only" | - ( - GIT_INDEX_FILE="$NEXT_INDEX" - export GIT_INDEX_FILE - git update-index --add --remove --stdin - ) || exit - ;; - esac - ;; -esac - -################################################################ -# If we do as-is commit, the index file will be THIS_INDEX, -# otherwise NEXT_INDEX after we make this commit. We leave -# the index as is if we abort. - -if test -f "$NEXT_INDEX" -then - USE_INDEX="$NEXT_INDEX" -else - USE_INDEX="$THIS_INDEX" -fi - -case "$status_only" in -t) - # This will silently fail in a read-only repository, which is - # what we want. - GIT_INDEX_FILE="$USE_INDEX" git update-index -q --unmerged --refresh - run_status - exit $? - ;; -'') - GIT_INDEX_FILE="$USE_INDEX" git update-index -q --refresh || exit - ;; -esac - -################################################################ -# Grab commit message, write out tree and make commit. - -if test t = "$verify" && test -x "$GIT_DIR"/hooks/pre-commit -then - GIT_INDEX_FILE="${TMP_INDEX:-${USE_INDEX}}" "$GIT_DIR"/hooks/pre-commit \ - || exit -fi - -if test "$log_message" != '' -then - printf '%s\n' "$log_message" -elif test "$logfile" != "" -then - if test "$logfile" = - - then - test -t 0 && - echo >&2 "(reading log message from standard input)" - cat - else - cat <"$logfile" - fi -elif test "$use_commit" != "" -then - encoding=$(git config i18n.commitencoding || echo UTF-8) - git show -s --pretty=raw --encoding="$encoding" "$use_commit" | - sed -e '1,/^$/d' -e 's/^ //' -elif test -f "$GIT_DIR/MERGE_MSG" -then - cat "$GIT_DIR/MERGE_MSG" -elif test -f "$GIT_DIR/SQUASH_MSG" -then - cat "$GIT_DIR/SQUASH_MSG" -elif test "$templatefile" != "" -then - cat "$templatefile" -fi | git stripspace >"$GIT_DIR"/COMMIT_EDITMSG - -case "$signoff" in -t) - sign=$(git var GIT_COMMITTER_IDENT | sed -e ' - s/>.*/>/ - s/^/Signed-off-by: / - ') - blank_before_signoff= - tail -n 1 "$GIT_DIR"/COMMIT_EDITMSG | - grep 'Signed-off-by:' >/dev/null || blank_before_signoff=' -' - tail -n 1 "$GIT_DIR"/COMMIT_EDITMSG | - grep "$sign"$ >/dev/null || - printf '%s%s\n' "$blank_before_signoff" "$sign" \ - >>"$GIT_DIR"/COMMIT_EDITMSG - ;; -esac - -if test -f "$GIT_DIR/MERGE_HEAD" && test -z "$no_edit"; then - echo "#" - echo "# It looks like you may be committing a MERGE." - echo "# If this is not correct, please remove the file" - printf '%s\n' "# $GIT_DIR/MERGE_HEAD" - echo "# and try again" - echo "#" -fi >>"$GIT_DIR"/COMMIT_EDITMSG - -# Author -if test '' != "$use_commit" -then - eval "$(get_author_ident_from_commit "$use_commit")" - export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE -fi -if test '' != "$force_author" -then - GIT_AUTHOR_NAME=$(expr "z$force_author" : 'z\(.*[^ ]\) *<.*') && - GIT_AUTHOR_EMAIL=$(expr "z$force_author" : '.*\(<.*\)') && - test '' != "$GIT_AUTHOR_NAME" && - test '' != "$GIT_AUTHOR_EMAIL" || - die "malformed --author parameter" - export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL -fi - -PARENTS="-p HEAD" -if test -z "$initial_commit" -then - rloga='commit' - if [ -f "$GIT_DIR/MERGE_HEAD" ]; then - rloga='commit (merge)' - PARENTS="-p HEAD "$(sed -e 's/^/-p /' "$GIT_DIR/MERGE_HEAD") - elif test -n "$amend"; then - rloga='commit (amend)' - PARENTS=$(git cat-file commit HEAD | - sed -n -e '/^$/q' -e 's/^parent /-p /p') - fi - current="$(git rev-parse --verify HEAD)" -else - if [ -z "$(git ls-files)" ]; then - echo >&2 'nothing to commit (use "git add file1 file2" to include for commit)' - exit 1 - fi - PARENTS="" - rloga='commit (initial)' - current='' -fi -set_reflog_action "$rloga" - -if test -z "$no_edit" -then - { - echo "" - echo "# Please enter the commit message for your changes." - echo "# (Comment lines starting with '#' will not be included)" - test -z "$only_include_assumed" || echo "$only_include_assumed" - run_status - } >>"$GIT_DIR"/COMMIT_EDITMSG -else - # we need to check if there is anything to commit - run_status >/dev/null -fi -case "$allow_empty,$?,$PARENTS" in -t,* | ?,0,* | ?,*,-p' '?*-p' '?*) - # an explicit --allow-empty, or a merge commit can record the - # same tree as its parent. Otherwise having commitable paths - # is required. - ;; -*) - rm -f "$GIT_DIR/COMMIT_EDITMSG" "$GIT_DIR/SQUASH_MSG" - use_status_color=t - run_status - exit 1 -esac - -case "$no_edit" in -'') - git var GIT_AUTHOR_IDENT > /dev/null || die - git var GIT_COMMITTER_IDENT > /dev/null || die - git_editor "$GIT_DIR/COMMIT_EDITMSG" - ;; -esac - -case "$verify" in -t) - if test -x "$GIT_DIR"/hooks/commit-msg - then - "$GIT_DIR"/hooks/commit-msg "$GIT_DIR"/COMMIT_EDITMSG || exit - fi -esac - -if test -z "$no_edit" -then - sed -e ' - /^diff --git a\/.*/{ - s/// - q - } - /^#/d - ' "$GIT_DIR"/COMMIT_EDITMSG -else - cat "$GIT_DIR"/COMMIT_EDITMSG -fi | -git stripspace >"$GIT_DIR"/COMMIT_MSG - -# Test whether the commit message has any content we didn't supply. -have_commitmsg= -grep -v -i '^Signed-off-by' "$GIT_DIR"/COMMIT_MSG | - git stripspace > "$GIT_DIR"/COMMIT_BAREMSG - -# Is the commit message totally empty? -if test -s "$GIT_DIR"/COMMIT_BAREMSG -then - if test "$templatefile" != "" - then - # Test whether this is just the unaltered template. - if cnt=$(sed -e '/^#/d' < "$templatefile" | - git stripspace | - diff "$GIT_DIR"/COMMIT_BAREMSG - | - wc -l) && - test 0 -lt $cnt - then - have_commitmsg=t - fi - else - # No template, so the content in the commit message must - # have come from the user. - have_commitmsg=t - fi -fi - -rm -f "$GIT_DIR"/COMMIT_BAREMSG - -if test "$have_commitmsg" = "t" -then - if test -z "$TMP_INDEX" - then - tree=$(GIT_INDEX_FILE="$USE_INDEX" git write-tree) - else - tree=$(GIT_INDEX_FILE="$TMP_INDEX" git write-tree) && - rm -f "$TMP_INDEX" - fi && - commit=$(git commit-tree $tree $PARENTS <"$GIT_DIR/COMMIT_MSG") && - rlogm=$(sed -e 1q "$GIT_DIR"/COMMIT_MSG) && - git update-ref -m "$GIT_REFLOG_ACTION: $rlogm" HEAD $commit "$current" && - rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" && - if test -f "$NEXT_INDEX" - then - mv "$NEXT_INDEX" "$THIS_INDEX" - else - : ;# happy - fi -else - echo >&2 "* no commit message? aborting commit." - false -fi -ret="$?" -rm -f "$GIT_DIR/COMMIT_MSG" "$GIT_DIR/COMMIT_EDITMSG" "$GIT_DIR/SQUASH_MSG" - -cd_to_toplevel - -git rerere - -if test "$ret" = 0 -then - git gc --auto - if test -x "$GIT_DIR"/hooks/post-commit - then - "$GIT_DIR"/hooks/post-commit - fi - if test -z "$quiet" - then - commit=$(git diff-tree --always --shortstat --pretty="format:%h: %s"\ - --abbrev --summary --root HEAD --) - echo "Created${initial_commit:+ initial} commit $commit" - fi -fi - -exit "$ret" diff --git a/contrib/examples/git-difftool.perl b/contrib/examples/git-difftool.perl deleted file mode 100755 index df59bdfe97..0000000000 --- a/contrib/examples/git-difftool.perl +++ /dev/null @@ -1,481 +0,0 @@ -#!/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 deleted file mode 100755 index 57d2e5616f..0000000000 --- a/contrib/examples/git-fetch.sh +++ /dev/null @@ -1,379 +0,0 @@ -#!/bin/sh -# - -USAGE='<fetch-options> <repository> <refspec>...' -SUBDIRECTORY_OK=Yes -. git-sh-setup -set_reflog_action "fetch $*" -cd_to_toplevel ;# probably unnecessary... - -. git-parse-remote -_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]' -_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" - -LF=' -' -IFS="$LF" - -no_tags= -tags= -append= -force= -verbose= -update_head_ok= -exec= -keep= -shallow_depth= -no_progress= -test -t 1 || no_progress=--no-progress -quiet= -while test $# != 0 -do - case "$1" in - -a|--a|--ap|--app|--appe|--appen|--append) - append=t - ;; - --upl|--uplo|--uploa|--upload|--upload-|--upload-p|\ - --upload-pa|--upload-pac|--upload-pack) - shift - exec="--upload-pack=$1" - ;; - --upl=*|--uplo=*|--uploa=*|--upload=*|\ - --upload-=*|--upload-p=*|--upload-pa=*|--upload-pac=*|--upload-pack=*) - exec=--upload-pack=$(expr "z$1" : 'z-[^=]*=\(.*\)') - shift - ;; - -f|--f|--fo|--for|--forc|--force) - force=t - ;; - -t|--t|--ta|--tag|--tags) - tags=t - ;; - -n|--n|--no|--no-|--no-t|--no-ta|--no-tag|--no-tags) - no_tags=t - ;; - -u|--u|--up|--upd|--upda|--updat|--update|--update-|--update-h|\ - --update-he|--update-hea|--update-head|--update-head-|\ - --update-head-o|--update-head-ok) - update_head_ok=t - ;; - -q|--q|--qu|--qui|--quie|--quiet) - quiet=--quiet - ;; - -v|--verbose) - verbose="$verbose"Yes - ;; - -k|--k|--ke|--kee|--keep) - keep='-k -k' - ;; - --depth=*) - shallow_depth="--depth=$(expr "z$1" : 'z-[^=]*=\(.*\)')" - ;; - --depth) - shift - shallow_depth="--depth=$1" - ;; - -*) - usage - ;; - *) - break - ;; - esac - shift -done - -case "$#" in -0) - origin=$(get_default_remote) - test -n "$(get_remote_url ${origin})" || - die "Where do you want to fetch from today?" - set x $origin ; shift ;; -esac - -if test -z "$exec" -then - # No command line override and we have configuration for the remote. - exec="--upload-pack=$(get_uploadpack $1)" -fi - -remote_nick="$1" -remote=$(get_remote_url "$@") -refs= -rref= -rsync_slurped_objects= - -if test "" = "$append" -then - : >"$GIT_DIR/FETCH_HEAD" -fi - -# Global that is reused later -ls_remote_result=$(git ls-remote $exec "$remote") || - die "Cannot get the repository state from $remote" - -append_fetch_head () { - flags= - test -n "$verbose" && flags="$flags$LF-v" - test -n "$force$single_force" && flags="$flags$LF-f" - GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION" \ - git fetch--tool $flags append-fetch-head "$@" -} - -# updating the current HEAD with git-fetch in a bare -# repository is always fine. -if test -z "$update_head_ok" && test $(is_bare_repository) = false -then - orig_head=$(git rev-parse --verify HEAD 2>/dev/null) -fi - -# Allow --tags/--notags from remote.$1.tagopt -case "$tags$no_tags" in -'') - case "$(git config --get "remote.$1.tagopt")" in - --tags) - tags=t ;; - --no-tags) - no_tags=t ;; - esac -esac - -# If --tags (and later --heads or --all) is specified, then we are -# not talking about defaults stored in Pull: line of remotes or -# branches file, and just fetch those and refspecs explicitly given. -# Otherwise we do what we always did. - -reflist=$(get_remote_refs_for_fetch "$@") -if test "$tags" -then - taglist=$(IFS=' ' && - echo "$ls_remote_result" | - git show-ref --exclude-existing=refs/tags/ | - while read sha1 name - do - echo ".${name}:${name}" - done) || exit - if test "$#" -gt 1 - then - # remote URL plus explicit refspecs; we need to merge them. - reflist="$reflist$LF$taglist" - else - # No explicit refspecs; fetch tags only. - reflist=$taglist - fi -fi - -fetch_all_at_once () { - - eval=$(echo "$1" | git fetch--tool parse-reflist "-") - eval "$eval" - - ( : subshell because we muck with IFS - IFS=" $LF" - ( - if test "$remote" = . ; then - git show-ref $rref || echo failed "$remote" - elif test -f "$remote" ; then - test -n "$shallow_depth" && - die "shallow clone with bundle is not supported" - git bundle unbundle "$remote" $rref || - echo failed "$remote" - else - if test -d "$remote" && - - # The remote might be our alternate. With - # this optimization we will bypass fetch-pack - # altogether, which means we cannot be doing - # the shallow stuff at all. - test ! -f "$GIT_DIR/shallow" && - test -z "$shallow_depth" && - - # See if all of what we are going to fetch are - # connected to our repository's tips, in which - # case we do not have to do any fetch. - theirs=$(echo "$ls_remote_result" | \ - git fetch--tool -s pick-rref "$rref" "-") && - - # This will barf when $theirs reach an object that - # we do not have in our repository. Otherwise, - # we already have everything the fetch would bring in. - git rev-list --objects $theirs --not --all \ - >/dev/null 2>/dev/null - then - echo "$ls_remote_result" | \ - git fetch--tool pick-rref "$rref" "-" - else - flags= - case $verbose in - YesYes*) - flags="-v" - ;; - esac - git-fetch-pack --thin $exec $keep $shallow_depth \ - $quiet $no_progress $flags "$remote" $rref || - echo failed "$remote" - fi - fi - ) | - ( - flags= - test -n "$verbose" && flags="$flags -v" - test -n "$force" && flags="$flags -f" - GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION" \ - git fetch--tool $flags native-store \ - "$remote" "$remote_nick" "$refs" - ) - ) || exit - -} - -fetch_per_ref () { - reflist="$1" - refs= - rref= - - for ref in $reflist - do - refs="$refs$LF$ref" - - # These are relative path from $GIT_DIR, typically starting at refs/ - # but may be HEAD - if expr "z$ref" : 'z\.' >/dev/null - then - not_for_merge=t - ref=$(expr "z$ref" : 'z\.\(.*\)') - else - not_for_merge= - fi - if expr "z$ref" : 'z+' >/dev/null - then - single_force=t - ref=$(expr "z$ref" : 'z+\(.*\)') - else - single_force= - fi - remote_name=$(expr "z$ref" : 'z\([^:]*\):') - local_name=$(expr "z$ref" : 'z[^:]*:\(.*\)') - - rref="$rref$LF$remote_name" - - # There are transports that can fetch only one head at a time... - case "$remote" in - http://* | https://* | ftp://*) - test -n "$shallow_depth" && - die "shallow clone with http not supported" - proto=$(expr "$remote" : '\([^:]*\):') - if [ -n "$GIT_SSL_NO_VERIFY" ]; then - curl_extra_args="-k" - fi - if [ -n "$GIT_CURL_FTP_NO_EPSV" -o \ - "$(git config --bool http.noEPSV)" = true ]; then - noepsv_opt="--disable-epsv" - fi - - # Find $remote_name from ls-remote output. - head=$(echo "$ls_remote_result" | \ - git fetch--tool -s pick-rref "$remote_name" "-") - expr "z$head" : "z$_x40\$" >/dev/null || - die "No such ref $remote_name at $remote" - echo >&2 "Fetching $remote_name from $remote using $proto" - case "$quiet" in '') v=-v ;; *) v= ;; esac - git-http-fetch $v -a "$head" "$remote" || exit - ;; - rsync://*) - test -n "$shallow_depth" && - die "shallow clone with rsync not supported" - TMP_HEAD="$GIT_DIR/TMP_HEAD" - rsync -L -q "$remote/$remote_name" "$TMP_HEAD" || exit 1 - head=$(git rev-parse --verify TMP_HEAD) - rm -f "$TMP_HEAD" - case "$quiet" in '') v=-v ;; *) v= ;; esac - test "$rsync_slurped_objects" || { - rsync -a $v --ignore-existing --exclude info \ - "$remote/objects/" "$GIT_OBJECT_DIRECTORY/" || exit - - # Look at objects/info/alternates for rsync -- http will - # support it natively and git native ones will do it on - # the remote end. Not having that file is not a crime. - rsync -q "$remote/objects/info/alternates" \ - "$GIT_DIR/TMP_ALT" 2>/dev/null || - rm -f "$GIT_DIR/TMP_ALT" - if test -f "$GIT_DIR/TMP_ALT" - then - resolve_alternates "$remote" <"$GIT_DIR/TMP_ALT" | - while read alt - do - case "$alt" in 'bad alternate: '*) die "$alt";; esac - echo >&2 "Getting alternate: $alt" - rsync -av --ignore-existing --exclude info \ - "$alt" "$GIT_OBJECT_DIRECTORY/" || exit - done - rm -f "$GIT_DIR/TMP_ALT" - fi - rsync_slurped_objects=t - } - ;; - esac - - append_fetch_head "$head" "$remote" \ - "$remote_name" "$remote_nick" "$local_name" "$not_for_merge" || exit - - done - -} - -fetch_main () { - case "$remote" in - http://* | https://* | ftp://* | rsync://* ) - fetch_per_ref "$@" - ;; - *) - fetch_all_at_once "$@" - ;; - esac -} - -fetch_main "$reflist" || exit - -# automated tag following -case "$no_tags$tags" in -'') - case "$reflist" in - *:refs/*) - # effective only when we are following remote branch - # using local tracking branch. - taglist=$(IFS=' ' && - echo "$ls_remote_result" | - git show-ref --exclude-existing=refs/tags/ | - while read sha1 name - do - git cat-file -t "$sha1" >/dev/null 2>&1 || continue - echo >&2 "Auto-following $name" - echo ".${name}:${name}" - done) - esac - case "$taglist" in - '') ;; - ?*) - # do not deepen a shallow tree when following tags - shallow_depth= - fetch_main "$taglist" || exit ;; - esac -esac - -# If the original head was empty (i.e. no "master" yet), or -# if we were told not to worry, we do not have to check. -case "$orig_head" in -'') - ;; -?*) - curr_head=$(git rev-parse --verify HEAD 2>/dev/null) - if test "$curr_head" != "$orig_head" - then - git update-ref \ - -m "$GIT_REFLOG_ACTION: Undoing incorrectly fetched HEAD." \ - HEAD "$orig_head" - die "Cannot fetch into the current branch." - fi - ;; -esac diff --git a/contrib/examples/git-gc.sh b/contrib/examples/git-gc.sh deleted file mode 100755 index 1597e9f33f..0000000000 --- a/contrib/examples/git-gc.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2006, Shawn O. Pearce -# -# Cleanup unreachable files and optimize the repository. - -USAGE='[--prune]' -SUBDIRECTORY_OK=Yes -. git-sh-setup - -no_prune=: -while test $# != 0 -do - case "$1" in - --prune) - no_prune= - ;; - --) - usage - ;; - esac - shift -done - -case "$(git config --get gc.packrefs)" in -notbare|"") - test $(is_bare_repository) = true || pack_refs=true;; -*) - pack_refs=$(git config --bool --get gc.packrefs) -esac - -test "true" != "$pack_refs" || -git pack-refs --prune && -git reflog expire --all && -git-repack -a -d -l && -$no_prune git prune && -git rerere gc || exit diff --git a/contrib/examples/git-log.sh b/contrib/examples/git-log.sh deleted file mode 100755 index c2ea71cf14..0000000000 --- a/contrib/examples/git-log.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2005 Linus Torvalds -# - -USAGE='[--max-count=<n>] [<since>..<limit>] [--pretty=<format>] [git-rev-list options]' -SUBDIRECTORY_OK='Yes' -. git-sh-setup - -revs=$(git-rev-parse --revs-only --no-flags --default HEAD "$@") || exit -[ "$revs" ] || { - die "No HEAD ref" -} -git-rev-list --pretty $(git-rev-parse --default HEAD "$@") | -LESS=-S ${PAGER:-less} diff --git a/contrib/examples/git-ls-remote.sh b/contrib/examples/git-ls-remote.sh deleted file mode 100755 index 2aa89a7df8..0000000000 --- a/contrib/examples/git-ls-remote.sh +++ /dev/null @@ -1,142 +0,0 @@ -#!/bin/sh -# - -usage () { - echo >&2 "usage: $0 [--heads] [--tags] [-u|--upload-pack <upload-pack>]" - echo >&2 " <repository> <refs>..." - exit 1; -} - -die () { - echo >&2 "$*" - exit 1 -} - -exec= -while test $# != 0 -do - case "$1" in - -h|--h|--he|--hea|--head|--heads) - heads=heads; shift ;; - -t|--t|--ta|--tag|--tags) - tags=tags; shift ;; - -u|--u|--up|--upl|--uploa|--upload|--upload-|--upload-p|--upload-pa|\ - --upload-pac|--upload-pack) - shift - exec="--upload-pack=$1" - shift;; - -u=*|--u=*|--up=*|--upl=*|--uplo=*|--uploa=*|--upload=*|\ - --upload-=*|--upload-p=*|--upload-pa=*|--upload-pac=*|--upload-pack=*) - exec=--upload-pack=$(expr "z$1" : 'z-[^=]*=\(.*\)') - shift;; - --) - shift; break ;; - -*) - usage ;; - *) - break ;; - esac -done - -case "$#" in 0) usage ;; esac - -case ",$heads,$tags," in -,,,) heads=heads tags=tags other=other ;; -esac - -. git-parse-remote -peek_repo="$(get_remote_url "$@")" -shift - -tmp=.ls-remote-$$ -trap "rm -fr $tmp-*" 0 1 2 3 15 -tmpdir=$tmp-d - -case "$peek_repo" in -http://* | https://* | ftp://* ) - if [ -n "$GIT_SSL_NO_VERIFY" -o \ - "$(git config --bool http.sslVerify)" = false ]; then - curl_extra_args="-k" - fi - if [ -n "$GIT_CURL_FTP_NO_EPSV" -o \ - "$(git config --bool http.noEPSV)" = true ]; then - curl_extra_args="${curl_extra_args} --disable-epsv" - fi - curl -nsf $curl_extra_args --header "Pragma: no-cache" "$peek_repo/info/refs" || - echo "failed slurping" - ;; - -rsync://* ) - mkdir $tmpdir && - rsync -rlq "$peek_repo/HEAD" $tmpdir && - rsync -rq "$peek_repo/refs" $tmpdir || { - echo "failed slurping" - exit - } - head=$(cat "$tmpdir/HEAD") && - case "$head" in - ref:' '*) - head=$(expr "z$head" : 'zref: \(.*\)') && - head=$(cat "$tmpdir/$head") || exit - esac && - echo "$head HEAD" - (cd $tmpdir && find refs -type f) | - while read path - do - tr -d '\012' <"$tmpdir/$path" - echo " $path" - done && - rm -fr $tmpdir - ;; - -* ) - if test -f "$peek_repo" ; then - git bundle list-heads "$peek_repo" || - echo "failed slurping" - else - git-peek-remote $exec "$peek_repo" || - echo "failed slurping" - fi - ;; -esac | -sort -t ' ' -k 2 | -while read sha1 path -do - case "$sha1" in - failed) - exit 1 ;; - esac - case "$path" in - refs/heads/*) - group=heads ;; - refs/tags/*) - group=tags ;; - *) - group=other ;; - esac - case ",$heads,$tags,$other," in - *,$group,*) - ;; - *) - continue;; - esac - case "$#" in - 0) - match=yes ;; - *) - match=no - for pat - do - case "/$path" in - */$pat ) - match=yes - break ;; - esac - done - esac - case "$match" in - no) - continue ;; - esac - echo "$sha1 $path" -done diff --git a/contrib/examples/git-merge-ours.sh b/contrib/examples/git-merge-ours.sh deleted file mode 100755 index 29dba4ba3a..0000000000 --- a/contrib/examples/git-merge-ours.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2005 Junio C Hamano -# -# Pretend we resolved the heads, but declare our tree trumps everybody else. -# - -# We need to exit with 2 if the index does not match our HEAD tree, -# because the current index is what we will be committing as the -# merge result. - -git diff-index --quiet --cached HEAD -- || exit 2 - -exit 0 diff --git a/contrib/examples/git-merge.sh b/contrib/examples/git-merge.sh deleted file mode 100755 index 932e78dbfe..0000000000 --- a/contrib/examples/git-merge.sh +++ /dev/null @@ -1,620 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2005 Junio C Hamano -# - -OPTIONS_KEEPDASHDASH= -OPTIONS_SPEC="\ -git merge [options] <remote>... -git merge [options] <msg> HEAD <remote> --- -stat show a diffstat at the end of the merge -n don't show a diffstat at the end of the merge -summary (synonym to --stat) -log add list of one-line log to merge commit message -squash create a single commit instead of doing a merge -commit perform a commit if the merge succeeds (default) -ff allow fast-forward (default) -ff-only abort if fast-forward is not possible -rerere-autoupdate update index with any reused conflict resolution -s,strategy= merge strategy to use -X= option for selected merge strategy -m,message= message to be used for the merge commit (if any) -" - -SUBDIRECTORY_OK=Yes -. git-sh-setup -require_work_tree -cd_to_toplevel - -test -z "$(git ls-files -u)" || - die "Merge is not possible because you have unmerged files." - -! test -e "$GIT_DIR/MERGE_HEAD" || - die 'You have not concluded your merge (MERGE_HEAD exists).' - -LF=' -' - -all_strategies='recur recursive octopus resolve stupid ours subtree' -all_strategies="$all_strategies recursive-ours recursive-theirs" -not_strategies='base file index tree' -default_twohead_strategies='recursive' -default_octopus_strategies='octopus' -no_fast_forward_strategies='subtree ours' -no_trivial_strategies='recursive recur subtree ours recursive-ours recursive-theirs' -use_strategies= -xopt= - -allow_fast_forward=t -fast_forward_only= -allow_trivial_merge=t -squash= no_commit= log_arg= rr_arg= - -dropsave() { - rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" \ - "$GIT_DIR/MERGE_STASH" "$GIT_DIR/MERGE_MODE" || exit 1 -} - -savestate() { - # Stash away any local modifications. - git stash create >"$GIT_DIR/MERGE_STASH" -} - -restorestate() { - if test -f "$GIT_DIR/MERGE_STASH" - then - git reset --hard $head >/dev/null - git stash apply $(cat "$GIT_DIR/MERGE_STASH") - git update-index --refresh >/dev/null - fi -} - -finish_up_to_date () { - case "$squash" in - t) - echo "$1 (nothing to squash)" ;; - '') - echo "$1" ;; - esac - dropsave -} - -squash_message () { - echo Squashed commit of the following: - echo - git log --no-merges --pretty=medium ^"$head" $remoteheads -} - -finish () { - if test '' = "$2" - then - rlogm="$GIT_REFLOG_ACTION" - else - echo "$2" - rlogm="$GIT_REFLOG_ACTION: $2" - fi - case "$squash" in - t) - echo "Squash commit -- not updating HEAD" - squash_message >"$GIT_DIR/SQUASH_MSG" - ;; - '') - case "$merge_msg" in - '') - echo "No merge message -- not updating HEAD" - ;; - *) - git update-ref -m "$rlogm" HEAD "$1" "$head" || exit 1 - git gc --auto - ;; - esac - ;; - esac - case "$1" in - '') - ;; - ?*) - if test "$show_diffstat" = t - then - # We want color (if set), but no pager - GIT_PAGER='' git diff --stat --summary -M "$head" "$1" - fi - ;; - esac - - # Run a post-merge hook - if test -x "$GIT_DIR"/hooks/post-merge - then - case "$squash" in - t) - "$GIT_DIR"/hooks/post-merge 1 - ;; - '') - "$GIT_DIR"/hooks/post-merge 0 - ;; - esac - fi -} - -merge_name () { - remote="$1" - rh=$(git rev-parse --verify "$remote^0" 2>/dev/null) || return - if truname=$(expr "$remote" : '\(.*\)~[0-9]*$') && - git show-ref -q --verify "refs/heads/$truname" 2>/dev/null - then - echo "$rh branch '$truname' (early part) of ." - return - fi - if found_ref=$(git rev-parse --symbolic-full-name --verify \ - "$remote" 2>/dev/null) - then - expanded=$(git check-ref-format --branch "$remote") || - exit - if test "${found_ref#refs/heads/}" != "$found_ref" - then - echo "$rh branch '$expanded' of ." - return - elif test "${found_ref#refs/remotes/}" != "$found_ref" - then - echo "$rh remote branch '$expanded' of ." - return - fi - fi - if test "$remote" = "FETCH_HEAD" && test -r "$GIT_DIR/FETCH_HEAD" - then - sed -e 's/ not-for-merge / /' -e 1q \ - "$GIT_DIR/FETCH_HEAD" - return - fi - echo "$rh commit '$remote'" -} - -parse_config () { - while test $# != 0; do - case "$1" in - -n|--no-stat|--no-summary) - show_diffstat=false ;; - --stat|--summary) - show_diffstat=t ;; - --log|--no-log) - log_arg=$1 ;; - --squash) - test "$allow_fast_forward" = t || - die "You cannot combine --squash with --no-ff." - squash=t no_commit=t ;; - --no-squash) - squash= no_commit= ;; - --commit) - no_commit= ;; - --no-commit) - no_commit=t ;; - --ff) - allow_fast_forward=t ;; - --no-ff) - test "$squash" != t || - die "You cannot combine --squash with --no-ff." - test "$fast_forward_only" != t || - die "You cannot combine --ff-only with --no-ff." - allow_fast_forward=f ;; - --ff-only) - test "$allow_fast_forward" != f || - die "You cannot combine --ff-only with --no-ff." - fast_forward_only=t ;; - --rerere-autoupdate|--no-rerere-autoupdate) - rr_arg=$1 ;; - -s|--strategy) - shift - case " $all_strategies " in - *" $1 "*) - use_strategies="$use_strategies$1 " - ;; - *) - case " $not_strategies " in - *" $1 "*) - false - esac && - type "git-merge-$1" >/dev/null 2>&1 || - die "available strategies are: $all_strategies" - use_strategies="$use_strategies$1 " - ;; - esac - ;; - -X) - shift - xopt="${xopt:+$xopt }$(git rev-parse --sq-quote "--$1")" - ;; - -m|--message) - shift - merge_msg="$1" - have_message=t - ;; - --) - shift - break ;; - *) usage ;; - esac - shift - done - args_left=$# -} - -test $# != 0 || usage - -have_message= - -if branch=$(git-symbolic-ref -q HEAD) -then - mergeopts=$(git config "branch.${branch#refs/heads/}.mergeoptions") - if test -n "$mergeopts" - then - parse_config $mergeopts -- - fi -fi - -parse_config "$@" -while test $args_left -lt $#; do shift; done - -if test -z "$show_diffstat"; then - test "$(git config --bool merge.diffstat)" = false && show_diffstat=false - test "$(git config --bool merge.stat)" = false && show_diffstat=false - test -z "$show_diffstat" && show_diffstat=t -fi - -# This could be traditional "merge <msg> HEAD <commit>..." and the -# way we can tell it is to see if the second token is HEAD, but some -# people might have misused the interface and used a commit-ish that -# is the same as HEAD there instead. Traditional format never would -# have "-m" so it is an additional safety measure to check for it. - -if test -z "$have_message" && - second_token=$(git rev-parse --verify "$2^0" 2>/dev/null) && - head_commit=$(git rev-parse --verify "HEAD" 2>/dev/null) && - test "$second_token" = "$head_commit" -then - merge_msg="$1" - shift - head_arg="$1" - shift -elif ! git rev-parse --verify HEAD >/dev/null 2>&1 -then - # If the merged head is a valid one there is no reason to - # forbid "git merge" into a branch yet to be born. We do - # the same for "git pull". - if test 1 -ne $# - then - echo >&2 "Can merge only exactly one commit into empty head" - exit 1 - fi - - test "$squash" != t || - die "Squash commit into empty head not supported yet" - test "$allow_fast_forward" = t || - die "Non-fast-forward into an empty head does not make sense" - rh=$(git rev-parse --verify "$1^0") || - die "$1 - not something we can merge" - - git update-ref -m "initial pull" HEAD "$rh" "" && - git read-tree --reset -u HEAD - exit - -else - # We are invoked directly as the first-class UI. - head_arg=HEAD - - # All the rest are the commits being merged; prepare - # the standard merge summary message to be appended to - # the given message. If remote is invalid we will die - # later in the common codepath so we discard the error - # in this loop. - merge_msg="$( - for remote - do - merge_name "$remote" - done | - if test "$have_message" = t - then - git fmt-merge-msg -m "$merge_msg" $log_arg - else - git fmt-merge-msg $log_arg - fi - )" -fi -head=$(git rev-parse --verify "$head_arg"^0) || usage - -# All the rest are remote heads -test "$#" = 0 && usage ;# we need at least one remote head. -set_reflog_action "merge $*" - -remoteheads= -for remote -do - remotehead=$(git rev-parse --verify "$remote"^0 2>/dev/null) || - die "$remote - not something we can merge" - remoteheads="${remoteheads}$remotehead " - eval GITHEAD_$remotehead='"$remote"' - export GITHEAD_$remotehead -done -set x $remoteheads ; shift - -case "$use_strategies" in -'') - case "$#" in - 1) - var="$(git config --get pull.twohead)" - if test -n "$var" - then - use_strategies="$var" - else - use_strategies="$default_twohead_strategies" - fi ;; - *) - var="$(git config --get pull.octopus)" - if test -n "$var" - then - use_strategies="$var" - else - use_strategies="$default_octopus_strategies" - fi ;; - esac - ;; -esac - -for s in $use_strategies -do - for ss in $no_fast_forward_strategies - do - case " $s " in - *" $ss "*) - allow_fast_forward=f - break - ;; - esac - done - for ss in $no_trivial_strategies - do - case " $s " in - *" $ss "*) - allow_trivial_merge=f - break - ;; - esac - done -done - -case "$#" in -1) - common=$(git merge-base --all $head "$@") - ;; -*) - common=$(git merge-base --all --octopus $head "$@") - ;; -esac -echo "$head" >"$GIT_DIR/ORIG_HEAD" - -case "$allow_fast_forward,$#,$common,$no_commit" in -?,*,'',*) - # No common ancestors found. We need a real merge. - ;; -?,1,"$1",*) - # If head can reach all the merge then we are up to date. - # but first the most common case of merging one remote. - finish_up_to_date "Already up to date." - exit 0 - ;; -t,1,"$head",*) - # Again the most common case of merging one remote. - echo "Updating $(git rev-parse --short $head)..$(git rev-parse --short $1)" - git update-index --refresh 2>/dev/null - msg="Fast-forward" - if test -n "$have_message" - then - msg="$msg (no commit created; -m option ignored)" - fi - new_head=$(git rev-parse --verify "$1^0") && - git read-tree -v -m -u --exclude-per-directory=.gitignore $head "$new_head" && - finish "$new_head" "$msg" || exit - dropsave - exit 0 - ;; -?,1,?*"$LF"?*,*) - # We are not doing octopus and not fast-forward. Need a - # real merge. - ;; -?,1,*,) - # We are not doing octopus, not fast-forward, and have only - # one common. - git update-index --refresh 2>/dev/null - case "$allow_trivial_merge,$fast_forward_only" in - t,) - # See if it is really trivial. - git var GIT_COMMITTER_IDENT >/dev/null || exit - echo "Trying really trivial in-index merge..." - if git read-tree --trivial -m -u -v $common $head "$1" && - result_tree=$(git write-tree) - then - echo "Wonderful." - result_commit=$( - printf '%s\n' "$merge_msg" | - git commit-tree $result_tree -p HEAD -p "$1" - ) || exit - finish "$result_commit" "In-index merge" - dropsave - exit 0 - fi - echo "Nope." - esac - ;; -*) - # An octopus. If we can reach all the remote we are up to date. - up_to_date=t - for remote - do - common_one=$(git merge-base --all $head $remote) - if test "$common_one" != "$remote" - then - up_to_date=f - break - fi - done - if test "$up_to_date" = t - then - finish_up_to_date "Already up to date. Yeeah!" - exit 0 - fi - ;; -esac - -if test "$fast_forward_only" = t -then - die "Not possible to fast-forward, aborting." -fi - -# We are going to make a new commit. -git var GIT_COMMITTER_IDENT >/dev/null || exit - -# At this point, we need a real merge. No matter what strategy -# we use, it would operate on the index, possibly affecting the -# working tree, and when resolved cleanly, have the desired tree -# in the index -- this means that the index must be in sync with -# the $head commit. The strategies are responsible to ensure this. - -case "$use_strategies" in -?*' '?*) - # Stash away the local changes so that we can try more than one. - savestate - single_strategy=no - ;; -*) - rm -f "$GIT_DIR/MERGE_STASH" - single_strategy=yes - ;; -esac - -result_tree= best_cnt=-1 best_strategy= wt_strategy= -merge_was_ok= -for strategy in $use_strategies -do - test "$wt_strategy" = '' || { - echo "Rewinding the tree to pristine..." - restorestate - } - case "$single_strategy" in - no) - echo "Trying merge strategy $strategy..." - ;; - esac - - # Remember which strategy left the state in the working tree - wt_strategy=$strategy - - eval 'git-merge-$strategy '"$xopt"' $common -- "$head_arg" "$@"' - exit=$? - if test "$no_commit" = t && test "$exit" = 0 - then - merge_was_ok=t - exit=1 ;# pretend it left conflicts. - fi - - test "$exit" = 0 || { - - # The backend exits with 1 when conflicts are left to be resolved, - # with 2 when it does not handle the given merge at all. - - if test "$exit" -eq 1 - then - cnt=$({ - git diff-files --name-only - git ls-files --unmerged - } | wc -l) - if test $best_cnt -le 0 || test $cnt -le $best_cnt - then - best_strategy=$strategy - best_cnt=$cnt - fi - fi - continue - } - - # Automerge succeeded. - result_tree=$(git write-tree) && break -done - -# If we have a resulting tree, that means the strategy module -# auto resolved the merge cleanly. -if test '' != "$result_tree" -then - if test "$allow_fast_forward" = "t" - then - parents=$(git merge-base --independent "$head" "$@") - else - parents=$(git rev-parse "$head" "$@") - fi - parents=$(echo "$parents" | sed -e 's/^/-p /') - result_commit=$(printf '%s\n' "$merge_msg" | git commit-tree $result_tree $parents) || exit - finish "$result_commit" "Merge made by $wt_strategy." - dropsave - exit 0 -fi - -# Pick the result from the best strategy and have the user fix it up. -case "$best_strategy" in -'') - restorestate - case "$use_strategies" in - ?*' '?*) - echo >&2 "No merge strategy handled the merge." - ;; - *) - echo >&2 "Merge with strategy $use_strategies failed." - ;; - esac - exit 2 - ;; -"$wt_strategy") - # We already have its result in the working tree. - ;; -*) - echo "Rewinding the tree to pristine..." - restorestate - echo "Using the $best_strategy to prepare resolving by hand." - git-merge-$best_strategy $common -- "$head_arg" "$@" - ;; -esac - -if test "$squash" = t -then - finish -else - for remote - do - echo $remote - done >"$GIT_DIR/MERGE_HEAD" - printf '%s\n' "$merge_msg" >"$GIT_DIR/MERGE_MSG" || - die "Could not write to $GIT_DIR/MERGE_MSG" - if test "$allow_fast_forward" != t - then - printf "%s" no-ff - else - : - fi >"$GIT_DIR/MERGE_MODE" || - die "Could not write to $GIT_DIR/MERGE_MODE" -fi - -if test "$merge_was_ok" = t -then - echo >&2 \ - "Automatic merge went well; stopped before committing as requested" - exit 0 -else - { - echo ' -Conflicts: -' - git ls-files --unmerged | - sed -e 's/^[^ ]* / /' | - uniq - } >>"$GIT_DIR/MERGE_MSG" - git rerere $rr_arg - die "Automatic merge failed; fix conflicts and then commit the result." -fi diff --git a/contrib/examples/git-notes.sh b/contrib/examples/git-notes.sh deleted file mode 100755 index e642e47d9f..0000000000 --- a/contrib/examples/git-notes.sh +++ /dev/null @@ -1,121 +0,0 @@ -#!/bin/sh - -USAGE="(edit [-F <file> | -m <msg>] | show) [commit]" -. git-sh-setup - -test -z "$1" && usage -ACTION="$1"; shift - -test -z "$GIT_NOTES_REF" && GIT_NOTES_REF="$(git config core.notesref)" -test -z "$GIT_NOTES_REF" && GIT_NOTES_REF="refs/notes/commits" - -MESSAGE= -while test $# != 0 -do - case "$1" in - -m) - test "$ACTION" = "edit" || usage - shift - if test "$#" = "0"; then - die "error: option -m needs an argument" - else - if [ -z "$MESSAGE" ]; then - MESSAGE="$1" - else - MESSAGE="$MESSAGE - -$1" - fi - shift - fi - ;; - -F) - test "$ACTION" = "edit" || usage - shift - if test "$#" = "0"; then - die "error: option -F needs an argument" - else - if [ -z "$MESSAGE" ]; then - MESSAGE="$(cat "$1")" - else - MESSAGE="$MESSAGE - -$(cat "$1")" - fi - shift - fi - ;; - -*) - usage - ;; - *) - break - ;; - esac -done - -COMMIT=$(git rev-parse --verify --default HEAD "$@") || -die "Invalid commit: $@" - -case "$ACTION" in -edit) - if [ "${GIT_NOTES_REF#refs/notes/}" = "$GIT_NOTES_REF" ]; then - die "Refusing to edit notes in $GIT_NOTES_REF (outside of refs/notes/)" - fi - - MSG_FILE="$GIT_DIR/new-notes-$COMMIT" - GIT_INDEX_FILE="$MSG_FILE.idx" - export GIT_INDEX_FILE - - trap ' - test -f "$MSG_FILE" && rm "$MSG_FILE" - test -f "$GIT_INDEX_FILE" && rm "$GIT_INDEX_FILE" - ' 0 - - CURRENT_HEAD=$(git show-ref "$GIT_NOTES_REF" | cut -f 1 -d ' ') - if [ -z "$CURRENT_HEAD" ]; then - PARENT= - else - PARENT="-p $CURRENT_HEAD" - git read-tree "$GIT_NOTES_REF" || die "Could not read index" - fi - - if [ -z "$MESSAGE" ]; then - GIT_NOTES_REF= git log -1 $COMMIT | sed "s/^/#/" > "$MSG_FILE" - if [ ! -z "$CURRENT_HEAD" ]; then - git cat-file blob :$COMMIT >> "$MSG_FILE" 2> /dev/null - fi - core_editor="$(git config core.editor)" - ${GIT_EDITOR:-${core_editor:-${VISUAL:-${EDITOR:-vi}}}} "$MSG_FILE" - else - echo "$MESSAGE" > "$MSG_FILE" - fi - - grep -v ^# < "$MSG_FILE" | git stripspace > "$MSG_FILE".processed - mv "$MSG_FILE".processed "$MSG_FILE" - if [ -s "$MSG_FILE" ]; then - BLOB=$(git hash-object -w "$MSG_FILE") || - die "Could not write into object database" - git update-index --add --cacheinfo 0644 $BLOB $COMMIT || - die "Could not write index" - else - test -z "$CURRENT_HEAD" && - die "Will not initialise with empty tree" - git update-index --force-remove $COMMIT || - die "Could not update index" - fi - - TREE=$(git write-tree) || die "Could not write tree" - NEW_HEAD=$(echo Annotate $COMMIT | git commit-tree $TREE $PARENT) || - die "Could not annotate" - git update-ref -m "Annotate $COMMIT" \ - "$GIT_NOTES_REF" $NEW_HEAD $CURRENT_HEAD -;; -show) - git rev-parse -q --verify "$GIT_NOTES_REF":$COMMIT > /dev/null || - die "No note for commit $COMMIT." - git show "$GIT_NOTES_REF":$COMMIT -;; -*) - usage -esac diff --git a/contrib/examples/git-pull.sh b/contrib/examples/git-pull.sh deleted file mode 100755 index 6b3a03f9b0..0000000000 --- a/contrib/examples/git-pull.sh +++ /dev/null @@ -1,381 +0,0 @@ -#!/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-remote.perl b/contrib/examples/git-remote.perl deleted file mode 100755 index d42df7b418..0000000000 --- a/contrib/examples/git-remote.perl +++ /dev/null @@ -1,474 +0,0 @@ -#!/usr/bin/perl -w - -use strict; -use Git; -my $git = Git->repository(); - -sub add_remote_config { - my ($hash, $name, $what, $value) = @_; - if ($what eq 'url') { - # Having more than one is Ok -- it is used for push. - if (! exists $hash->{'URL'}) { - $hash->{$name}{'URL'} = $value; - } - } - elsif ($what eq 'fetch') { - $hash->{$name}{'FETCH'} ||= []; - push @{$hash->{$name}{'FETCH'}}, $value; - } - elsif ($what eq 'push') { - $hash->{$name}{'PUSH'} ||= []; - push @{$hash->{$name}{'PUSH'}}, $value; - } - if (!exists $hash->{$name}{'SOURCE'}) { - $hash->{$name}{'SOURCE'} = 'config'; - } -} - -sub add_remote_remotes { - my ($hash, $file, $name) = @_; - - if (exists $hash->{$name}) { - $hash->{$name}{'WARNING'} = 'ignored due to config'; - return; - } - - my $fh; - if (!open($fh, '<', $file)) { - print STDERR "Warning: cannot open $file\n"; - return; - } - my $it = { 'SOURCE' => 'remotes' }; - $hash->{$name} = $it; - while (<$fh>) { - chomp; - if (/^URL:\s*(.*)$/) { - # Having more than one is Ok -- it is used for push. - if (! exists $it->{'URL'}) { - $it->{'URL'} = $1; - } - } - elsif (/^Push:\s*(.*)$/) { - $it->{'PUSH'} ||= []; - push @{$it->{'PUSH'}}, $1; - } - elsif (/^Pull:\s*(.*)$/) { - $it->{'FETCH'} ||= []; - push @{$it->{'FETCH'}}, $1; - } - elsif (/^\#/) { - ; # ignore - } - else { - print STDERR "Warning: funny line in $file: $_\n"; - } - } - close($fh); -} - -sub list_remote { - my ($git) = @_; - my %seen = (); - my @remotes = eval { - $git->command(qw(config --get-regexp), '^remote\.'); - }; - for (@remotes) { - if (/^remote\.(\S+?)\.([^.\s]+)\s+(.*)$/) { - add_remote_config(\%seen, $1, $2, $3); - } - } - - my $dir = $git->repo_path() . "/remotes"; - if (opendir(my $dh, $dir)) { - local $_; - while ($_ = readdir($dh)) { - chomp; - next if (! -f "$dir/$_" || ! -r _); - add_remote_remotes(\%seen, "$dir/$_", $_); - } - } - - return \%seen; -} - -sub add_branch_config { - my ($hash, $name, $what, $value) = @_; - if ($what eq 'remote') { - if (exists $hash->{$name}{'REMOTE'}) { - print STDERR "Warning: more than one branch.$name.remote\n"; - } - $hash->{$name}{'REMOTE'} = $value; - } - elsif ($what eq 'merge') { - $hash->{$name}{'MERGE'} ||= []; - push @{$hash->{$name}{'MERGE'}}, $value; - } -} - -sub list_branch { - my ($git) = @_; - my %seen = (); - my @branches = eval { - $git->command(qw(config --get-regexp), '^branch\.'); - }; - for (@branches) { - if (/^branch\.([^.]*)\.(\S*)\s+(.*)$/) { - add_branch_config(\%seen, $1, $2, $3); - } - } - - return \%seen; -} - -my $remote = list_remote($git); -my $branch = list_branch($git); - -sub update_ls_remote { - my ($harder, $info) = @_; - - return if (($harder == 0) || - (($harder == 1) && exists $info->{'LS_REMOTE'})); - - my @ref = map { s|refs/heads/||; $_; } keys %{$git->remote_refs($info->{'URL'}, [ 'heads' ])}; - $info->{'LS_REMOTE'} = \@ref; -} - -sub list_wildcard_mapping { - my ($forced, $ours, $ls) = @_; - my %refs; - for (@$ls) { - $refs{$_} = 01; # bit #0 to say "they have" - } - for ($git->command('for-each-ref', "refs/remotes/$ours")) { - chomp; - next unless (s|^[0-9a-f]{40}\s[a-z]+\srefs/remotes/$ours/||); - next if ($_ eq 'HEAD'); - $refs{$_} ||= 0; - $refs{$_} |= 02; # bit #1 to say "we have" - } - my (@new, @stale, @tracked); - for (sort keys %refs) { - my $have = $refs{$_}; - if ($have == 1) { - push @new, $_; - } - elsif ($have == 2) { - push @stale, $_; - } - elsif ($have == 3) { - push @tracked, $_; - } - } - return \@new, \@stale, \@tracked; -} - -sub list_mapping { - my ($name, $info) = @_; - my $fetch = $info->{'FETCH'}; - my $ls = $info->{'LS_REMOTE'}; - my (@new, @stale, @tracked); - - for (@$fetch) { - next unless (/(\+)?([^:]+):(.*)/); - my ($forced, $theirs, $ours) = ($1, $2, $3); - if ($theirs eq 'refs/heads/*' && - $ours =~ /^refs\/remotes\/(.*)\/\*$/) { - # wildcard mapping - my ($w_new, $w_stale, $w_tracked) - = list_wildcard_mapping($forced, $1, $ls); - push @new, @$w_new; - push @stale, @$w_stale; - push @tracked, @$w_tracked; - } - elsif ($theirs =~ /\*/ || $ours =~ /\*/) { - print STDERR "Warning: unrecognized mapping in remotes.$name.fetch: $_\n"; - } - elsif ($theirs =~ s|^refs/heads/||) { - if (!grep { $_ eq $theirs } @$ls) { - push @stale, $theirs; - } - elsif ($ours ne '') { - push @tracked, $theirs; - } - } - } - return \@new, \@stale, \@tracked; -} - -sub show_mapping { - my ($name, $info) = @_; - my ($new, $stale, $tracked) = list_mapping($name, $info); - if (@$new) { - print " New remote branches (next fetch will store in remotes/$name)\n"; - print " @$new\n"; - } - if (@$stale) { - print " Stale tracking branches in remotes/$name (use 'git remote prune')\n"; - print " @$stale\n"; - } - if (@$tracked) { - print " Tracked remote branches\n"; - print " @$tracked\n"; - } -} - -sub prune_remote { - my ($name, $ls_remote) = @_; - if (!exists $remote->{$name}) { - print STDERR "No such remote $name\n"; - return 1; - } - my $info = $remote->{$name}; - update_ls_remote($ls_remote, $info); - - my ($new, $stale, $tracked) = list_mapping($name, $info); - my $prefix = "refs/remotes/$name"; - foreach my $to_prune (@$stale) { - my @v = $git->command(qw(rev-parse --verify), "$prefix/$to_prune"); - $git->command(qw(update-ref -d), "$prefix/$to_prune", $v[0]); - } - return 0; -} - -sub show_remote { - my ($name, $ls_remote) = @_; - if (!exists $remote->{$name}) { - print STDERR "No such remote $name\n"; - return 1; - } - my $info = $remote->{$name}; - update_ls_remote($ls_remote, $info); - - print "* remote $name\n"; - print " URL: $info->{'URL'}\n"; - for my $branchname (sort keys %$branch) { - next unless (defined $branch->{$branchname}{'REMOTE'} && - $branch->{$branchname}{'REMOTE'} eq $name); - my @merged = map { - s|^refs/heads/||; - $_; - } split(' ',"@{$branch->{$branchname}{'MERGE'}}"); - next unless (@merged); - print " Remote branch(es) merged with 'git pull' while on branch $branchname\n"; - print " @merged\n"; - } - if ($info->{'LS_REMOTE'}) { - show_mapping($name, $info); - } - if ($info->{'PUSH'}) { - my @pushed = map { - s|^refs/heads/||; - s|^\+refs/heads/|+|; - s|:refs/heads/|:|; - $_; - } @{$info->{'PUSH'}}; - print " Local branch(es) pushed with 'git push'\n"; - print " @pushed\n"; - } - return 0; -} - -sub add_remote { - my ($name, $url, $opts) = @_; - if (exists $remote->{$name}) { - print STDERR "remote $name already exists.\n"; - exit(1); - } - $git->command('config', "remote.$name.url", $url); - my $track = $opts->{'track'} || ["*"]; - - for (@$track) { - $git->command('config', '--add', "remote.$name.fetch", - $opts->{'mirror'} ? - "+refs/$_:refs/$_" : - "+refs/heads/$_:refs/remotes/$name/$_"); - } - if ($opts->{'fetch'}) { - $git->command('fetch', $name); - } - if (exists $opts->{'master'}) { - $git->command('symbolic-ref', "refs/remotes/$name/HEAD", - "refs/remotes/$name/$opts->{'master'}"); - } -} - -sub update_remote { - my ($name) = @_; - my @remotes; - - my $conf = $git->config("remotes." . $name); - if (defined($conf)) { - @remotes = split(' ', $conf); - } elsif ($name eq 'default') { - @remotes = (); - for (sort keys %$remote) { - my $do_fetch = $git->config_bool("remote." . $_ . - ".skipDefaultUpdate"); - unless ($do_fetch) { - push @remotes, $_; - } - } - } else { - print STDERR "Remote group $name does not exist.\n"; - exit(1); - } - for (@remotes) { - print "Updating $_\n"; - $git->command('fetch', "$_"); - } -} - -sub rm_remote { - my ($name) = @_; - if (!exists $remote->{$name}) { - print STDERR "No such remote $name\n"; - return 1; - } - - $git->command('config', '--remove-section', "remote.$name"); - - eval { - my @trackers = $git->command('config', '--get-regexp', - 'branch.*.remote', $name); - for (@trackers) { - /^branch\.(.*)?\.remote/; - $git->config('--unset', "branch.$1.remote"); - $git->config('--unset', "branch.$1.merge"); - } - }; - - my @refs = $git->command('for-each-ref', - '--format=%(refname) %(objectname)', "refs/remotes/$name"); - for (@refs) { - my ($ref, $object) = split; - $git->command(qw(update-ref -d), $ref, $object); - } - return 0; -} - -sub add_usage { - print STDERR "usage: git remote add [-f] [-t track]* [-m master] <name> <url>\n"; - exit(1); -} - -my $VERBOSE = 0; -@ARGV = grep { - if ($_ eq '-v' or $_ eq '--verbose') { - $VERBOSE=1; - 0 - } else { - 1 - } -} @ARGV; - -if (!@ARGV) { - for (sort keys %$remote) { - print "$_"; - print "\t$remote->{$_}->{URL}" if $VERBOSE; - print "\n"; - } -} -elsif ($ARGV[0] eq 'show') { - my $ls_remote = 1; - my $i; - for ($i = 1; $i < @ARGV; $i++) { - if ($ARGV[$i] eq '-n') { - $ls_remote = 0; - } - else { - last; - } - } - if ($i >= @ARGV) { - print STDERR "usage: git remote show <remote>\n"; - exit(1); - } - my $status = 0; - for (; $i < @ARGV; $i++) { - $status |= show_remote($ARGV[$i], $ls_remote); - } - exit($status); -} -elsif ($ARGV[0] eq 'update') { - if (@ARGV <= 1) { - update_remote("default"); - exit(1); - } - for (my $i = 1; $i < @ARGV; $i++) { - update_remote($ARGV[$i]); - } -} -elsif ($ARGV[0] eq 'prune') { - my $ls_remote = 1; - my $i; - for ($i = 1; $i < @ARGV; $i++) { - if ($ARGV[$i] eq '-n') { - $ls_remote = 0; - } - else { - last; - } - } - if ($i >= @ARGV) { - print STDERR "usage: git remote prune <remote>\n"; - exit(1); - } - my $status = 0; - for (; $i < @ARGV; $i++) { - $status |= prune_remote($ARGV[$i], $ls_remote); - } - exit($status); -} -elsif ($ARGV[0] eq 'add') { - my %opts = (); - while (1 < @ARGV && $ARGV[1] =~ /^-/) { - my $opt = $ARGV[1]; - shift @ARGV; - if ($opt eq '-f' || $opt eq '--fetch') { - $opts{'fetch'} = 1; - next; - } - if ($opt eq '-t' || $opt eq '--track') { - if (@ARGV < 1) { - add_usage(); - } - $opts{'track'} ||= []; - push @{$opts{'track'}}, $ARGV[1]; - shift @ARGV; - next; - } - if ($opt eq '-m' || $opt eq '--master') { - if ((@ARGV < 1) || exists $opts{'master'}) { - add_usage(); - } - $opts{'master'} = $ARGV[1]; - shift @ARGV; - next; - } - if ($opt eq '--mirror') { - $opts{'mirror'} = 1; - next; - } - add_usage(); - } - if (@ARGV != 3) { - add_usage(); - } - add_remote($ARGV[1], $ARGV[2], \%opts); -} -elsif ($ARGV[0] eq 'rm') { - if (@ARGV <= 1) { - print STDERR "usage: git remote rm <remote>\n"; - exit(1); - } - exit(rm_remote($ARGV[1])); -} -else { - print STDERR "usage: git remote\n"; - print STDERR " git remote add <name> <url>\n"; - print STDERR " git remote rm <name>\n"; - print STDERR " git remote show <name>\n"; - print STDERR " git remote prune <name>\n"; - print STDERR " git remote update [group]\n"; - exit(1); -} diff --git a/contrib/examples/git-repack.sh b/contrib/examples/git-repack.sh deleted file mode 100755 index 672af93443..0000000000 --- a/contrib/examples/git-repack.sh +++ /dev/null @@ -1,194 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2005 Linus Torvalds -# - -OPTIONS_KEEPDASHDASH= -OPTIONS_SPEC="\ -git repack [options] --- -a pack everything in a single pack -A same as -a, and turn unreachable objects loose -d remove redundant packs, and run git-prune-packed -f pass --no-reuse-delta to git-pack-objects -F pass --no-reuse-object to git-pack-objects -n do not run git-update-server-info -q,quiet be quiet -l pass --local to git-pack-objects -unpack-unreachable= with -A, do not loosen objects older than this - Packing constraints -window= size of the window used for delta compression -window-memory= same as the above, but limit memory size instead of entries count -depth= limits the maximum delta depth -max-pack-size= maximum size of each packfile -" -SUBDIRECTORY_OK='Yes' -. git-sh-setup - -no_update_info= all_into_one= remove_redundant= unpack_unreachable= -local= no_reuse= extra= -while test $# != 0 -do - case "$1" in - -n) no_update_info=t ;; - -a) all_into_one=t ;; - -A) all_into_one=t - unpack_unreachable=--unpack-unreachable ;; - --unpack-unreachable) - unpack_unreachable="--unpack-unreachable=$2"; shift ;; - -d) remove_redundant=t ;; - -q) GIT_QUIET=t ;; - -f) no_reuse=--no-reuse-delta ;; - -F) no_reuse=--no-reuse-object ;; - -l) local=--local ;; - --max-pack-size|--window|--window-memory|--depth) - extra="$extra $1=$2"; shift ;; - --) shift; break;; - *) usage ;; - esac - shift -done - -case "$(git config --bool repack.usedeltabaseoffset || echo true)" in -true) - extra="$extra --delta-base-offset" ;; -esac - -PACKDIR="$GIT_OBJECT_DIRECTORY/pack" -PACKTMP="$PACKDIR/.tmp-$$-pack" -rm -f "$PACKTMP"-* -trap 'rm -f "$PACKTMP"-*' 0 1 2 3 15 - -# There will be more repacking strategies to come... -case ",$all_into_one," in -,,) - args='--unpacked --incremental' - ;; -,t,) - args= existing= - if [ -d "$PACKDIR" ]; then - for e in $(cd "$PACKDIR" && find . -type f -name '*.pack' \ - | sed -e 's/^\.\///' -e 's/\.pack$//') - do - if [ -e "$PACKDIR/$e.keep" ]; then - : keep - else - existing="$existing $e" - fi - done - if test -n "$existing" && test -n "$unpack_unreachable" && \ - test -n "$remove_redundant" - then - # This may have arbitrary user arguments, so we - # have to protect it against whitespace splitting - # when it gets run as "pack-objects $args" later. - # Fortunately, we know it's an approxidate, so we - # can just use dots instead. - args="$args $(echo "$unpack_unreachable" | tr ' ' .)" - fi - fi - ;; -esac - -mkdir -p "$PACKDIR" || exit - -args="$args $local ${GIT_QUIET:+-q} $no_reuse$extra" -names=$(git pack-objects --keep-true-parents --honor-pack-keep --non-empty --all --reflog $args </dev/null "$PACKTMP") || - exit 1 -if [ -z "$names" ]; then - say Nothing new to pack. -fi - -# Ok we have prepared all new packfiles. - -# First see if there are packs of the same name and if so -# if we can move them out of the way (this can happen if we -# repacked immediately after packing fully. -rollback= -failed= -for name in $names -do - for sfx in pack idx - do - file=pack-$name.$sfx - test -f "$PACKDIR/$file" || continue - rm -f "$PACKDIR/old-$file" && - mv "$PACKDIR/$file" "$PACKDIR/old-$file" || { - failed=t - break - } - rollback="$rollback $file" - done - test -z "$failed" || break -done - -# If renaming failed for any of them, roll the ones we have -# already renamed back to their original names. -if test -n "$failed" -then - rollback_failure= - for file in $rollback - do - mv "$PACKDIR/old-$file" "$PACKDIR/$file" || - rollback_failure="$rollback_failure $file" - done - if test -n "$rollback_failure" - then - echo >&2 "WARNING: Some packs in use have been renamed by" - echo >&2 "WARNING: prefixing old- to their name, in order to" - echo >&2 "WARNING: replace them with the new version of the" - echo >&2 "WARNING: file. But the operation failed, and" - echo >&2 "WARNING: attempt to rename them back to their" - echo >&2 "WARNING: original names also failed." - echo >&2 "WARNING: Please rename them in $PACKDIR manually:" - for file in $rollback_failure - do - echo >&2 "WARNING: old-$file -> $file" - done - fi - exit 1 -fi - -# Now the ones with the same name are out of the way... -fullbases= -for name in $names -do - fullbases="$fullbases pack-$name" - chmod a-w "$PACKTMP-$name.pack" - chmod a-w "$PACKTMP-$name.idx" - mv -f "$PACKTMP-$name.pack" "$PACKDIR/pack-$name.pack" && - mv -f "$PACKTMP-$name.idx" "$PACKDIR/pack-$name.idx" || - exit -done - -# Remove the "old-" files -for name in $names -do - rm -f "$PACKDIR/old-pack-$name.idx" - rm -f "$PACKDIR/old-pack-$name.pack" -done - -# End of pack replacement. - -if test "$remove_redundant" = t -then - # We know $existing are all redundant. - if [ -n "$existing" ] - then - ( cd "$PACKDIR" && - for e in $existing - do - case " $fullbases " in - *" $e "*) ;; - *) rm -f "$e.pack" "$e.idx" "$e.keep" ;; - esac - done - ) - fi - git prune-packed ${GIT_QUIET:+-q} -fi - -case "$no_update_info" in -t) : ;; -*) git update-server-info ;; -esac diff --git a/contrib/examples/git-rerere.perl b/contrib/examples/git-rerere.perl deleted file mode 100755 index 4f692091e7..0000000000 --- a/contrib/examples/git-rerere.perl +++ /dev/null @@ -1,284 +0,0 @@ -#!/usr/bin/perl -# -# REuse REcorded REsolve. This tool records a conflicted automerge -# result and its hand resolution, and helps to resolve future -# automerge that results in the same conflict. -# -# To enable this feature, create a directory 'rr-cache' under your -# .git/ directory. - -use Digest; -use File::Path; -use File::Copy; - -my $git_dir = $::ENV{GIT_DIR} || ".git"; -my $rr_dir = "$git_dir/rr-cache"; -my $merge_rr = "$git_dir/rr-cache/MERGE_RR"; - -my %merge_rr = (); - -sub read_rr { - if (!-f $merge_rr) { - %merge_rr = (); - return; - } - my $in; - local $/ = "\0"; - open $in, "<$merge_rr" or die "$!: $merge_rr"; - while (<$in>) { - chomp; - my ($name, $path) = /^([0-9a-f]{40})\t(.*)$/s; - $merge_rr{$path} = $name; - } - close $in; -} - -sub write_rr { - my $out; - open $out, ">$merge_rr" or die "$!: $merge_rr"; - for my $path (sort keys %merge_rr) { - my $name = $merge_rr{$path}; - print $out "$name\t$path\0"; - } - close $out; -} - -sub compute_conflict_name { - my ($path) = @_; - my @side = (); - my $in; - open $in, "<$path" or die "$!: $path"; - - my $sha1 = Digest->new("SHA-1"); - my $hunk = 0; - while (<$in>) { - if (/^<<<<<<< .*/) { - $hunk++; - @side = ([], undef); - } - elsif (/^=======$/) { - $side[1] = []; - } - elsif (/^>>>>>>> .*/) { - my ($one, $two); - $one = join('', @{$side[0]}); - $two = join('', @{$side[1]}); - if ($two le $one) { - ($one, $two) = ($two, $one); - } - $sha1->add($one); - $sha1->add("\0"); - $sha1->add($two); - $sha1->add("\0"); - @side = (); - } - elsif (@side == 0) { - next; - } - elsif (defined $side[1]) { - push @{$side[1]}, $_; - } - else { - push @{$side[0]}, $_; - } - } - close $in; - return ($sha1->hexdigest, $hunk); -} - -sub record_preimage { - my ($path, $name) = @_; - my @side = (); - my ($in, $out); - open $in, "<$path" or die "$!: $path"; - open $out, ">$name" or die "$!: $name"; - - while (<$in>) { - if (/^<<<<<<< .*/) { - @side = ([], undef); - } - elsif (/^=======$/) { - $side[1] = []; - } - elsif (/^>>>>>>> .*/) { - my ($one, $two); - $one = join('', @{$side[0]}); - $two = join('', @{$side[1]}); - if ($two le $one) { - ($one, $two) = ($two, $one); - } - print $out "<<<<<<<\n"; - print $out $one; - print $out "=======\n"; - print $out $two; - print $out ">>>>>>>\n"; - @side = (); - } - elsif (@side == 0) { - print $out $_; - } - elsif (defined $side[1]) { - push @{$side[1]}, $_; - } - else { - push @{$side[0]}, $_; - } - } - close $out; - close $in; -} - -sub find_conflict { - my $in; - local $/ = "\0"; - my $pid = open($in, '-|'); - die "$!" unless defined $pid; - if (!$pid) { - exec(qw(git ls-files -z -u)) or die "$!: ls-files"; - } - my %path = (); - my @path = (); - while (<$in>) { - chomp; - my ($mode, $sha1, $stage, $path) = - /^([0-7]+) ([0-9a-f]{40}) ([123])\t(.*)$/s; - $path{$path} |= (1 << $stage); - } - close $in; - while (my ($path, $status) = each %path) { - if ($status == 14) { push @path, $path; } - } - return @path; -} - -sub merge { - my ($name, $path) = @_; - record_preimage($path, "$rr_dir/$name/thisimage"); - unless (system('git', 'merge-file', map { "$rr_dir/$name/${_}image" } - qw(this pre post))) { - my $in; - open $in, "<$rr_dir/$name/thisimage" or - die "$!: $name/thisimage"; - my $out; - open $out, ">$path" or die "$!: $path"; - while (<$in>) { print $out $_; } - close $in; - close $out; - return 1; - } - return 0; -} - -sub garbage_collect_rerere { - # We should allow specifying these from the command line and - # that is why the caller gives @ARGV to us, but I am lazy. - - my $cutoff_noresolve = 15; # two weeks - my $cutoff_resolve = 60; # two months - my @to_remove; - while (<$rr_dir/*/preimage>) { - my ($dir) = /^(.*)\/preimage$/; - my $cutoff = ((-f "$dir/postimage") - ? $cutoff_resolve - : $cutoff_noresolve); - my $age = -M "$_"; - if ($cutoff <= $age) { - push @to_remove, $dir; - } - } - if (@to_remove) { - rmtree(\@to_remove); - } -} - --d "$rr_dir" || exit(0); - -read_rr(); - -if (@ARGV) { - my $arg = shift @ARGV; - if ($arg eq 'clear') { - for my $path (keys %merge_rr) { - my $name = $merge_rr{$path}; - if (-d "$rr_dir/$name" && - ! -f "$rr_dir/$name/postimage") { - rmtree(["$rr_dir/$name"]); - } - } - unlink $merge_rr; - } - elsif ($arg eq 'status') { - for my $path (keys %merge_rr) { - print $path, "\n"; - } - } - elsif ($arg eq 'diff') { - for my $path (keys %merge_rr) { - my $name = $merge_rr{$path}; - system('diff', ((@ARGV == 0) ? ('-u') : @ARGV), - '-L', "a/$path", '-L', "b/$path", - "$rr_dir/$name/preimage", $path); - } - } - elsif ($arg eq 'gc') { - garbage_collect_rerere(@ARGV); - } - else { - die "$0 unknown command: $arg\n"; - } - exit 0; -} - -my %conflict = map { $_ => 1 } find_conflict(); - -# MERGE_RR records paths with conflicts immediately after merge -# failed. Some of the conflicted paths might have been hand resolved -# in the working tree since then, but the initial run would catch all -# and register their preimages. - -for my $path (keys %conflict) { - # This path has conflict. If it is not recorded yet, - # record the pre-image. - if (!exists $merge_rr{$path}) { - my ($name, $hunk) = compute_conflict_name($path); - next unless ($hunk); - $merge_rr{$path} = $name; - if (! -d "$rr_dir/$name") { - mkpath("$rr_dir/$name", 0, 0777); - print STDERR "Recorded preimage for '$path'\n"; - record_preimage($path, "$rr_dir/$name/preimage"); - } - } -} - -# Now some of the paths that had conflicts earlier might have been -# hand resolved. Others may be similar to a conflict already that -# was resolved before. - -for my $path (keys %merge_rr) { - my $name = $merge_rr{$path}; - - # We could resolve this automatically if we have images. - if (-f "$rr_dir/$name/preimage" && - -f "$rr_dir/$name/postimage") { - if (merge($name, $path)) { - print STDERR "Resolved '$path' using previous resolution.\n"; - # Then we do not have to worry about this path - # anymore. - delete $merge_rr{$path}; - next; - } - } - - # Let's see if we have resolved it. - (undef, my $hunk) = compute_conflict_name($path); - next if ($hunk); - - print STDERR "Recorded resolution for '$path'.\n"; - copy($path, "$rr_dir/$name/postimage"); - # And we do not have to worry about this path anymore. - delete $merge_rr{$path}; -} - -# Write out the rest. -write_rr(); diff --git a/contrib/examples/git-reset.sh b/contrib/examples/git-reset.sh deleted file mode 100755 index cb1bbf3b90..0000000000 --- a/contrib/examples/git-reset.sh +++ /dev/null @@ -1,106 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano -# -USAGE='[--mixed | --soft | --hard] [<commit-ish>] [ [--] <paths>...]' -SUBDIRECTORY_OK=Yes -. git-sh-setup -set_reflog_action "reset $*" -require_work_tree - -update= reset_type=--mixed -unset rev - -while test $# != 0 -do - case "$1" in - --mixed | --soft | --hard) - reset_type="$1" - ;; - --) - break - ;; - -*) - usage - ;; - *) - rev=$(git rev-parse --verify "$1") || exit - shift - break - ;; - esac - shift -done - -: ${rev=HEAD} -rev=$(git rev-parse --verify $rev^0) || exit - -# Skip -- in "git reset HEAD -- foo" and "git reset -- foo". -case "$1" in --) shift ;; esac - -# git reset --mixed tree [--] paths... can be used to -# load chosen paths from the tree into the index without -# affecting the working tree or HEAD. -if test $# != 0 -then - test "$reset_type" = "--mixed" || - die "Cannot do partial $reset_type reset." - - git diff-index --cached $rev -- "$@" | - sed -e 's/^:\([0-7][0-7]*\) [0-7][0-7]* \([0-9a-f][0-9a-f]*\) [0-9a-f][0-9a-f]* [A-Z] \(.*\)$/\1 \2 \3/' | - git update-index --add --remove --index-info || exit - git update-index --refresh - exit -fi - -cd_to_toplevel - -if test "$reset_type" = "--hard" -then - update=-u -fi - -# Soft reset does not touch the index file or the working tree -# at all, but requires them in a good order. Other resets reset -# the index file to the tree object we are switching to. -if test "$reset_type" = "--soft" -then - if test -f "$GIT_DIR/MERGE_HEAD" || - test "" != "$(git ls-files --unmerged)" - then - die "Cannot do a soft reset in the middle of a merge." - fi -else - git read-tree -v --reset $update "$rev" || exit -fi - -# Any resets update HEAD to the head being switched to. -if orig=$(git rev-parse --verify HEAD 2>/dev/null) -then - echo "$orig" >"$GIT_DIR/ORIG_HEAD" -else - rm -f "$GIT_DIR/ORIG_HEAD" -fi -git update-ref -m "$GIT_REFLOG_ACTION" HEAD "$rev" -update_ref_status=$? - -case "$reset_type" in ---hard ) - test $update_ref_status = 0 && { - printf "HEAD is now at " - GIT_PAGER= git log --max-count=1 --pretty=oneline \ - --abbrev-commit HEAD - } - ;; ---soft ) - ;; # Nothing else to do ---mixed ) - # Report what has not been updated. - git update-index --refresh - ;; -esac - -rm -f "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/rr-cache/MERGE_RR" \ - "$GIT_DIR/SQUASH_MSG" "$GIT_DIR/MERGE_MSG" - -exit $update_ref_status diff --git a/contrib/examples/git-resolve.sh b/contrib/examples/git-resolve.sh deleted file mode 100755 index 3099dc851a..0000000000 --- a/contrib/examples/git-resolve.sh +++ /dev/null @@ -1,112 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2005 Linus Torvalds -# -# Resolve two trees. -# - -echo 'WARNING: This command is DEPRECATED and will be removed very soon.' >&2 -echo 'WARNING: Please use git-merge or git-pull instead.' >&2 -sleep 2 - -USAGE='<head> <remote> <merge-message>' -. git-sh-setup - -dropheads() { - rm -f -- "$GIT_DIR/MERGE_HEAD" \ - "$GIT_DIR/LAST_MERGE" || exit 1 -} - -head=$(git rev-parse --verify "$1"^0) && -merge=$(git rev-parse --verify "$2"^0) && -merge_name="$2" && -merge_msg="$3" || usage - -# -# The remote name is just used for the message, -# but we do want it. -# -if [ -z "$head" -o -z "$merge" -o -z "$merge_msg" ]; then - usage -fi - -dropheads -echo $head > "$GIT_DIR"/ORIG_HEAD -echo $merge > "$GIT_DIR"/LAST_MERGE - -common=$(git merge-base $head $merge) -if [ -z "$common" ]; then - die "Unable to find common commit between" $merge $head -fi - -case "$common" in -"$merge") - echo "Already up to date. Yeeah!" - dropheads - exit 0 - ;; -"$head") - echo "Updating $(git rev-parse --short $head)..$(git rev-parse --short $merge)" - git read-tree -u -m $head $merge || exit 1 - git update-ref -m "resolve $merge_name: Fast-forward" \ - HEAD "$merge" "$head" - git diff-tree -p $head $merge | git apply --stat - dropheads - exit 0 - ;; -esac - -# We are going to make a new commit. -git var GIT_COMMITTER_IDENT >/dev/null || exit - -# Find an optimum merge base if there are more than one candidates. -LF=' -' -common=$(git merge-base -a $head $merge) -case "$common" in -?*"$LF"?*) - echo "Trying to find the optimum merge base." - G=.tmp-index$$ - best= - best_cnt=-1 - for c in $common - do - rm -f $G - GIT_INDEX_FILE=$G git read-tree -m $c $head $merge \ - 2>/dev/null || continue - # Count the paths that are unmerged. - cnt=$(GIT_INDEX_FILE=$G git ls-files --unmerged | wc -l) - if test $best_cnt -le 0 || test $cnt -le $best_cnt - then - best=$c - best_cnt=$cnt - if test "$best_cnt" -eq 0 - then - # Cannot do any better than all trivial merge. - break - fi - fi - done - rm -f $G - common="$best" -esac - -echo "Trying to merge $merge into $head using $common." -git update-index --refresh 2>/dev/null -git read-tree -u -m $common $head $merge || exit 1 -result_tree=$(git write-tree 2> /dev/null) -if [ $? -ne 0 ]; then - echo "Simple merge failed, trying Automatic merge" - git-merge-index -o git-merge-one-file -a - if [ $? -ne 0 ]; then - echo $merge > "$GIT_DIR"/MERGE_HEAD - die "Automatic merge failed, fix up by hand" - fi - result_tree=$(git write-tree) || exit 1 -fi -result_commit=$(echo "$merge_msg" | git commit-tree $result_tree -p $head -p $merge) -echo "Committed merge $result_commit" -git update-ref -m "resolve $merge_name: In-index merge" \ - HEAD "$result_commit" "$head" -git diff-tree -p $head $result_commit | git apply --stat -dropheads diff --git a/contrib/examples/git-revert.sh b/contrib/examples/git-revert.sh deleted file mode 100755 index 197838d10b..0000000000 --- a/contrib/examples/git-revert.sh +++ /dev/null @@ -1,207 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2005 Linus Torvalds -# Copyright (c) 2005 Junio C Hamano -# - -case "$0" in -*-revert* ) - test -t 0 && edit=-e - replay= - me=revert - USAGE='[--edit | --no-edit] [-n] <commit-ish>' ;; -*-cherry-pick* ) - replay=t - edit= - me=cherry-pick - USAGE='[--edit] [-n] [-r] [-x] <commit-ish>' ;; -* ) - echo >&2 "What are you talking about?" - exit 1 ;; -esac - -SUBDIRECTORY_OK=Yes ;# we will cd up -. git-sh-setup -require_work_tree -cd_to_toplevel - -no_commit= -xopt= -while case "$#" in 0) break ;; esac -do - case "$1" in - -n|--n|--no|--no-|--no-c|--no-co|--no-com|--no-comm|\ - --no-commi|--no-commit) - no_commit=t - ;; - -e|--e|--ed|--edi|--edit) - edit=-e - ;; - --n|--no|--no-|--no-e|--no-ed|--no-edi|--no-edit) - edit= - ;; - -r) - : no-op ;; - -x|--i-really-want-to-expose-my-private-commit-object-name) - replay= - ;; - -X?*) - xopt="$xopt$(git rev-parse --sq-quote "--${1#-X}")" - ;; - --strategy-option=*) - xopt="$xopt$(git rev-parse --sq-quote "--${1#--strategy-option=}")" - ;; - -X|--strategy-option) - shift - xopt="$xopt$(git rev-parse --sq-quote "--$1")" - ;; - -*) - usage - ;; - *) - break - ;; - esac - shift -done - -set_reflog_action "$me" - -test "$me,$replay" = "revert,t" && usage - -case "$no_commit" in -t) - # We do not intend to commit immediately. We just want to - # merge the differences in. - head=$(git-write-tree) || - die "Your index file is unmerged." - ;; -*) - head=$(git-rev-parse --verify HEAD) || - die "You do not have a valid HEAD" - files=$(git-diff-index --cached --name-only $head) || exit - if [ "$files" ]; then - die "Dirty index: cannot $me (dirty: $files)" - fi - ;; -esac - -rev=$(git-rev-parse --verify "$@") && -commit=$(git-rev-parse --verify "$rev^0") || - die "Not a single commit $@" -prev=$(git-rev-parse --verify "$commit^1" 2>/dev/null) || - die "Cannot run $me a root commit" -git-rev-parse --verify "$commit^2" >/dev/null 2>&1 && - die "Cannot run $me a multi-parent commit." - -encoding=$(git config i18n.commitencoding || echo UTF-8) - -# "commit" is an existing commit. We would want to apply -# the difference it introduces since its first parent "prev" -# on top of the current HEAD if we are cherry-pick. Or the -# reverse of it if we are revert. - -case "$me" in -revert) - git show -s --pretty=oneline --encoding="$encoding" $commit | - sed -e ' - s/^[^ ]* /Revert "/ - s/$/"/ - ' - echo - echo "This reverts commit $commit." - test "$rev" = "$commit" || - echo "(original 'git revert' arguments: $@)" - base=$commit next=$prev - ;; - -cherry-pick) - pick_author_script=' - /^author /{ - s/'\''/'\''\\'\'\''/g - h - s/^author \([^<]*\) <[^>]*> .*$/\1/ - s/'\''/'\''\'\'\''/g - s/.*/GIT_AUTHOR_NAME='\''&'\''/p - - g - s/^author [^<]* <\([^>]*\)> .*$/\1/ - s/'\''/'\''\'\'\''/g - s/.*/GIT_AUTHOR_EMAIL='\''&'\''/p - - g - s/^author [^<]* <[^>]*> \(.*\)$/\1/ - s/'\''/'\''\'\'\''/g - s/.*/GIT_AUTHOR_DATE='\''&'\''/p - - q - }' - - logmsg=$(git show -s --pretty=raw --encoding="$encoding" "$commit") - 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 - export GIT_AUTHOR_DATE - - echo "$logmsg" | - sed -e '1,/^$/d' -e 's/^ //' - case "$replay" in - '') - echo "(cherry picked from commit $commit)" - test "$rev" = "$commit" || - echo "(original 'git cherry-pick' arguments: $@)" - ;; - esac - base=$prev next=$commit - ;; - -esac >.msg - -eval GITHEAD_$head=HEAD -eval GITHEAD_$next='$(git show -s \ - --pretty=oneline --encoding="$encoding" "$commit" | - sed -e "s/^[^ ]* //")' -export GITHEAD_$head GITHEAD_$next - -# This three way merge is an interesting one. We are at -# $head, and would want to apply the change between $commit -# and $prev on top of us (when reverting), or the change between -# $prev and $commit on top of us (when cherry-picking or replaying). - -eval "git merge-recursive $xopt $base -- $head $next" && -result=$(git-write-tree 2>/dev/null) || { - mv -f .msg "$GIT_DIR/MERGE_MSG" - { - echo ' -Conflicts: -' - git ls-files --unmerged | - sed -e 's/^[^ ]* / /' | - uniq - } >>"$GIT_DIR/MERGE_MSG" - echo >&2 "Automatic $me failed. After resolving the conflicts," - echo >&2 "mark the corrected paths with 'git-add <paths>'" - echo >&2 "and commit the result." - case "$me" in - cherry-pick) - echo >&2 "You may choose to use the following when making" - echo >&2 "the commit:" - echo >&2 "$set_author_env" - esac - exit 1 -} - -# If we are cherry-pick, and if the merge did not result in -# hand-editing, we will hit this commit and inherit the original -# author date and name. -# If we are revert, or if our cherry-pick results in a hand merge, -# we had better say that the current user is responsible for that. - -case "$no_commit" in -'') - git-commit -n -F .msg $edit - rm -f .msg - ;; -esac diff --git a/contrib/examples/git-svnimport.perl b/contrib/examples/git-svnimport.perl deleted file mode 100755 index c414f0d9c7..0000000000 --- a/contrib/examples/git-svnimport.perl +++ /dev/null @@ -1,976 +0,0 @@ -#!/usr/bin/perl - -# This tool is copyright (c) 2005, Matthias Urlichs. -# It is released under the Gnu Public License, version 2. -# -# The basic idea is to pull and analyze SVN changes. -# -# Checking out the files is done by a single long-running SVN connection. -# -# The head revision is on branch "origin" by default. -# You can change that with the '-o' option. - -use strict; -use warnings; -use Getopt::Std; -use File::Copy; -use File::Spec; -use File::Temp qw(tempfile); -use File::Path qw(mkpath); -use File::Basename qw(basename dirname); -use Time::Local; -use IO::Pipe; -use POSIX qw(strftime dup2); -use IPC::Open2; -use SVN::Core; -use SVN::Ra; - -die "Need SVN:Core 1.2.1 or better" if $SVN::Core::VERSION lt "1.2.1"; - -$SIG{'PIPE'}="IGNORE"; -$ENV{'TZ'}="UTC"; - -our($opt_h,$opt_o,$opt_v,$opt_u,$opt_C,$opt_i,$opt_m,$opt_M,$opt_t,$opt_T, - $opt_b,$opt_r,$opt_I,$opt_A,$opt_s,$opt_l,$opt_d,$opt_D,$opt_S,$opt_F, - $opt_P,$opt_R); - -sub usage() { - print STDERR <<END; -usage: ${\basename $0} # fetch/update GIT from SVN - [-o branch-for-HEAD] [-h] [-v] [-l max_rev] [-R repack_each_revs] - [-C GIT_repository] [-t tagname] [-T trunkname] [-b branchname] - [-d|-D] [-i] [-u] [-r] [-I ignorefilename] [-s start_chg] - [-m] [-M regex] [-A author_file] [-S] [-F] [-P project_name] [SVN_URL] -END - exit(1); -} - -getopts("A:b:C:dDFhiI:l:mM:o:rs:t:T:SP:R:uv") or usage(); -usage if $opt_h; - -my $tag_name = $opt_t || "tags"; -my $trunk_name = defined $opt_T ? $opt_T : "trunk"; -my $branch_name = $opt_b || "branches"; -my $project_name = $opt_P || ""; -$project_name = "/" . $project_name if ($project_name); -my $repack_after = $opt_R || 1000; -my $root_pool = SVN::Pool->new_default; - -@ARGV == 1 or @ARGV == 2 or usage(); - -$opt_o ||= "origin"; -$opt_s ||= 1; -my $git_tree = $opt_C; -$git_tree ||= "."; - -my $svn_url = $ARGV[0]; -my $svn_dir = $ARGV[1]; - -our @mergerx = (); -if ($opt_m) { - my $branch_esc = quotemeta ($branch_name); - my $trunk_esc = quotemeta ($trunk_name); - @mergerx = - ( - qr!\b(?:merg(?:ed?|ing))\b.*?\b((?:(?<=$branch_esc/)[\w\.\-]+)|(?:$trunk_esc))\b!i, - qr!\b(?:from|of)\W+((?:(?<=$branch_esc/)[\w\.\-]+)|(?:$trunk_esc))\b!i, - qr!\b(?:from|of)\W+(?:the )?([\w\.\-]+)[-\s]branch\b!i - ); -} -if ($opt_M) { - unshift (@mergerx, qr/$opt_M/); -} - -# Absolutize filename now, since we will have chdir'ed by the time we -# get around to opening it. -$opt_A = File::Spec->rel2abs($opt_A) if $opt_A; - -our %users = (); -our $users_file = undef; -sub read_users($) { - $users_file = File::Spec->rel2abs(@_); - die "Cannot open $users_file\n" unless -f $users_file; - open(my $authors,$users_file); - while(<$authors>) { - chomp; - next unless /^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*$/; - (my $user,my $name,my $email) = ($1,$2,$3); - $users{$user} = [$name,$email]; - } - close($authors); -} - -select(STDERR); $|=1; select(STDOUT); - - -package SVNconn; -# Basic SVN connection. -# We're only interested in connecting and downloading, so ... - -use File::Spec; -use File::Temp qw(tempfile); -use POSIX qw(strftime dup2); -use Fcntl qw(SEEK_SET); - -sub new { - my($what,$repo) = @_; - $what=ref($what) if ref($what); - - my $self = {}; - $self->{'buffer'} = ""; - bless($self,$what); - - $repo =~ s#/+$##; - $self->{'fullrep'} = $repo; - $self->conn(); - - return $self; -} - -sub conn { - my $self = shift; - my $repo = $self->{'fullrep'}; - my $auth = SVN::Core::auth_open ([SVN::Client::get_simple_provider, - SVN::Client::get_ssl_server_trust_file_provider, - SVN::Client::get_username_provider]); - my $s = SVN::Ra->new(url => $repo, auth => $auth, pool => $root_pool); - die "SVN connection to $repo: $!\n" unless defined $s; - $self->{'svn'} = $s; - $self->{'repo'} = $repo; - $self->{'maxrev'} = $s->get_latest_revnum(); -} - -sub file { - my($self,$path,$rev) = @_; - - my ($fh, $name) = tempfile('gitsvn.XXXXXX', - DIR => File::Spec->tmpdir(), UNLINK => 1); - - print "... $rev $path ...\n" if $opt_v; - my (undef, $properties); - $path =~ s#^/*##; - my $subpool = SVN::Pool::new_default_sub; - eval { (undef, $properties) - = $self->{'svn'}->get_file($path,$rev,$fh); }; - if($@) { - return undef if $@ =~ /Attempted to get checksum/; - die $@; - } - my $mode; - if (exists $properties->{'svn:executable'}) { - $mode = '100755'; - } elsif (exists $properties->{'svn:special'}) { - my ($special_content, $filesize); - $filesize = tell $fh; - seek $fh, 0, SEEK_SET; - read $fh, $special_content, $filesize; - if ($special_content =~ s/^link //) { - $mode = '120000'; - seek $fh, 0, SEEK_SET; - truncate $fh, 0; - print $fh $special_content; - } else { - die "unexpected svn:special file encountered"; - } - } else { - $mode = '100644'; - } - close ($fh); - - return ($name, $mode); -} - -sub ignore { - my($self,$path,$rev) = @_; - - print "... $rev $path ...\n" if $opt_v; - $path =~ s#^/*##; - my $subpool = SVN::Pool::new_default_sub; - my (undef,undef,$properties) - = $self->{'svn'}->get_dir($path,$rev,undef); - if (exists $properties->{'svn:ignore'}) { - my ($fh, $name) = tempfile('gitsvn.XXXXXX', - DIR => File::Spec->tmpdir(), - UNLINK => 1); - print $fh $properties->{'svn:ignore'}; - close($fh); - return $name; - } else { - return undef; - } -} - -sub dir_list { - my($self,$path,$rev) = @_; - $path =~ s#^/*##; - my $subpool = SVN::Pool::new_default_sub; - my ($dirents,undef,$properties) - = $self->{'svn'}->get_dir($path,$rev,undef); - return $dirents; -} - -package main; -use URI; - -our $svn = $svn_url; -$svn .= "/$svn_dir" if defined $svn_dir; -my $svn2 = SVNconn->new($svn); -$svn = SVNconn->new($svn); - -my $lwp_ua; -if($opt_d or $opt_D) { - $svn_url = URI->new($svn_url)->canonical; - if($opt_D) { - $svn_dir =~ s#/*$#/#; - } else { - $svn_dir = ""; - } - if ($svn_url->scheme eq "http") { - use LWP::UserAgent; - $lwp_ua = LWP::UserAgent->new(keep_alive => 1, requests_redirectable => []); - } else { - print STDERR "Warning: not HTTP; turning off direct file access\n"; - $opt_d=0; - } -} - -sub pdate($) { - my($d) = @_; - $d =~ m#(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)# - or die "Unparseable date: $d\n"; - my $y=$1; $y-=1900 if $y>1900; - return timegm($6||0,$5,$4,$3,$2-1,$y); -} - -sub getwd() { - my $pwd = `pwd`; - chomp $pwd; - return $pwd; -} - - -sub get_headref($$) { - my $name = shift; - my $git_dir = shift; - my $sha; - - if (open(C,"$git_dir/refs/heads/$name")) { - chomp($sha = <C>); - close(C); - length($sha) == 40 - or die "Cannot get head id for $name ($sha): $!\n"; - } - return $sha; -} - - --d $git_tree - or mkdir($git_tree,0777) - or die "Could not create $git_tree: $!"; -chdir($git_tree); - -my $orig_branch = ""; -my $forward_master = 0; -my %branches; - -my $git_dir = $ENV{"GIT_DIR"} || ".git"; -$git_dir = getwd()."/".$git_dir unless $git_dir =~ m#^/#; -$ENV{"GIT_DIR"} = $git_dir; -my $orig_git_index; -$orig_git_index = $ENV{GIT_INDEX_FILE} if exists $ENV{GIT_INDEX_FILE}; -my ($git_ih, $git_index) = tempfile('gitXXXXXX', SUFFIX => '.idx', - DIR => File::Spec->tmpdir()); -close ($git_ih); -$ENV{GIT_INDEX_FILE} = $git_index; -my $maxnum = 0; -my $last_rev = ""; -my $last_branch; -my $current_rev = $opt_s || 1; -unless(-d $git_dir) { - system("git init"); - die "Cannot init the GIT db at $git_tree: $?\n" if $?; - system("git read-tree --empty"); - die "Cannot init an empty tree: $?\n" if $?; - - $last_branch = $opt_o; - $orig_branch = ""; -} else { - -f "$git_dir/refs/heads/$opt_o" - or die "Branch '$opt_o' does not exist.\n". - "Either use the correct '-o branch' option,\n". - "or import to a new repository.\n"; - - -f "$git_dir/svn2git" - or die "'$git_dir/svn2git' does not exist.\n". - "You need that file for incremental imports.\n"; - open(F, "git symbolic-ref HEAD |") or - die "Cannot run git-symbolic-ref: $!\n"; - chomp ($last_branch = <F>); - $last_branch = basename($last_branch); - close(F); - unless($last_branch) { - warn "Cannot read the last branch name: $! -- assuming 'master'\n"; - $last_branch = "master"; - } - $orig_branch = $last_branch; - $last_rev = get_headref($orig_branch, $git_dir); - if (-f "$git_dir/SVN2GIT_HEAD") { - die <<EOM; -SVN2GIT_HEAD exists. -Make sure your working directory corresponds to HEAD and remove SVN2GIT_HEAD. -You may need to run - - git-read-tree -m -u SVN2GIT_HEAD HEAD -EOM - } - system('cp', "$git_dir/HEAD", "$git_dir/SVN2GIT_HEAD"); - - $forward_master = - $opt_o ne 'master' && -f "$git_dir/refs/heads/master" && - system('cmp', '-s', "$git_dir/refs/heads/master", - "$git_dir/refs/heads/$opt_o") == 0; - - # populate index - system('git', 'read-tree', $last_rev); - die "read-tree failed: $?\n" if $?; - - # Get the last import timestamps - open my $B,"<", "$git_dir/svn2git"; - while(<$B>) { - chomp; - my($num,$branch,$ref) = split; - $branches{$branch}{$num} = $ref; - $branches{$branch}{"LAST"} = $ref; - $current_rev = $num+1 if $current_rev <= $num; - } - close($B); -} --d $git_dir - or die "Could not create git subdir ($git_dir).\n"; - -my $default_authors = "$git_dir/svn-authors"; -if ($opt_A) { - read_users($opt_A); - copy($opt_A,$default_authors) or die "Copy failed: $!"; -} else { - read_users($default_authors) if -f $default_authors; -} - -open BRANCHES,">>", "$git_dir/svn2git"; - -sub node_kind($$) { - my ($svnpath, $revision) = @_; - $svnpath =~ s#^/*##; - my $subpool = SVN::Pool::new_default_sub; - my $kind = $svn->{'svn'}->check_path($svnpath,$revision); - return $kind; -} - -sub get_file($$$) { - my($svnpath,$rev,$path) = @_; - - # now get it - my ($name,$mode); - if($opt_d) { - my($req,$res); - - # /svn/!svn/bc/2/django/trunk/django-docs/build.py - my $url=$svn_url->clone(); - $url->path($url->path."/!svn/bc/$rev/$svn_dir$svnpath"); - print "... $path...\n" if $opt_v; - $req = HTTP::Request->new(GET => $url); - $res = $lwp_ua->request($req); - if ($res->is_success) { - my $fh; - ($fh, $name) = tempfile('gitsvn.XXXXXX', - DIR => File::Spec->tmpdir(), UNLINK => 1); - print $fh $res->content; - close($fh) or die "Could not write $name: $!\n"; - } else { - return undef if $res->code == 301; # directory? - die $res->status_line." at $url\n"; - } - $mode = '0644'; # can't obtain mode via direct http request? - } else { - ($name,$mode) = $svn->file("$svnpath",$rev); - return undef unless defined $name; - } - - my $pid = open(my $F, '-|'); - die $! unless defined $pid; - if (!$pid) { - exec("git", "hash-object", "-w", $name) - or die "Cannot create object: $!\n"; - } - my $sha = <$F>; - chomp $sha; - close $F; - unlink $name; - return [$mode, $sha, $path]; -} - -sub get_ignore($$$$$) { - my($new,$old,$rev,$path,$svnpath) = @_; - - return unless $opt_I; - my $name = $svn->ignore("$svnpath",$rev); - if ($path eq '/') { - $path = $opt_I; - } else { - $path = File::Spec->catfile($path,$opt_I); - } - if (defined $name) { - my $pid = open(my $F, '-|'); - die $! unless defined $pid; - if (!$pid) { - exec("git", "hash-object", "-w", $name) - or die "Cannot create object: $!\n"; - } - my $sha = <$F>; - chomp $sha; - close $F; - unlink $name; - push(@$new,['0644',$sha,$path]); - } elsif (defined $old) { - push(@$old,$path); - } -} - -sub project_path($$) -{ - my ($path, $project) = @_; - - $path = "/".$path unless ($path =~ m#^\/#) ; - return $1 if ($path =~ m#^$project\/(.*)$#); - - $path =~ s#\.#\\\.#g; - $path =~ s#\+#\\\+#g; - return "/" if ($project =~ m#^$path.*$#); - - return undef; -} - -sub split_path($$) { - my($rev,$path) = @_; - my $branch; - - if($path =~ s#^/\Q$tag_name\E/([^/]+)/?##) { - $branch = "/$1"; - } elsif($path =~ s#^/\Q$trunk_name\E/?##) { - $branch = "/"; - } elsif($path =~ s#^/\Q$branch_name\E/([^/]+)/?##) { - $branch = $1; - } else { - my %no_error = ( - "/" => 1, - "/$tag_name" => 1, - "/$branch_name" => 1 - ); - print STDERR "$rev: Unrecognized path: $path\n" unless (defined $no_error{$path}); - return () - } - if ($path eq "") { - $path = "/"; - } elsif ($project_name) { - $path = project_path($path, $project_name); - } - return ($branch,$path); -} - -sub branch_rev($$) { - - my ($srcbranch,$uptorev) = @_; - - my $bbranches = $branches{$srcbranch}; - my @revs = reverse sort { ($a eq 'LAST' ? 0 : $a) <=> ($b eq 'LAST' ? 0 : $b) } keys %$bbranches; - my $therev; - foreach my $arev(@revs) { - next if ($arev eq 'LAST'); - if ($arev <= $uptorev) { - $therev = $arev; - last; - } - } - return $therev; -} - -sub expand_svndir($$$); - -sub expand_svndir($$$) -{ - my ($svnpath, $rev, $path) = @_; - my @list; - get_ignore(\@list, undef, $rev, $path, $svnpath); - my $dirents = $svn->dir_list($svnpath, $rev); - foreach my $p(keys %$dirents) { - my $kind = node_kind($svnpath.'/'.$p, $rev); - if ($kind eq $SVN::Node::file) { - my $f = get_file($svnpath.'/'.$p, $rev, $path.'/'.$p); - push(@list, $f) if $f; - } elsif ($kind eq $SVN::Node::dir) { - push(@list, - expand_svndir($svnpath.'/'.$p, $rev, $path.'/'.$p)); - } - } - return @list; -} - -sub copy_path($$$$$$$$) { - # Somebody copied a whole subdirectory. - # We need to find the index entries from the old version which the - # SVN log entry points to, and add them to the new place. - - my($newrev,$newbranch,$path,$oldpath,$rev,$node_kind,$new,$parents) = @_; - - my($srcbranch,$srcpath) = split_path($rev,$oldpath); - unless(defined $srcbranch && defined $srcpath) { - print "Path not found when copying from $oldpath @ $rev.\n". - "Will try to copy from original SVN location...\n" - if $opt_v; - push (@$new, expand_svndir($oldpath, $rev, $path)); - return; - } - my $therev = branch_rev($srcbranch, $rev); - my $gitrev = $branches{$srcbranch}{$therev}; - unless($gitrev) { - print STDERR "$newrev:$newbranch: could not find $oldpath \@ $rev\n"; - return; - } - if ($srcbranch ne $newbranch) { - push(@$parents, $branches{$srcbranch}{'LAST'}); - } - print "$newrev:$newbranch:$path: copying from $srcbranch:$srcpath @ $rev\n" if $opt_v; - if ($node_kind eq $SVN::Node::dir) { - $srcpath =~ s#/*$#/#; - } - - my $pid = open my $f,'-|'; - die $! unless defined $pid; - if (!$pid) { - exec("git","ls-tree","-r","-z",$gitrev,$srcpath) - or die $!; - } - local $/ = "\0"; - while(<$f>) { - chomp; - my($m,$p) = split(/\t/,$_,2); - my($mode,$type,$sha1) = split(/ /,$m); - next if $type ne "blob"; - if ($node_kind eq $SVN::Node::dir) { - $p = $path . substr($p,length($srcpath)-1); - } else { - $p = $path; - } - push(@$new,[$mode,$sha1,$p]); - } - close($f) or - print STDERR "$newrev:$newbranch: could not list files in $oldpath \@ $rev\n"; -} - -sub commit { - my($branch, $changed_paths, $revision, $author, $date, $message) = @_; - my($committer_name,$committer_email,$dest); - my($author_name,$author_email); - my(@old,@new,@parents); - - if (not defined $author or $author eq "") { - $committer_name = $committer_email = "unknown"; - } elsif (defined $users_file) { - die "User $author is not listed in $users_file\n" - unless exists $users{$author}; - ($committer_name,$committer_email) = @{$users{$author}}; - } elsif ($author =~ /^(.*?)\s+<(.*)>$/) { - ($committer_name, $committer_email) = ($1, $2); - } else { - $author =~ s/^<(.*)>$/$1/; - $committer_name = $committer_email = $author; - } - - if ($opt_F && $message =~ /From:\s+(.*?)\s+<(.*)>\s*\n/) { - ($author_name, $author_email) = ($1, $2); - print "Author from From: $1 <$2>\n" if ($opt_v);; - } elsif ($opt_S && $message =~ /Signed-off-by:\s+(.*?)\s+<(.*)>\s*\n/) { - ($author_name, $author_email) = ($1, $2); - print "Author from Signed-off-by: $1 <$2>\n" if ($opt_v);; - } else { - $author_name = $committer_name; - $author_email = $committer_email; - } - - $date = pdate($date); - - my $tag; - my $parent; - if($branch eq "/") { # trunk - $parent = $opt_o; - } elsif($branch =~ m#^/(.+)#) { # tag - $tag = 1; - $parent = $1; - } else { # "normal" branch - # nothing to do - $parent = $branch; - } - $dest = $parent; - - my $prev = $changed_paths->{"/"}; - if($prev and $prev->[0] eq "A") { - delete $changed_paths->{"/"}; - my $oldpath = $prev->[1]; - my $rev; - if(defined $oldpath) { - my $p; - ($parent,$p) = split_path($revision,$oldpath); - if(defined $parent) { - if($parent eq "/") { - $parent = $opt_o; - } else { - $parent =~ s#^/##; # if it's a tag - } - } - } else { - $parent = undef; - } - } - - my $rev; - if($revision > $opt_s and defined $parent) { - open(H,'-|',"git","rev-parse","--verify",$parent); - $rev = <H>; - close(H) or do { - print STDERR "$revision: cannot find commit '$parent'!\n"; - return; - }; - chop $rev; - if(length($rev) != 40) { - print STDERR "$revision: cannot find commit '$parent'!\n"; - return; - } - $rev = $branches{($parent eq $opt_o) ? "/" : $parent}{"LAST"}; - if($revision != $opt_s and not $rev) { - print STDERR "$revision: do not know ancestor for '$parent'!\n"; - return; - } - } else { - $rev = undef; - } - -# if($prev and $prev->[0] eq "A") { -# if(not $tag) { -# unless(open(H,"> $git_dir/refs/heads/$branch")) { -# print STDERR "$revision: Could not create branch $branch: $!\n"; -# $state=11; -# next; -# } -# print H "$rev\n" -# or die "Could not write branch $branch: $!"; -# close(H) -# or die "Could not write branch $branch: $!"; -# } -# } - if(not defined $rev) { - unlink($git_index); - } elsif ($rev ne $last_rev) { - print "Switching from $last_rev to $rev ($branch)\n" if $opt_v; - system("git", "read-tree", $rev); - die "read-tree failed for $rev: $?\n" if $?; - $last_rev = $rev; - } - - push (@parents, $rev) if defined $rev; - - my $cid; - if($tag and not %$changed_paths) { - $cid = $rev; - } else { - my @paths = sort keys %$changed_paths; - foreach my $path(@paths) { - my $action = $changed_paths->{$path}; - - if ($action->[0] eq "R") { - # refer to a file/tree in an earlier commit - push(@old,$path); # remove any old stuff - } - if(($action->[0] eq "A") || ($action->[0] eq "R")) { - my $node_kind = node_kind($action->[3], $revision); - if ($node_kind eq $SVN::Node::file) { - my $f = get_file($action->[3], - $revision, $path); - if ($f) { - push(@new,$f) if $f; - } else { - my $opath = $action->[3]; - print STDERR "$revision: $branch: could not fetch '$opath'\n"; - } - } elsif ($node_kind eq $SVN::Node::dir) { - if($action->[1]) { - copy_path($revision, $branch, - $path, $action->[1], - $action->[2], $node_kind, - \@new, \@parents); - } else { - get_ignore(\@new, \@old, $revision, - $path, $action->[3]); - } - } - } elsif ($action->[0] eq "D") { - push(@old,$path); - } elsif ($action->[0] eq "M") { - my $node_kind = node_kind($action->[3], $revision); - if ($node_kind eq $SVN::Node::file) { - my $f = get_file($action->[3], - $revision, $path); - push(@new,$f) if $f; - } elsif ($node_kind eq $SVN::Node::dir) { - get_ignore(\@new, \@old, $revision, - $path, $action->[3]); - } - } else { - die "$revision: unknown action '".$action->[0]."' for $path\n"; - } - } - - while(@old) { - my @o1; - if(@old > 55) { - @o1 = splice(@old,0,50); - } else { - @o1 = @old; - @old = (); - } - my $pid = open my $F, "-|"; - die "$!" unless defined $pid; - if (!$pid) { - exec("git", "ls-files", "-z", @o1) or die $!; - } - @o1 = (); - local $/ = "\0"; - while(<$F>) { - chomp; - push(@o1,$_); - } - close($F); - - while(@o1) { - my @o2; - if(@o1 > 55) { - @o2 = splice(@o1,0,50); - } else { - @o2 = @o1; - @o1 = (); - } - system("git","update-index","--force-remove","--",@o2); - die "Cannot remove files: $?\n" if $?; - } - } - while(@new) { - my @n2; - if(@new > 12) { - @n2 = splice(@new,0,10); - } else { - @n2 = @new; - @new = (); - } - system("git","update-index","--add", - (map { ('--cacheinfo', @$_) } @n2)); - die "Cannot add files: $?\n" if $?; - } - - my $pid = open(C,"-|"); - die "Cannot fork: $!" unless defined $pid; - unless($pid) { - exec("git","write-tree"); - die "Cannot exec git-write-tree: $!\n"; - } - chomp(my $tree = <C>); - length($tree) == 40 - or die "Cannot get tree id ($tree): $!\n"; - close(C) - or die "Error running git-write-tree: $?\n"; - print "Tree ID $tree\n" if $opt_v; - - my $pr = IO::Pipe->new() or die "Cannot open pipe: $!\n"; - my $pw = IO::Pipe->new() or die "Cannot open pipe: $!\n"; - $pid = fork(); - die "Fork: $!\n" unless defined $pid; - unless($pid) { - $pr->writer(); - $pw->reader(); - open(OUT,">&STDOUT"); - dup2($pw->fileno(),0); - dup2($pr->fileno(),1); - $pr->close(); - $pw->close(); - - my @par = (); - - # loose detection of merges - # based on the commit msg - foreach my $rx (@mergerx) { - if ($message =~ $rx) { - my $mparent = $1; - if ($mparent eq 'HEAD') { $mparent = $opt_o }; - if ( -e "$git_dir/refs/heads/$mparent") { - $mparent = get_headref($mparent, $git_dir); - push (@parents, $mparent); - print OUT "Merge parent branch: $mparent\n" if $opt_v; - } - } - } - my %seen_parents = (); - my @unique_parents = grep { ! $seen_parents{$_} ++ } @parents; - foreach my $bparent (@unique_parents) { - push @par, '-p', $bparent; - print OUT "Merge parent branch: $bparent\n" if $opt_v; - } - - exec("env", - "GIT_AUTHOR_NAME=$author_name", - "GIT_AUTHOR_EMAIL=$author_email", - "GIT_AUTHOR_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)), - "GIT_COMMITTER_NAME=$committer_name", - "GIT_COMMITTER_EMAIL=$committer_email", - "GIT_COMMITTER_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)), - "git", "commit-tree", $tree,@par); - die "Cannot exec git-commit-tree: $!\n"; - } - $pw->writer(); - $pr->reader(); - - $message =~ s/[\s\n]+\z//; - $message = "r$revision: $message" if $opt_r; - - print $pw "$message\n" - or die "Error writing to git-commit-tree: $!\n"; - $pw->close(); - - print "Committed change $revision:$branch ".strftime("%Y-%m-%d %H:%M:%S",gmtime($date)).")\n" if $opt_v; - chomp($cid = <$pr>); - length($cid) == 40 - or die "Cannot get commit id ($cid): $!\n"; - print "Commit ID $cid\n" if $opt_v; - $pr->close(); - - waitpid($pid,0); - die "Error running git-commit-tree: $?\n" if $?; - } - - if (not defined $cid) { - $cid = $branches{"/"}{"LAST"}; - } - - if(not defined $dest) { - print "... no known parent\n" if $opt_v; - } elsif(not $tag) { - print "Writing to refs/heads/$dest\n" if $opt_v; - open(C,">$git_dir/refs/heads/$dest") and - print C ("$cid\n") and - close(C) - or die "Cannot write branch $dest for update: $!\n"; - } - - if ($tag) { - $last_rev = "-" if %$changed_paths; - # the tag was 'complex', i.e. did not refer to a "real" revision - - $dest =~ tr/_/\./ if $opt_u; - - system('git', 'tag', '-f', $dest, $cid) == 0 - or die "Cannot create tag $dest: $!\n"; - - print "Created tag '$dest' on '$branch'\n" if $opt_v; - } - $branches{$branch}{"LAST"} = $cid; - $branches{$branch}{$revision} = $cid; - $last_rev = $cid; - print BRANCHES "$revision $branch $cid\n"; - print "DONE: $revision $dest $cid\n" if $opt_v; -} - -sub commit_all { - # Recursive use of the SVN connection does not work - local $svn = $svn2; - - my ($changed_paths, $revision, $author, $date, $message) = @_; - my %p; - while(my($path,$action) = each %$changed_paths) { - $p{$path} = [ $action->action,$action->copyfrom_path, $action->copyfrom_rev, $path ]; - } - $changed_paths = \%p; - - my %done; - my @col; - my $pref; - my $branch; - - while(my($path,$action) = each %$changed_paths) { - ($branch,$path) = split_path($revision,$path); - next if not defined $branch; - next if not defined $path; - $done{$branch}{$path} = $action; - } - while(($branch,$changed_paths) = each %done) { - commit($branch, $changed_paths, $revision, $author, $date, $message); - } -} - -$opt_l = $svn->{'maxrev'} if not defined $opt_l or $opt_l > $svn->{'maxrev'}; - -if ($opt_l < $current_rev) { - print "Up to date: no new revisions to fetch!\n" if $opt_v; - unlink("$git_dir/SVN2GIT_HEAD"); - exit; -} - -print "Processing from $current_rev to $opt_l ...\n" if $opt_v; - -my $from_rev; -my $to_rev = $current_rev - 1; - -my $subpool = SVN::Pool::new_default_sub; -while ($to_rev < $opt_l) { - $subpool->clear; - $from_rev = $to_rev + 1; - $to_rev = $from_rev + $repack_after; - $to_rev = $opt_l if $opt_l < $to_rev; - print "Fetching from $from_rev to $to_rev ...\n" if $opt_v; - $svn->{'svn'}->get_log("",$from_rev,$to_rev,0,1,1,\&commit_all); - my $pid = fork(); - die "Fork: $!\n" unless defined $pid; - unless($pid) { - exec("git", "repack", "-d") - or die "Cannot repack: $!\n"; - } - waitpid($pid, 0); -} - - -unlink($git_index); - -if (defined $orig_git_index) { - $ENV{GIT_INDEX_FILE} = $orig_git_index; -} else { - delete $ENV{GIT_INDEX_FILE}; -} - -# Now switch back to the branch we were in before all of this happened -if($orig_branch) { - print "DONE\n" if $opt_v and (not defined $opt_l or $opt_l > 0); - system("cp","$git_dir/refs/heads/$opt_o","$git_dir/refs/heads/master") - if $forward_master; - unless ($opt_i) { - system('git', 'read-tree', '-m', '-u', 'SVN2GIT_HEAD', 'HEAD'); - die "read-tree failed: $?\n" if $?; - } -} else { - $orig_branch = "master"; - print "DONE; creating $orig_branch branch\n" if $opt_v and (not defined $opt_l or $opt_l > 0); - system("cp","$git_dir/refs/heads/$opt_o","$git_dir/refs/heads/master") - unless -f "$git_dir/refs/heads/master"; - system('git', 'update-ref', 'HEAD', "$orig_branch"); - unless ($opt_i) { - system('git checkout'); - die "checkout failed: $?\n" if $?; - } -} -unlink("$git_dir/SVN2GIT_HEAD"); -close(BRANCHES); diff --git a/contrib/examples/git-svnimport.txt b/contrib/examples/git-svnimport.txt deleted file mode 100644 index 3f0a9c33b5..0000000000 --- a/contrib/examples/git-svnimport.txt +++ /dev/null @@ -1,179 +0,0 @@ -git-svnimport(1) -================ -v0.1, July 2005 - -NAME ----- -git-svnimport - Import a SVN repository into git - - -SYNOPSIS --------- -[verse] -'git-svnimport' [ -o <branch-for-HEAD> ] [ -h ] [ -v ] [ -d | -D ] - [ -C <GIT_repository> ] [ -i ] [ -u ] [-l limit_rev] - [ -b branch_subdir ] [ -T trunk_subdir ] [ -t tag_subdir ] - [ -s start_chg ] [ -m ] [ -r ] [ -M regex ] - [ -I <ignorefile_name> ] [ -A <author_file> ] - [ -R <repack_each_revs>] [ -P <path_from_trunk> ] - <SVN_repository_URL> [ <path> ] - - -DESCRIPTION ------------ -Imports a SVN repository into git. It will either create a new -repository, or incrementally import into an existing one. - -SVN access is done by the SVN::Perl module. - -git-svnimport assumes that SVN repositories are organized into one -"trunk" directory where the main development happens, "branches/FOO" -directories for branches, and "/tags/FOO" directories for tags. -Other subdirectories are ignored. - -git-svnimport creates a file ".git/svn2git", which is required for -incremental SVN imports. - -OPTIONS -------- --C <target-dir>:: - The GIT repository to import to. If the directory doesn't - exist, it will be created. Default is the current directory. - --s <start_rev>:: - Start importing at this SVN change number. The default is 1. -+ -When importing incrementally, you might need to edit the .git/svn2git file. - --i:: - Import-only: don't perform a checkout after importing. This option - ensures the working directory and index remain untouched and will - not create them if they do not exist. - --T <trunk_subdir>:: - Name the SVN trunk. Default "trunk". - --t <tag_subdir>:: - Name the SVN subdirectory for tags. Default "tags". - --b <branch_subdir>:: - Name the SVN subdirectory for branches. Default "branches". - --o <branch-for-HEAD>:: - The 'trunk' branch from SVN is imported to the 'origin' branch within - the git repository. Use this option if you want to import into a - different branch. - --r:: - Prepend 'rX: ' to commit messages, where X is the imported - subversion revision. - --u:: - Replace underscores in tag names with periods. - --I <ignorefile_name>:: - Import the svn:ignore directory property to files with this - name in each directory. (The Subversion and GIT ignore - syntaxes are similar enough that using the Subversion patterns - directly with "-I .gitignore" will almost always just work.) - --A <author_file>:: - Read a file with lines on the form -+ ------- - username = User's Full Name <email@addr.es> - ------- -+ -and use "User's Full Name <email@addr.es>" as the GIT -author and committer for Subversion commits made by -"username". If encountering a commit made by a user not in the -list, abort. -+ -For convenience, this data is saved to $GIT_DIR/svn-authors -each time the -A option is provided, and read from that same -file each time git-svnimport is run with an existing GIT -repository without -A. - --m:: - Attempt to detect merges based on the commit message. This option - will enable default regexes that try to capture the name source - branch name from the commit message. - --M <regex>:: - Attempt to detect merges based on the commit message with a custom - regex. It can be used with -m to also see the default regexes. - You must escape forward slashes. - --l <max_rev>:: - Specify a maximum revision number to pull. -+ -Formerly, this option controlled how many revisions to pull, -due to SVN memory leaks. (These have been worked around.) - --R <repack_each_revs>:: - Specify how often git repository should be repacked. -+ -The default value is 1000. git-svnimport will do imports in chunks of 1000 -revisions, after each chunk the git repository will be repacked. To disable -this behavior specify some large value here which is greater than the number of -revisions to import. - --P <path_from_trunk>:: - Partial import of the SVN tree. -+ -By default, the whole tree on the SVN trunk (/trunk) is imported. -'-P my/proj' will import starting only from '/trunk/my/proj'. -This option is useful when you want to import one project from a -svn repo which hosts multiple projects under the same trunk. - --v:: - Verbosity: let 'svnimport' report what it is doing. - --d:: - Use direct HTTP requests if possible. The "<path>" argument is used - only for retrieving the SVN logs; the path to the contents is - included in the SVN log. - --D:: - Use direct HTTP requests if possible. The "<path>" argument is used - for retrieving the logs, as well as for the contents. -+ -There's no safe way to automatically find out which of these options to -use, so you need to try both. Usually, the one that's wrong will die -with a 40x error pretty quickly. - -<SVN_repository_URL>:: - The URL of the SVN module you want to import. For local - repositories, use "file:///absolute/path". -+ -If you're using the "-d" or "-D" option, this is the URL of the SVN -repository itself; it usually ends in "/svn". - -<path>:: - The path to the module you want to check out. - --h:: - Print a short usage message and exit. - -OUTPUT ------- -If '-v' is specified, the script reports what it is doing. - -Otherwise, success is indicated the Unix way, i.e. by simply exiting with -a zero exit status. - -Author ------- -Written by Matthias Urlichs <smurf@smurf.noris.de>, with help from -various participants of the git-list <git@vger.kernel.org>. - -Based on a cvs2git script by the same author. - -Documentation --------------- -Documentation by Matthias Urlichs <smurf@smurf.noris.de>. - -GIT ---- -Part of the linkgit:git[7] suite diff --git a/contrib/examples/git-tag.sh b/contrib/examples/git-tag.sh deleted file mode 100755 index 1bd8f3c58d..0000000000 --- a/contrib/examples/git-tag.sh +++ /dev/null @@ -1,205 +0,0 @@ -#!/bin/sh -# Copyright (c) 2005 Linus Torvalds - -USAGE='[-n [<num>]] -l [<pattern>] | [-a | -s | -u <key-id>] [-f | -d | -v] [-m <msg>] <tagname> [<head>]' -SUBDIRECTORY_OK='Yes' -. git-sh-setup - -message_given= -annotate= -signed= -force= -message= -username= -list= -verify= -LINES=0 -while test $# != 0 -do - case "$1" in - -a) - annotate=1 - shift - ;; - -s) - annotate=1 - signed=1 - shift - ;; - -f) - force=1 - shift - ;; - -n) - case "$#,$2" in - 1,* | *,-*) - LINES=1 # no argument - ;; - *) shift - LINES=$(expr "$1" : '\([0-9]*\)') - [ -z "$LINES" ] && LINES=1 # 1 line is default when -n is used - ;; - esac - shift - ;; - -l) - list=1 - shift - case $# in - 0) PATTERN= - ;; - *) - PATTERN="$1" # select tags by shell pattern, not re - shift - ;; - esac - git rev-parse --symbolic --tags | sort | - while read TAG - do - case "$TAG" in - *$PATTERN*) ;; - *) continue ;; - esac - [ "$LINES" -le 0 ] && { echo "$TAG"; continue ;} - OBJTYPE=$(git cat-file -t "$TAG") - case $OBJTYPE in - tag) - ANNOTATION=$(git cat-file tag "$TAG" | - sed -e '1,/^$/d' | - sed -n -e " - /^-----BEGIN PGP SIGNATURE-----\$/q - 2,\$s/^/ / - p - ${LINES}q - ") - printf "%-15s %s\n" "$TAG" "$ANNOTATION" - ;; - *) echo "$TAG" - ;; - esac - done - ;; - -m) - annotate=1 - shift - message="$1" - if test "$#" = "0"; then - die "error: option -m needs an argument" - else - message="$1" - message_given=1 - shift - fi - ;; - -F) - annotate=1 - shift - if test "$#" = "0"; then - die "error: option -F needs an argument" - else - message="$(cat "$1")" - message_given=1 - shift - fi - ;; - -u) - annotate=1 - signed=1 - shift - if test "$#" = "0"; then - die "error: option -u needs an argument" - else - username="$1" - shift - fi - ;; - -d) - shift - had_error=0 - for tag - do - cur=$(git show-ref --verify --hash -- "refs/tags/$tag") || { - echo >&2 "Seriously, what tag are you talking about?" - had_error=1 - continue - } - git update-ref -m 'tag: delete' -d "refs/tags/$tag" "$cur" || { - had_error=1 - continue - } - echo "Deleted tag $tag." - done - exit $had_error - ;; - -v) - shift - tag_name="$1" - tag=$(git show-ref --verify --hash -- "refs/tags/$tag_name") || - die "Seriously, what tag are you talking about?" - git-verify-tag -v "$tag" - exit $? - ;; - -*) - usage - ;; - *) - break - ;; - esac -done - -[ -n "$list" ] && exit 0 - -name="$1" -[ "$name" ] || usage -prev=0000000000000000000000000000000000000000 -if git show-ref --verify --quiet -- "refs/tags/$name" -then - test -n "$force" || die "tag '$name' already exists" - prev=$(git rev-parse "refs/tags/$name") -fi -shift -git check-ref-format "tags/$name" || - die "we do not like '$name' as a tag name." - -object=$(git rev-parse --verify --default HEAD "$@") || exit 1 -type=$(git cat-file -t $object) || exit 1 -tagger=$(git var GIT_COMMITTER_IDENT) || exit 1 - -test -n "$username" || - username=$(git config user.signingkey) || - username=$(expr "z$tagger" : 'z\(.*>\)') - -trap 'rm -f "$GIT_DIR"/TAG_TMP* "$GIT_DIR"/TAG_FINALMSG "$GIT_DIR"/TAG_EDITMSG' 0 - -if [ "$annotate" ]; then - if [ -z "$message_given" ]; then - ( echo "#" - echo "# Write a tag message" - echo "#" ) > "$GIT_DIR"/TAG_EDITMSG - git_editor "$GIT_DIR"/TAG_EDITMSG || exit - else - printf '%s\n' "$message" >"$GIT_DIR"/TAG_EDITMSG - fi - - grep -v '^#' <"$GIT_DIR"/TAG_EDITMSG | - git stripspace >"$GIT_DIR"/TAG_FINALMSG - - [ -s "$GIT_DIR"/TAG_FINALMSG -o -n "$message_given" ] || { - echo >&2 "No tag message?" - exit 1 - } - - ( printf 'object %s\ntype %s\ntag %s\ntagger %s\n\n' \ - "$object" "$type" "$name" "$tagger"; - cat "$GIT_DIR"/TAG_FINALMSG ) >"$GIT_DIR"/TAG_TMP - rm -f "$GIT_DIR"/TAG_TMP.asc "$GIT_DIR"/TAG_FINALMSG - if [ "$signed" ]; then - gpg -bsa -u "$username" "$GIT_DIR"/TAG_TMP && - cat "$GIT_DIR"/TAG_TMP.asc >>"$GIT_DIR"/TAG_TMP || - die "failed to sign the tag with GPG." - fi - object=$(git-mktag < "$GIT_DIR"/TAG_TMP) -fi - -git update-ref "refs/tags/$name" "$object" "$prev" diff --git a/contrib/examples/git-verify-tag.sh b/contrib/examples/git-verify-tag.sh deleted file mode 100755 index 0902a5c21a..0000000000 --- a/contrib/examples/git-verify-tag.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/sh - -USAGE='<tag>' -SUBDIRECTORY_OK='Yes' -. git-sh-setup - -verbose= -while test $# != 0 -do - case "$1" in - -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose) - verbose=t ;; - *) - break ;; - esac - shift -done - -if [ "$#" != "1" ] -then - usage -fi - -type="$(git cat-file -t "$1" 2>/dev/null)" || - die "$1: no such object." - -test "$type" = tag || - die "$1: cannot verify a non-tag object of type $type." - -case "$verbose" in -t) - git cat-file -p "$1" | - sed -n -e '/^-----BEGIN PGP SIGNATURE-----/q' -e p - ;; -esac - -trap 'rm -f "$GIT_DIR/.tmp-vtag"' 0 - -git cat-file tag "$1" >"$GIT_DIR/.tmp-vtag" || exit 1 -sed -n -e ' - /^-----BEGIN PGP SIGNATURE-----$/q - p -' <"$GIT_DIR/.tmp-vtag" | -gpg --verify "$GIT_DIR/.tmp-vtag" - || exit 1 -rm -f "$GIT_DIR/.tmp-vtag" diff --git a/contrib/examples/git-whatchanged.sh b/contrib/examples/git-whatchanged.sh deleted file mode 100755 index 2edbdc6d99..0000000000 --- a/contrib/examples/git-whatchanged.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/sh - -USAGE='[-p] [--max-count=<n>] [<since>..<limit>] [--pretty=<format>] [-m] [git-diff-tree options] [git-rev-list options]' -SUBDIRECTORY_OK='Yes' -. git-sh-setup - -diff_tree_flags=$(git-rev-parse --sq --no-revs --flags "$@") || exit -case "$0" in -*whatchanged) - count= - test -z "$diff_tree_flags" && - diff_tree_flags=$(git config --get whatchanged.difftree) - diff_tree_default_flags='-c -M --abbrev' ;; -*show) - count=-n1 - test -z "$diff_tree_flags" && - diff_tree_flags=$(git config --get show.difftree) - diff_tree_default_flags='--cc --always' ;; -esac -test -z "$diff_tree_flags" && - diff_tree_flags="$diff_tree_default_flags" - -rev_list_args=$(git-rev-parse --sq --default HEAD --revs-only "$@") && -diff_tree_args=$(git-rev-parse --sq --no-revs --no-flags "$@") && - -eval "git-rev-list $count $rev_list_args" | -eval "git-diff-tree --stdin --pretty -r $diff_tree_flags $diff_tree_args" | -LESS="$LESS -S" ${PAGER:-less} diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl index d60b4315ed..e800d9f5c9 100755 --- a/contrib/fast-import/import-tars.perl +++ b/contrib/fast-import/import-tars.perl @@ -63,6 +63,8 @@ foreach my $tar_file (@ARGV) my $have_top_dir = 1; my ($top_dir, %files); + my $next_path = ''; + while (read(I, $_, 512) == 512) { my ($name, $mode, $uid, $gid, $size, $mtime, $chksum, $typeflag, $linkname, $magic, @@ -70,6 +72,13 @@ foreach my $tar_file (@ARGV) $prefix) = unpack 'Z100 Z8 Z8 Z8 Z12 Z12 Z8 Z1 Z100 Z6 Z2 Z32 Z32 Z8 Z8 Z*', $_; + + unless ($next_path eq '') { + # Recover name from previous extended header + $name = $next_path; + $next_path = ''; + } + last unless length($name); if ($name eq '././@LongLink') { # GNU tar extension @@ -90,13 +99,31 @@ foreach my $tar_file (@ARGV) Z8 Z1 Z100 Z6 Z2 Z32 Z32 Z8 Z8 Z*', $_; } - next if $name =~ m{/\z}; $mode = oct $mode; $size = oct $size; $mtime = oct $mtime; next if $typeflag == 5; # directory - if ($typeflag != 1) { # handle hard links later + if ($typeflag eq 'x') { # extended header + # If extended header, check for path + my $pax_header = ''; + while ($size > 0 && read(I, $_, 512) == 512) { + $pax_header = $pax_header . substr($_, 0, $size); + $size -= 512; + } + + my @lines = split /\n/, $pax_header; + foreach my $line (@lines) { + my ($len, $entry) = split / /, $line; + my ($key, $value) = split /=/, $entry; + if ($key eq 'path') { + $next_path = $value; + } + } + next; + } elsif ($name =~ m{/\z}) { # directory + next; + } elsif ($typeflag != 1) { # handle hard links later print FI "blob\n", "mark :$next_mark\n"; if ($typeflag == 2) { # symbolic link print FI "data ", length($linkname), "\n", diff --git a/contrib/git-jump/README b/contrib/git-jump/README index 225e3f0954..2f618a7f97 100644 --- a/contrib/git-jump/README +++ b/contrib/git-jump/README @@ -25,6 +25,13 @@ git-jump will feed this to the editor: foo.c:2: printf("hello word!\n"); ----------------------------------- +Or, when running 'git jump grep', column numbers will also be emitted, +e.g. `git jump grep "hello"` would return: + +----------------------------------- +foo.c:2:9: printf("hello word!\n"); +----------------------------------- + Obviously this trivial case isn't that interesting; you could just open `foo.c` yourself. But when you have many changes scattered across a project, you can use the editor's support to "jump" from point to point. @@ -35,7 +42,8 @@ Git-jump can generate four types of interesting lists: 2. The beginning of any merge conflict markers. - 3. Any grep matches. + 3. Any grep matches, including the column of the first match on a + line. 4. Any whitespace errors detected by `git diff --check`. @@ -63,6 +71,9 @@ git jump grep foo_bar # same as above, but case-insensitive; you can give # arbitrary grep options git jump grep -i foo_bar + +# use the silver searcher for git jump grep +git config jump.grepCmd "ag --column" -------------------------------------------------- @@ -79,7 +90,7 @@ which does something similar to `git jump grep`. However, it is limited to positioning the cursor to the correct line in only the first file, leaving you to locate subsequent hits in that file or other files using the editor or pager. By contrast, git-jump provides the editor with a -complete list of files and line numbers for each match. +complete list of files, lines, and a column number for each match. Limitations @@ -92,3 +103,10 @@ how to activate it. The shell snippets to generate the quickfix lines will almost certainly choke on filenames with exotic characters (like newlines). + +Contributing +------------ + +Bug fixes, bug reports, and feature requests should be discussed on the +Git mailing list <git@vger.kernel.org>, and cc'd to the git-jump +maintainer, Jeff King <peff@peff.net>. diff --git a/contrib/git-jump/git-jump b/contrib/git-jump/git-jump index 427f206a45..931b0fe3a9 100755 --- a/contrib/git-jump/git-jump +++ b/contrib/git-jump/git-jump @@ -11,7 +11,8 @@ diff: elements are diff hunks. Arguments are given to diff. merge: elements are merge conflicts. Arguments are ignored. -grep: elements are grep hits. Arguments are given to grep. +grep: elements are grep hits. Arguments are given to git grep or, if + configured, to the command in `jump.grepCmd`. ws: elements are whitespace errors. Arguments are given to diff --check. EOF @@ -50,7 +51,9 @@ mode_merge() { # but let's clean up extra whitespace, so they look better if the # editor shows them to us in the status bar. mode_grep() { - git grep -n "$@" | + cmd=$(git config jump.grepCmd) + test -n "$cmd" || cmd="git grep -n --column" + $cmd "$@" | perl -pe ' s/[ \t]+/ /g; s/^ *//; diff --git a/contrib/hooks/pre-auto-gc-battery b/contrib/hooks/pre-auto-gc-battery index 6a2cdebdb7..7ba78c4dff 100755 --- a/contrib/hooks/pre-auto-gc-battery +++ b/contrib/hooks/pre-auto-gc-battery @@ -17,7 +17,7 @@ # ln -sf /usr/share/git-core/contrib/hooks/pre-auto-gc-battery \ # hooks/pre-auto-gc -if test -x /sbin/on_ac_power && /sbin/on_ac_power +if test -x /sbin/on_ac_power && (/sbin/on_ac_power;test $? -ne 1) then exit 0 elif test "$(cat /sys/class/power_supply/AC/online 2>/dev/null)" = 1 diff --git a/contrib/mw-to-git/Git/Mediawiki.pm b/contrib/mw-to-git/Git/Mediawiki.pm index d13c4dfa7d..917d9e2d32 100644 --- a/contrib/mw-to-git/Git/Mediawiki.pm +++ b/contrib/mw-to-git/Git/Mediawiki.pm @@ -2,6 +2,7 @@ package Git::Mediawiki; use 5.008; use strict; +use POSIX; use Git; BEGIN { @@ -52,7 +53,7 @@ sub smudge_filename { $filename =~ s/ /_/g; # Decode forbidden characters encoded in clean_filename $filename =~ s/_%_([0-9a-fA-F][0-9a-fA-F])/sprintf('%c', hex($1))/ge; - return $filename; + return substr($filename, 0, NAME_MAX-length('.mw')); } sub connect_maybe { diff --git a/contrib/mw-to-git/Makefile b/contrib/mw-to-git/Makefile index a4b6f7a2cd..4e603512a3 100644 --- a/contrib/mw-to-git/Makefile +++ b/contrib/mw-to-git/Makefile @@ -21,8 +21,9 @@ HERE=contrib/mw-to-git/ INSTALL = install SCRIPT_PERL_FULL=$(patsubst %,$(HERE)/%,$(SCRIPT_PERL)) -INSTLIBDIR=$(shell $(MAKE) -C $(GIT_ROOT_DIR)/perl \ - -s --no-print-directory instlibdir) +INSTLIBDIR=$(shell $(MAKE) -C $(GIT_ROOT_DIR)/ \ + -s --no-print-directory prefix=$(prefix) \ + perllibdir=$(perllibdir) perllibdir) DESTDIR_SQ = $(subst ','\'',$(DESTDIR)) INSTLIBDIR_SQ = $(subst ','\'',$(INSTLIBDIR)) diff --git a/contrib/mw-to-git/git-remote-mediawiki.perl b/contrib/mw-to-git/git-remote-mediawiki.perl index e7f857c1a2..af9cbc9d0f 100755 --- a/contrib/mw-to-git/git-remote-mediawiki.perl +++ b/contrib/mw-to-git/git-remote-mediawiki.perl @@ -63,6 +63,11 @@ chomp(@tracked_pages); my @tracked_categories = split(/[ \n]/, run_git("config --get-all remote.${remotename}.categories")); chomp(@tracked_categories); +# Just like @tracked_categories, but for MediaWiki namespaces. +my @tracked_namespaces = split(/[ \n]/, run_git("config --get-all remote.${remotename}.namespaces")); +for (@tracked_namespaces) { s/_/ /g; } +chomp(@tracked_namespaces); + # Import media files on pull my $import_media = run_git("config --get --bool remote.${remotename}.mediaimport"); chomp($import_media); @@ -256,6 +261,32 @@ sub get_mw_tracked_categories { return; } +sub get_mw_tracked_namespaces { + my $pages = shift; + foreach my $local_namespace (sort @tracked_namespaces) { + my $namespace_id; + if ($local_namespace eq "(Main)") { + $namespace_id = 0; + } else { + $namespace_id = get_mw_namespace_id($local_namespace); + } + # virtual namespaces don't support allpages + next if !defined($namespace_id) || $namespace_id < 0; + my $mw_pages = $mediawiki->list( { + action => 'query', + list => 'allpages', + apnamespace => $namespace_id, + aplimit => 'max' } ) + || die $mediawiki->{error}->{code} . ': ' + . $mediawiki->{error}->{details} . "\n"; + print {*STDERR} "$#{$mw_pages} found in namespace $local_namespace ($namespace_id)\n"; + foreach my $page (@{$mw_pages}) { + $pages->{$page->{title}} = $page; + } + } + return; +} + sub get_mw_all_pages { my $pages = shift; # No user-provided list, get the list of pages from the API. @@ -319,6 +350,10 @@ sub get_mw_pages { $user_defined = 1; get_mw_tracked_categories(\%pages); } + if (@tracked_namespaces) { + $user_defined = 1; + get_mw_tracked_namespaces(\%pages); + } if (!$user_defined) { get_mw_all_pages(\%pages); } @@ -1308,7 +1343,8 @@ sub get_mw_namespace_id { my $id; if (!defined $ns) { - print {*STDERR} "No such namespace ${name} on MediaWiki.\n"; + my @namespaces = map { s/ /_/g; $_; } sort keys %namespace_id; + print {*STDERR} "No such namespace ${name} on MediaWiki, known namespaces: @namespaces\n"; $ns = {is_namespace => 0}; $namespace_id{$name} = $ns; } diff --git a/contrib/mw-to-git/t/t9360-mw-to-git-clone.sh b/contrib/mw-to-git/t/t9360-mw-to-git-clone.sh index 22f069db48..cfbfe7ddf6 100755 --- a/contrib/mw-to-git/t/t9360-mw-to-git-clone.sh +++ b/contrib/mw-to-git/t/t9360-mw-to-git-clone.sh @@ -247,7 +247,7 @@ test_expect_success 'Test of resistance to modification of category on wiki for wiki_editpage Notconsidered "this page will not appear on local" false && wiki_editpage Othercategory "this page will not appear on local" false -c=Cattwo && wiki_editpage Tobeedited "this page have been modified" true -c=Catone && - wiki_delete_page Tobedeleted + wiki_delete_page Tobedeleted && git clone -c remote.origin.categories="Catone" \ mediawiki::'"$WIKI_URL"' mw_dir_14 && wiki_getallpage ref_page_14 Catone && diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh index dec085a235..d3f39a862a 100755 --- a/contrib/subtree/git-subtree.sh +++ b/contrib/subtree/git-subtree.sh @@ -297,7 +297,7 @@ find_latest_squash () { main= sub= git log --grep="^git-subtree-dir: $dir/*\$" \ - --pretty=format:'START %H%n%s%n%n%b%nEND%n' HEAD | + --no-show-signature --pretty=format:'START %H%n%s%n%n%b%nEND%n' HEAD | while read a b junk do debug "$a $b $junk" @@ -341,7 +341,7 @@ find_existing_splits () { main= sub= git log --grep="^git-subtree-dir: $dir/*\$" \ - --pretty=format:'START %H%n%s%n%n%b%nEND%n' $revs | + --no-show-signature --pretty=format:'START %H%n%s%n%n%b%nEND%n' $revs | while read a b junk do case "$a" in @@ -382,7 +382,7 @@ copy_commit () { # We're going to set some environment vars here, so # do it in a subshell to get rid of them safely later debug copy_commit "{$1}" "{$2}" "{$3}" - git log -1 --pretty=format:'%an%n%ae%n%aD%n%cn%n%ce%n%cD%n%B' "$1" | + git log -1 --no-show-signature --pretty=format:'%an%n%ae%n%aD%n%cn%n%ce%n%cD%n%B' "$1" | ( read GIT_AUTHOR_NAME read GIT_AUTHOR_EMAIL @@ -462,8 +462,8 @@ squash_msg () { oldsub_short=$(git rev-parse --short "$oldsub") echo "Squashed '$dir/' changes from $oldsub_short..$newsub_short" echo - git log --pretty=tformat:'%h %s' "$oldsub..$newsub" - git log --pretty=tformat:'REVERT: %h %s' "$newsub..$oldsub" + git log --no-show-signature --pretty=tformat:'%h %s' "$oldsub..$newsub" + git log --no-show-signature --pretty=tformat:'REVERT: %h %s' "$newsub..$oldsub" else echo "Squashed '$dir/' content from commit $newsub_short" fi @@ -475,7 +475,7 @@ squash_msg () { toptree_for_commit () { commit="$1" - git log -1 --pretty=format:'%T' "$commit" -- || exit $? + git rev-parse --verify "$commit^{tree}" || exit $? } subtree_for_commit () { diff --git a/contrib/subtree/git-subtree.txt b/contrib/subtree/git-subtree.txt index 60d76cdddf..352deda69d 100644 --- a/contrib/subtree/git-subtree.txt +++ b/contrib/subtree/git-subtree.txt @@ -28,7 +28,7 @@ as a subdirectory of your application. Subtrees are not to be confused with submodules, which are meant for the same task. Unlike submodules, subtrees do not need any special -constructions (like .gitmodule files or gitlinks) be present in +constructions (like .gitmodules files or gitlinks) be present in your repository, and do not force end-users of your repository to do anything special or to understand how subtrees work. A subtree is just a subdirectory that can be diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh index d05c613c97..57ff4b25c1 100755 --- a/contrib/subtree/t/t7900-subtree.sh +++ b/contrib/subtree/t/t7900-subtree.sh @@ -540,26 +540,10 @@ test_expect_success 'make sure exactly the right set of files ends up in the sub git fetch .. subproj-br && git merge FETCH_HEAD && - chks="sub1 -sub2 -sub3 -sub4" && - chks_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\'' -$chks -TXT -) && - chkms="main-sub1 -main-sub2 -main-sub3 -main-sub4" && - chkms_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\'' -$chkms -TXT -) && - - subfiles=$(git ls-files) && - check_equal "$subfiles" "$chkms -$chks" + test_write_lines main-sub1 main-sub2 main-sub3 main-sub4 \ + sub1 sub2 sub3 sub4 >expect && + git ls-files >actual && + test_cmp expect actual ) ' @@ -606,25 +590,11 @@ test_expect_success 'make sure the subproj *only* contains commits that affect t git fetch .. subproj-br && git merge FETCH_HEAD && - chks="sub1 -sub2 -sub3 -sub4" && - chks_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\'' -$chks -TXT -) && - chkms="main-sub1 -main-sub2 -main-sub3 -main-sub4" && - chkms_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\'' -$chkms -TXT -) && - allchanges=$(git log --name-only --pretty=format:"" | sort | sed "/^$/d") && - check_equal "$allchanges" "$chkms -$chks" + test_write_lines main-sub1 main-sub2 main-sub3 main-sub4 \ + sub1 sub2 sub3 sub4 >expect && + git log --name-only --pretty=format:"" >log && + sort <log | sed "/^\$/ d" >actual && + test_cmp expect actual ) ' @@ -675,29 +645,16 @@ test_expect_success 'make sure exactly the right set of files ends up in the mai cd "$subtree_test_count" && git subtree pull --prefix="sub dir" ./"sub proj" master && - chkm="main1 -main2" && - chks="sub1 -sub2 -sub3 -sub4" && - chks_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\'' -$chks -TXT -) && - chkms="main-sub1 -main-sub2 -main-sub3 -main-sub4" && - chkms_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\'' -$chkms -TXT -) && - mainfiles=$(git ls-files) && - check_equal "$mainfiles" "$chkm -$chkms_sub -$chks_sub" -) + test_write_lines main1 main2 >chkm && + test_write_lines main-sub1 main-sub2 main-sub3 main-sub4 >chkms && + sed "s,^,sub dir/," chkms >chkms_sub && + test_write_lines sub1 sub2 sub3 sub4 >chks && + sed "s,^,sub dir/," chks >chks_sub && + + cat chkm chkms_sub chks_sub >expect && + git ls-files >actual && + test_cmp expect actual + ) ' next_test @@ -708,7 +665,7 @@ test_expect_success 'make sure each filename changed exactly once in the entire test_create_commit "$subtree_test_count/sub proj" sub1 && ( cd "$subtree_test_count" && - git config log.date relative + git config log.date relative && git fetch ./"sub proj" master && git subtree add --prefix="sub dir" FETCH_HEAD ) && @@ -748,37 +705,21 @@ test_expect_success 'make sure each filename changed exactly once in the entire cd "$subtree_test_count" && git subtree pull --prefix="sub dir" ./"sub proj" master && - chkm="main1 -main2" && - chks="sub1 -sub2 -sub3 -sub4" && - chks_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\'' -$chks -TXT -) && - chkms="main-sub1 -main-sub2 -main-sub3 -main-sub4" && - chkms_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\'' -$chkms -TXT -) && + test_write_lines main1 main2 >chkm && + test_write_lines sub1 sub2 sub3 sub4 >chks && + test_write_lines main-sub1 main-sub2 main-sub3 main-sub4 >chkms && + sed "s,^,sub dir/," chkms >chkms_sub && # main-sub?? and /"sub dir"/main-sub?? both change, because those are the # changes that were split into their own history. And "sub dir"/sub?? never # change, since they were *only* changed in the subtree branch. - allchanges=$(git log --name-only --pretty=format:"" | sort | sed "/^$/d") && - expected=''"$(cat <<TXT | sort -$chkms -$chkm -$chks -$chkms_sub -TXT -)"'' && - check_equal "$allchanges" "$expected" + git log --name-only --pretty=format:"" >log && + sort <log >sorted-log && + sed "/^$/ d" sorted-log >actual && + + cat chkms chkm chks chkms_sub >expect-unsorted && + sort expect-unsorted >expect && + test_cmp expect actual ) ' diff --git a/contrib/update-unicode/README b/contrib/update-unicode/README index b9e2fc8540..151a197041 100644 --- a/contrib/update-unicode/README +++ b/contrib/update-unicode/README @@ -1,10 +1,10 @@ TL;DR: Run update_unicode.sh after the publication of a new Unicode -standard and commit the resulting unicode_widths.h file. +standard and commit the resulting unicode-widths.h file. The long version ================ -The Git source code ships the file unicode_widths.h which contains +The Git source code ships the file unicode-widths.h which contains tables of zero and double width Unicode code points, respectively. These tables are generated using update_unicode.sh in this directory. update_unicode.sh itself uses a third-party tool, uniset, to query two @@ -16,5 +16,5 @@ This requires a current-ish version of autoconf (2.69 works per December On each run, update_unicode.sh checks whether more recent Unicode data files are available from the Unicode consortium, and rebuilds the header -unicode_widths.h with the new data. The new header can then be +unicode-widths.h with the new data. The new header can then be committed. diff --git a/contrib/update-unicode/update_unicode.sh b/contrib/update-unicode/update_unicode.sh index e05db92d3f..aa90865bef 100755 --- a/contrib/update-unicode/update_unicode.sh +++ b/contrib/update-unicode/update_unicode.sh @@ -6,7 +6,7 @@ #Cf Format a format control character # cd "$(dirname "$0")" -UNICODEWIDTH_H=$(git rev-parse --show-toplevel)/unicode_width.h +UNICODEWIDTH_H=$(git rev-parse --show-toplevel)/unicode-width.h wget -N http://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt \ http://www.unicode.org/Public/UCD/latest/ucd/EastAsianWidth.txt && diff --git a/contrib/vscode/.gitattributes b/contrib/vscode/.gitattributes new file mode 100644 index 0000000000..e89f2236ef --- /dev/null +++ b/contrib/vscode/.gitattributes @@ -0,0 +1 @@ +init.sh whitespace=-indent-with-non-tab diff --git a/contrib/vscode/README.md b/contrib/vscode/README.md new file mode 100644 index 0000000000..8202d62035 --- /dev/null +++ b/contrib/vscode/README.md @@ -0,0 +1,14 @@ +Configuration for VS Code +========================= + +[VS Code](https://code.visualstudio.com/) is a lightweight but powerful source +code editor which runs on your desktop and is available for +[Windows](https://code.visualstudio.com/docs/setup/windows), +[macOS](https://code.visualstudio.com/docs/setup/mac) and +[Linux](https://code.visualstudio.com/docs/setup/linux). Among other languages, +it has [support for C/C++ via an extension](https://github.com/Microsoft/vscode-cpptools). + +To start developing Git with VS Code, simply run the Unix shell script called +`init.sh` in this directory, which creates the configuration files in +`.vscode/` that VS Code consumes. `init.sh` needs access to `make` and `gcc`, +so run the script in a Git SDK shell if you are using Windows. diff --git a/contrib/vscode/init.sh b/contrib/vscode/init.sh new file mode 100755 index 0000000000..27de94994b --- /dev/null +++ b/contrib/vscode/init.sh @@ -0,0 +1,375 @@ +#!/bin/sh + +die () { + echo "$*" >&2 + exit 1 +} + +cd "$(dirname "$0")"/../.. || +die "Could not cd to top-level directory" + +mkdir -p .vscode || +die "Could not create .vscode/" + +# General settings + +cat >.vscode/settings.json.new <<\EOF || +{ + "C_Cpp.intelliSenseEngine": "Default", + "C_Cpp.intelliSenseEngineFallback": "Disabled", + "[git-commit]": { + "editor.wordWrap": "wordWrapColumn", + "editor.wordWrapColumn": 72 + }, + "[c]": { + "editor.detectIndentation": false, + "editor.insertSpaces": false, + "editor.tabSize": 8, + "editor.wordWrap": "wordWrapColumn", + "editor.wordWrapColumn": 80, + "files.trimTrailingWhitespace": true + }, + "files.associations": { + "*.h": "c", + "*.c": "c" + }, + "cSpell.ignorePaths": [ + ], + "cSpell.words": [ + "DATAW", + "DBCACHED", + "DFCHECK", + "DTYPE", + "Hamano", + "HCAST", + "HEXSZ", + "HKEY", + "HKLM", + "IFGITLINK", + "IFINVALID", + "ISBROKEN", + "ISGITLINK", + "ISSYMREF", + "Junio", + "LPDWORD", + "LPPROC", + "LPWSTR", + "MSVCRT", + "NOARG", + "NOCOMPLETE", + "NOINHERIT", + "RENORMALIZE", + "STARTF", + "STARTUPINFOEXW", + "Schindelin", + "UCRT", + "YESNO", + "argcp", + "beginthreadex", + "committish", + "contentp", + "cpath", + "cpidx", + "ctim", + "dequote", + "envw", + "ewah", + "fdata", + "fherr", + "fhin", + "fhout", + "fragp", + "fsmonitor", + "hnsec", + "idents", + "includeif", + "interpr", + "iprog", + "isexe", + "iskeychar", + "kompare", + "mksnpath", + "mktag", + "mktree", + "mmblob", + "mmbuffer", + "mmfile", + "noenv", + "nparents", + "ntpath", + "ondisk", + "ooid", + "oplen", + "osdl", + "pnew", + "pold", + "ppinfo", + "pushf", + "pushv", + "rawsz", + "rebasing", + "reencode", + "repo", + "rerere", + "scld", + "sharedrepo", + "spawnv", + "spawnve", + "spawnvpe", + "strdup'ing", + "submodule", + "submodules", + "topath", + "topo", + "tpatch", + "unexecutable", + "unhide", + "unkc", + "unkv", + "unmark", + "unmatch", + "unsets", + "unshown", + "untracked", + "untrackedcache", + "unuse", + "upos", + "uval", + "vreportf", + "wargs", + "wargv", + "wbuffer", + "wcmd", + "wcsnicmp", + "wcstoutfdup", + "wdeltaenv", + "wdir", + "wenv", + "wenvblk", + "wenvcmp", + "wenviron", + "wenvpos", + "wenvsz", + "wfile", + "wfilename", + "wfopen", + "wfreopen", + "wfullpath", + "which'll", + "wlink", + "wmain", + "wmkdir", + "wmktemp", + "wnewpath", + "wotype", + "wpath", + "wpathname", + "wpgmptr", + "wpnew", + "wpointer", + "wpold", + "wpos", + "wputenv", + "wrmdir", + "wship", + "wtarget", + "wtemplate", + "wunlink", + "xcalloc", + "xgetcwd", + "xmallocz", + "xmemdupz", + "xmmap", + "xopts", + "xrealloc", + "xsnprintf", + "xutftowcs", + "xutftowcsn", + "xwcstoutf" + ], + "cSpell.ignoreRegExpList": [ + "\\\"(DIRC|FSMN|REUC|UNTR)\\\"", + "\\\\u[0-9a-fA-Fx]{4}\\b", + "\\b(filfre|frotz|xyzzy)\\b", + "\\bCMIT_FMT_DEFAULT\\b", + "\\bde-munge\\b", + "\\bGET_OID_DISAMBIGUATORS\\b", + "\\bHASH_RENORMALIZE\\b", + "\\bTREESAMEness\\b", + "\\bUSE_STDEV\\b", + "\\Wchar *\\*\\W*utfs\\W", + "cURL's", + "nedmalloc'ed", + "ntifs\\.h", + ], +} +EOF +die "Could not write settings.json" + +# Infer some setup-specific locations/names + +GCCPATH="$(which gcc)" +GDBPATH="$(which gdb)" +MAKECOMMAND="make -j5 DEVELOPER=1" +OSNAME= +X= +case "$(uname -s)" in +MINGW*) + GCCPATH="$(cygpath -am "$GCCPATH")" + GDBPATH="$(cygpath -am "$GDBPATH")" + MAKE_BASH="$(cygpath -am /git-cmd.exe) --command=usr\\\\bin\\\\bash.exe" + MAKECOMMAND="$MAKE_BASH -lc \\\"$MAKECOMMAND\\\"" + OSNAME=Win32 + X=.exe + ;; +Linux) + OSNAME=Linux + ;; +Darwin) + OSNAME=macOS + ;; +esac + +# Default build task + +cat >.vscode/tasks.json.new <<EOF || +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "make", + "type": "shell", + "command": "$MAKECOMMAND", + "group": { + "kind": "build", + "isDefault": true + } + } + ] +} +EOF +die "Could not install default build task" + +# Debugger settings + +cat >.vscode/launch.json.new <<EOF || +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: + // https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "(gdb) Launch", + "type": "cppdbg", + "request": "launch", + "program": "\${workspaceFolder}/git$X", + "args": [], + "stopAtEntry": false, + "cwd": "\${workspaceFolder}", + "environment": [], + "externalConsole": true, + "MIMode": "gdb", + "miDebuggerPath": "$GDBPATH", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } + ] +} +EOF +die "Could not write launch configuration" + +# C/C++ extension settings + +make -f - OSNAME=$OSNAME GCCPATH="$GCCPATH" vscode-init \ + >.vscode/c_cpp_properties.json <<\EOF || +include Makefile + +vscode-init: + @mkdir -p .vscode && \ + incs= && defs= && \ + for e in $(ALL_CFLAGS) \ + '-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' \ + '-DGIT_LOCALE_PATH="$(localedir_relative_SQ)"' \ + '-DBINDIR="$(bindir_relative_SQ)"' \ + '-DFALLBACK_RUNTIME_PREFIX="$(prefix_SQ)"' \ + '-DDEFAULT_GIT_TEMPLATE_DIR="$(template_dir_SQ)"' \ + '-DETC_GITCONFIG="$(ETC_GITCONFIG_SQ)"' \ + '-DETC_GITATTRIBUTES="$(ETC_GITATTRIBUTES_SQ)"' \ + '-DGIT_LOCALE_PATH="$(localedir_relative_SQ)"' \ + '-DCURL_DISABLE_TYPECHECK', \ + '-DGIT_HTML_PATH="$(htmldir_relative_SQ)"' \ + '-DGIT_MAN_PATH="$(mandir_relative_SQ)"' \ + '-DGIT_INFO_PATH="$(infodir_relative_SQ)"'; do \ + case "$$e" in \ + -I.) \ + incs="$$(printf '% 16s"$${workspaceRoot}",\n%s' \ + "" "$$incs")" \ + ;; \ + -I/*) \ + incs="$$(printf '% 16s"%s",\n%s' \ + "" "$${e#-I}" "$$incs")" \ + ;; \ + -I*) \ + incs="$$(printf '% 16s"$${workspaceRoot}/%s",\n%s' \ + "" "$${e#-I}" "$$incs")" \ + ;; \ + -D*) \ + defs="$$(printf '% 16s"%s",\n%s' \ + "" "$$(echo "$${e#-D}" | sed 's/"/\\&/g')" \ + "$$defs")" \ + ;; \ + esac; \ + done && \ + echo '{' && \ + echo ' "configurations": [' && \ + echo ' {' && \ + echo ' "name": "$(OSNAME)",' && \ + echo ' "intelliSenseMode": "clang-x64",' && \ + echo ' "includePath": [' && \ + echo "$$incs" | sort | sed '$$s/,$$//' && \ + echo ' ],' && \ + echo ' "defines": [' && \ + echo "$$defs" | sort | sed '$$s/,$$//' && \ + echo ' ],' && \ + echo ' "browse": {' && \ + echo ' "limitSymbolsToIncludedHeaders": true,' && \ + echo ' "databaseFilename": "",' && \ + echo ' "path": [' && \ + echo ' "$${workspaceRoot}"' && \ + echo ' ]' && \ + echo ' },' && \ + echo ' "cStandard": "c11",' && \ + echo ' "cppStandard": "c++17",' && \ + echo ' "compilerPath": "$(GCCPATH)"' && \ + echo ' }' && \ + echo ' ],' && \ + echo ' "version": 4' && \ + echo '}' +EOF +die "Could not write settings for the C/C++ extension" + +for file in .vscode/settings.json .vscode/tasks.json .vscode/launch.json +do + if test -f $file + then + if git diff --no-index --quiet --exit-code $file $file.new + then + rm $file.new + else + printf "The file $file.new has these changes:\n\n" + git --no-pager diff --no-index $file $file.new + printf "\n\nMaybe \`mv $file.new $file\`?\n\n" + fi + else + mv $file.new $file + fi +done |