summaryrefslogtreecommitdiff
path: root/contrib
diff options
context:
space:
mode:
Diffstat (limited to 'contrib')
-rwxr-xr-xcontrib/completion/git-completion.bash214
-rwxr-xr-xcontrib/difftool/git-difftool73
-rwxr-xr-xcontrib/difftool/git-difftool-helper240
-rw-r--r--contrib/difftool/git-difftool.txt105
-rw-r--r--contrib/emacs/Makefile2
-rw-r--r--contrib/emacs/README39
-rw-r--r--contrib/emacs/git.el207
-rw-r--r--contrib/emacs/vc-git.el216
-rwxr-xr-xcontrib/examples/git-svnimport.perl36
-rw-r--r--contrib/examples/git-svnimport.txt6
-rwxr-xr-xcontrib/fast-import/git-p411
-rwxr-xr-xcontrib/git-resurrect.sh180
-rw-r--r--contrib/hooks/post-receive-email4
-rw-r--r--contrib/vim/README12
14 files changed, 983 insertions, 362 deletions
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 3889cfb5aa..8431837f97 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1,3 +1,4 @@
+#!bash
#
# bash completion support for core Git.
#
@@ -33,6 +34,12 @@
# are currently in a git repository. The %s token will be
# the name of the current branch.
#
+# In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty
+# value, unstaged (*) and staged (+) changes will be shown next
+# to the branch name. You can configure this per-repository
+# with the bash.showDirtyState variable, which defaults to true
+# once GIT_PS1_SHOWDIRTYSTATE is enabled.
+#
# To submit patches:
#
# *) Read Documentation/SubmittingPatches
@@ -50,9 +57,11 @@ case "$COMP_WORDBREAKS" in
*) COMP_WORDBREAKS="$COMP_WORDBREAKS:"
esac
+# __gitdir accepts 0 or 1 arguments (i.e., location)
+# returns location of .git repo
__gitdir ()
{
- if [ -z "$1" ]; then
+ if [ -z "${1-}" ]; then
if [ -n "$__git_dir" ]; then
echo "$__git_dir"
elif [ -d .git ]; then
@@ -67,6 +76,8 @@ __gitdir ()
fi
}
+# __git_ps1 accepts 0 or 1 arguments (i.e., format string)
+# returns text to add to bash PS1 prompt (includes branch name)
__git_ps1 ()
{
local g="$(git rev-parse --git-dir 2>/dev/null)"
@@ -111,14 +122,31 @@ __git_ps1 ()
fi
fi
- if [ -n "$1" ]; then
- printf "$1" "${b##refs/heads/}$r"
+ local w
+ local i
+
+ if test -n "${GIT_PS1_SHOWDIRTYSTATE-}"; then
+ if test "$(git config --bool bash.showDirtyState)" != "false"; then
+ git diff --no-ext-diff --ignore-submodules \
+ --quiet --exit-code || w="*"
+ if git rev-parse --quiet --verify HEAD >/dev/null; then
+ git diff-index --cached --quiet \
+ --ignore-submodules HEAD -- || i="+"
+ else
+ i="#"
+ fi
+ fi
+ fi
+
+ if [ -n "${1-}" ]; then
+ printf "$1" "${b##refs/heads/}$w$i$r"
else
- printf " (%s)" "${b##refs/heads/}$r"
+ printf " (%s)" "${b##refs/heads/}$w$i$r"
fi
fi
}
+# __gitcomp_1 requires 2 arguments
__gitcomp_1 ()
{
local c IFS=' '$'\t'$'\n'
@@ -131,6 +159,8 @@ __gitcomp_1 ()
done
}
+# __gitcomp accepts 1, 2, 3, or 4 arguments
+# generates completion reply with compgen
__gitcomp ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
@@ -143,22 +173,23 @@ __gitcomp ()
;;
*)
local IFS=$'\n'
- COMPREPLY=($(compgen -P "$2" \
- -W "$(__gitcomp_1 "$1" "$4")" \
+ COMPREPLY=($(compgen -P "${2-}" \
+ -W "$(__gitcomp_1 "${1-}" "${4-}")" \
-- "$cur"))
;;
esac
}
+# __git_heads accepts 0 or 1 arguments (to pass to __gitdir)
__git_heads ()
{
- local cmd i is_hash=y dir="$(__gitdir "$1")"
+ local cmd i is_hash=y dir="$(__gitdir "${1-}")"
if [ -d "$dir" ]; then
git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
refs/heads
return
fi
- for i in $(git ls-remote "$1" 2>/dev/null); do
+ for i in $(git ls-remote "${1-}" 2>/dev/null); do
case "$is_hash,$i" in
y,*) is_hash=n ;;
n,*^{}) is_hash=y ;;
@@ -168,15 +199,16 @@ __git_heads ()
done
}
+# __git_tags accepts 0 or 1 arguments (to pass to __gitdir)
__git_tags ()
{
- local cmd i is_hash=y dir="$(__gitdir "$1")"
+ local cmd i is_hash=y dir="$(__gitdir "${1-}")"
if [ -d "$dir" ]; then
git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
refs/tags
return
fi
- for i in $(git ls-remote "$1" 2>/dev/null); do
+ for i in $(git ls-remote "${1-}" 2>/dev/null); do
case "$is_hash,$i" in
y,*) is_hash=n ;;
n,*^{}) is_hash=y ;;
@@ -186,9 +218,10 @@ __git_tags ()
done
}
+# __git_refs accepts 0 or 1 arguments (to pass to __gitdir)
__git_refs ()
{
- local i is_hash=y dir="$(__gitdir "$1")"
+ local i is_hash=y dir="$(__gitdir "${1-}")"
local cur="${COMP_WORDS[COMP_CWORD]}" format refs
if [ -d "$dir" ]; then
case "$cur" in
@@ -218,6 +251,7 @@ __git_refs ()
done
}
+# __git_refs2 requires 1 argument (to pass to __git_refs)
__git_refs2 ()
{
local i
@@ -226,6 +260,7 @@ __git_refs2 ()
done
}
+# __git_refs_remotes requires 1 argument (to pass to ls-remote)
__git_refs_remotes ()
{
local cmd i is_hash=y
@@ -470,6 +505,7 @@ __git_aliases ()
done
}
+# __git_aliased_command requires 1 argument
__git_aliased_command ()
{
local word cmdline=$(git --git-dir="$(__gitdir)" \
@@ -482,6 +518,7 @@ __git_aliased_command ()
done
}
+# __git_find_subcommand requires 1 argument
__git_find_subcommand ()
{
local word subcommand c=1
@@ -563,7 +600,7 @@ _git_add ()
--*)
__gitcomp "
--interactive --refresh --patch --update --dry-run
- --ignore-errors
+ --ignore-errors --intent-to-add
"
return
esac
@@ -628,7 +665,6 @@ _git_branch ()
done
case "${COMP_WORDS[COMP_CWORD]}" in
- --*=*) COMPREPLY=() ;;
--*)
__gitcomp "
--color --no-color --verbose --abbrev= --no-abbrev
@@ -759,23 +795,30 @@ _git_describe ()
__gitcomp "$(__git_refs)"
}
-_git_diff ()
-{
- __git_has_doubledash && return
-
- local cur="${COMP_WORDS[COMP_CWORD]}"
- case "$cur" in
- --*)
- __gitcomp "--cached --stat --numstat --shortstat --summary
+__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 --pickaxe-all --pickaxe-regex
+ --find-copies-harder
--text --ignore-space-at-eol --ignore-space-change
--ignore-all-space --exit-code --quiet --ext-diff
--no-ext-diff
--no-prefix --src-prefix= --dst-prefix=
+ --inter-hunk-context=
+ --patience
+ --raw
+"
+
+_git_diff ()
+{
+ __git_has_doubledash && return
+
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ case "$cur" in
+ --*)
+ __gitcomp "--cached --pickaxe-all --pickaxe-regex
--base --ours --theirs
+ $__git_diff_common_options
"
return
;;
@@ -823,6 +866,8 @@ _git_format_patch ()
--not --all
--cover-letter
--no-prefix --src-prefix= --dst-prefix=
+ --inline --suffix= --ignore-if-in-upstream
+ --subject-prefix=
"
return
;;
@@ -930,6 +975,29 @@ _git_ls_tree ()
__git_complete_file
}
+# Options that go well for log, shortlog and gitk
+__git_log_common_options="
+ --not --all
+ --branches --tags --remotes
+ --first-parent --no-merges
+ --max-count=
+ --max-age= --since= --after=
+ --min-age= --until= --before=
+"
+# Options that go well for log and gitk (not shortlog)
+__git_log_gitk_options="
+ --dense --sparse --full-history
+ --simplify-merges --simplify-by-decoration
+ --left-right
+"
+# Options that go well for log and shortlog (not gitk)
+__git_log_shortlog_options="
+ --author= --committer= --grep=
+ --all-match
+"
+
+__git_log_pretty_formats="oneline short medium full fuller email raw format:"
+
_git_log ()
{
__git_has_doubledash && return
@@ -942,8 +1010,7 @@ _git_log ()
fi
case "$cur" in
--pretty=*)
- __gitcomp "
- oneline short medium full fuller email raw
+ __gitcomp "$__git_log_pretty_formats
" "" "${cur##--pretty=}"
return
;;
@@ -955,23 +1022,22 @@ _git_log ()
;;
--*)
__gitcomp "
- --max-count= --max-age= --since= --after=
- --min-age= --before= --until=
+ $__git_log_common_options
+ $__git_log_shortlog_options
+ $__git_log_gitk_options
--root --topo-order --date-order --reverse
- --no-merges --follow
+ --follow
--abbrev-commit --abbrev=
--relative-date --date=
- --author= --committer= --grep=
- --all-match
- --pretty= --name-status --name-only --raw
- --not --all
- --left-right --cherry-pick
+ --pretty=
+ --cherry-pick
--graph
- --stat --numstat --shortstat
- --decorate --diff-filter=
- --color-words --walk-reflogs
- --parents --children --full-history
+ --decorate
+ --walk-reflogs
+ --parents --children
$merge
+ $__git_diff_common_options
+ --pickaxe-all --pickaxe-regex
"
return
;;
@@ -995,6 +1061,7 @@ _git_merge ()
--*)
__gitcomp "
--no-commit --no-stat --log --no-log --squash --strategy
+ --commit --stat --no-squash --ff --no-ff
"
return
esac
@@ -1154,10 +1221,14 @@ _git_config ()
__gitcomp "$(__git_merge_strategies)"
return
;;
- color.branch|color.diff|color.status)
+ color.branch|color.diff|color.interactive|color.status|color.ui)
__gitcomp "always never auto"
return
;;
+ color.pager)
+ __gitcomp "false true"
+ return
+ ;;
color.*.*)
__gitcomp "
normal black red green yellow blue magenta cyan white
@@ -1372,7 +1443,7 @@ _git_config ()
_git_remote ()
{
- local subcommands="add rm show prune update"
+ local subcommands="add rename rm show prune update"
local subcommand="$(__git_find_subcommand "$subcommands")"
if [ -z "$subcommand" ]; then
__gitcomp "$subcommands"
@@ -1380,7 +1451,7 @@ _git_remote ()
fi
case "$subcommand" in
- rm|show|prune)
+ rename|rm|show|prune)
__gitcomp "$(__git_remotes)"
;;
update)
@@ -1408,7 +1479,7 @@ _git_reset ()
local cur="${COMP_WORDS[COMP_CWORD]}"
case "$cur" in
--*)
- __gitcomp "--mixed --hard --soft"
+ __gitcomp "--merge --mixed --hard --soft"
return
;;
esac
@@ -1449,12 +1520,8 @@ _git_shortlog ()
case "$cur" in
--*)
__gitcomp "
- --max-count= --max-age= --since= --after=
- --min-age= --before= --until=
- --no-merges
- --author= --committer= --grep=
- --all-match
- --not --all
+ $__git_log_common_options
+ $__git_log_shortlog_options
--numbered --summary
"
return
@@ -1470,13 +1537,14 @@ _git_show ()
local cur="${COMP_WORDS[COMP_CWORD]}"
case "$cur" in
--pretty=*)
- __gitcomp "
- oneline short medium full fuller email raw
+ __gitcomp "$__git_log_pretty_formats
" "" "${cur##--pretty=}"
return
;;
--*)
- __gitcomp "--pretty="
+ __gitcomp "--pretty=
+ $__git_diff_common_options
+ "
return
;;
esac
@@ -1552,7 +1620,8 @@ _git_svn ()
local subcommands="
init fetch clone rebase dcommit log find-rev
set-tree commit-diff info create-ignore propget
- proplist show-ignore show-externals
+ proplist show-ignore show-externals branch tag blame
+ migrate
"
local subcommand="$(__git_find_subcommand "$subcommands")"
if [ -z "$subcommand" ]; then
@@ -1563,13 +1632,15 @@ _git_svn ()
--follow-parent --authors-file= --repack=
--no-metadata --use-svm-props --use-svnsync-props
--log-window-size= --no-checkout --quiet
- --repack-flags --user-log-author $remote_opts
+ --repack-flags --use-log-author --localtime
+ --ignore-paths= $remote_opts
"
local init_opts="
--template= --shared= --trunk= --tags=
--branches= --stdlayout --minimize-url
--no-metadata --use-svm-props --use-svnsync-props
- --rewrite-root= $remote_opts
+ --rewrite-root= --prefix= --use-log-author
+ --add-author-from $remote_opts
"
local cmt_opts="
--edit --rmdir --find-copies-harder --copy-similarity=
@@ -1589,7 +1660,8 @@ _git_svn ()
dcommit,--*)
__gitcomp "
--merge --strategy= --verbose --dry-run
- --fetch-all --no-rebase $cmt_opts $fc_opts
+ --fetch-all --no-rebase --commit-url
+ --revision $cmt_opts $fc_opts
"
;;
set-tree,--*)
@@ -1603,13 +1675,13 @@ _git_svn ()
__gitcomp "
--limit= --revision= --verbose --incremental
--oneline --show-commit --non-recursive
- --authors-file=
+ --authors-file= --color
"
;;
rebase,--*)
__gitcomp "
--merge --verbose --strategy= --local
- --fetch-all $fc_opts
+ --fetch-all --dry-run $fc_opts
"
;;
commit-diff,--*)
@@ -1618,6 +1690,21 @@ _git_svn ()
info,--*)
__gitcomp "--url"
;;
+ branch,--*)
+ __gitcomp "--dry-run --message --tag"
+ ;;
+ tag,--*)
+ __gitcomp "--dry-run --message"
+ ;;
+ blame,--*)
+ __gitcomp "--git-format"
+ ;;
+ migrate,--*)
+ __gitcomp "
+ --config-dir= --ignore-paths= --minimize
+ --no-auth-cache --username=
+ "
+ ;;
*)
COMPREPLY=()
;;
@@ -1677,7 +1764,6 @@ _git ()
if [ -z "$command" ]; then
case "${COMP_WORDS[COMP_CWORD]}" in
- --*=*) COMPREPLY=() ;;
--*) __gitcomp "
--paginate
--no-pager
@@ -1741,6 +1827,7 @@ _git ()
show) _git_show ;;
show-branch) _git_show_branch ;;
stash) _git_stash ;;
+ stage) _git_add ;;
submodule) _git_submodule ;;
svn) _git_svn ;;
tag) _git_tag ;;
@@ -1761,20 +1848,27 @@ _gitk ()
fi
case "$cur" in
--*)
- __gitcomp "--not --all $merge"
+ __gitcomp "
+ $__git_log_common_options
+ $__git_log_gitk_options
+ $merge
+ "
return
;;
esac
__git_complete_revlist
}
-complete -o default -o nospace -F _git git
-complete -o default -o nospace -F _gitk gitk
+complete -o bashdefault -o default -o nospace -F _git git 2>/dev/null \
+ || complete -o default -o nospace -F _git git
+complete -o bashdefault -o default -o nospace -F _gitk gitk 2>/dev/null \
+ || complete -o default -o nospace -F _gitk gitk
# The following are necessary only for Cygwin, and only are needed
# when the user has tab-completed the executable name and consequently
# included the '.exe' suffix.
#
if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then
-complete -o default -o nospace -F _git git.exe
+complete -o bashdefault -o default -o nospace -F _git git.exe 2>/dev/null \
+ || complete -o default -o nospace -F _git git.exe
fi
diff --git a/contrib/difftool/git-difftool b/contrib/difftool/git-difftool
new file mode 100755
index 0000000000..0cda3d2eea
--- /dev/null
+++ b/contrib/difftool/git-difftool
@@ -0,0 +1,73 @@
+#!/usr/bin/env perl
+# Copyright (c) 2009 David Aguilar
+#
+# 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, and
+# GIT_DIFFTOOL_NO_PROMPT and GIT_MERGE_TOOL for use by git-difftool-helper.
+# Any arguments that are unknown to this script are forwarded to 'git diff'.
+
+use strict;
+use warnings;
+use Cwd qw(abs_path);
+use File::Basename qw(dirname);
+
+my $DIR = abs_path(dirname($0));
+
+
+sub usage
+{
+ print << 'USAGE';
+usage: git difftool [--tool=<tool>] [--no-prompt] ["git diff" options]
+USAGE
+ exit 1;
+}
+
+sub setup_environment
+{
+ $ENV{PATH} = "$DIR:$ENV{PATH}";
+ $ENV{GIT_PAGER} = '';
+ $ENV{GIT_EXTERNAL_DIFF} = 'git-difftool-helper';
+}
+
+sub exe
+{
+ my $exe = shift;
+ return defined $ENV{COMSPEC} ? "$exe.exe" : $exe;
+}
+
+sub generate_command
+{
+ my @command = (exe('git'), 'diff');
+ my $skip_next = 0;
+ my $idx = -1;
+ for my $arg (@ARGV) {
+ $idx++;
+ if ($skip_next) {
+ $skip_next = 0;
+ next;
+ }
+ if ($arg eq '-t' or $arg eq '--tool') {
+ usage() if $#ARGV <= $idx;
+ $ENV{GIT_MERGE_TOOL} = $ARGV[$idx + 1];
+ $skip_next = 1;
+ next;
+ }
+ if ($arg =~ /^--tool=/) {
+ $ENV{GIT_MERGE_TOOL} = substr($arg, 7);
+ next;
+ }
+ if ($arg eq '--no-prompt') {
+ $ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';
+ next;
+ }
+ if ($arg eq '-h' or $arg eq '--help') {
+ usage();
+ }
+ push @command, $arg;
+ }
+ return @command
+}
+
+setup_environment();
+exec(generate_command());
diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper
new file mode 100755
index 0000000000..db3af6a833
--- /dev/null
+++ b/contrib/difftool/git-difftool-helper
@@ -0,0 +1,240 @@
+#!/bin/sh
+# git-difftool-helper is a GIT_EXTERNAL_DIFF-compatible diff tool launcher.
+# It supports kdiff3, kompare, tkdiff, xxdiff, meld, opendiff,
+# emerge, ecmerge, vimdiff, gvimdiff, and custom user-configurable tools.
+# This script is typically launched by using the 'git difftool'
+# convenience command.
+#
+# Copyright (c) 2009 David Aguilar
+
+# Set GIT_DIFFTOOL_NO_PROMPT to bypass the per-file prompt.
+should_prompt () {
+ ! test -n "$GIT_DIFFTOOL_NO_PROMPT"
+}
+
+# Should we keep the backup .orig file?
+keep_backup_mode="$(git config --bool merge.keepBackup || echo true)"
+keep_backup () {
+ test "$keep_backup_mode" = "true"
+}
+
+# This function manages the backup .orig file.
+# A backup $MERGED.orig file is created if changes are detected.
+cleanup_temp_files () {
+ if test -n "$MERGED"; then
+ if keep_backup && test "$MERGED" -nt "$BACKUP"; then
+ test -f "$BACKUP" && mv -- "$BACKUP" "$MERGED.orig"
+ else
+ rm -f -- "$BACKUP"
+ fi
+ fi
+}
+
+# This is called when users Ctrl-C out of git-difftool-helper
+sigint_handler () {
+ cleanup_temp_files
+ exit 1
+}
+
+# This function prepares temporary files and launches the appropriate
+# merge tool.
+launch_merge_tool () {
+ # Merged is the filename as it appears in the work tree
+ # Local is the contents of a/filename
+ # Remote is the contents of b/filename
+ # Custom merge tool commands might use $BASE so we provide it
+ MERGED="$1"
+ LOCAL="$2"
+ REMOTE="$3"
+ BASE="$1"
+ ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')"
+ BACKUP="$MERGED.BACKUP.$ext"
+
+ # Create and ensure that we clean up $BACKUP
+ test -f "$MERGED" && cp -- "$MERGED" "$BACKUP"
+ trap sigint_handler INT
+
+ # $LOCAL and $REMOTE are temporary files so prompt
+ # the user with the real $MERGED name before launching $merge_tool.
+ if should_prompt; then
+ printf "\nViewing: '$MERGED'\n"
+ printf "Hit return to launch '%s': " "$merge_tool"
+ read ans
+ fi
+
+ # Run the appropriate merge tool command
+ case "$merge_tool" in
+ kdiff3)
+ basename=$(basename "$MERGED")
+ "$merge_tool_path" --auto \
+ --L1 "$basename (A)" \
+ --L2 "$basename (B)" \
+ -o "$MERGED" "$LOCAL" "$REMOTE" \
+ > /dev/null 2>&1
+ ;;
+
+ kompare)
+ "$merge_tool_path" "$LOCAL" "$REMOTE"
+ ;;
+
+ tkdiff)
+ "$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE"
+ ;;
+
+ meld)
+ "$merge_tool_path" "$LOCAL" "$REMOTE"
+ ;;
+
+ vimdiff)
+ "$merge_tool_path" -c "wincmd l" "$LOCAL" "$REMOTE"
+ ;;
+
+ gvimdiff)
+ "$merge_tool_path" -c "wincmd l" -f "$LOCAL" "$REMOTE"
+ ;;
+
+ xxdiff)
+ "$merge_tool_path" \
+ -X \
+ -R 'Accel.SaveAsMerged: "Ctrl-S"' \
+ -R 'Accel.Search: "Ctrl+F"' \
+ -R 'Accel.SearchForward: "Ctrl-G"' \
+ --merged-file "$MERGED" \
+ "$LOCAL" "$REMOTE"
+ ;;
+
+ opendiff)
+ "$merge_tool_path" "$LOCAL" "$REMOTE" \
+ -merge "$MERGED" | cat
+ ;;
+
+ ecmerge)
+ "$merge_tool_path" "$LOCAL" "$REMOTE" \
+ --default --mode=merge2 --to="$MERGED"
+ ;;
+
+ emerge)
+ "$merge_tool_path" -f emerge-files-command \
+ "$LOCAL" "$REMOTE" "$(basename "$MERGED")"
+ ;;
+
+ *)
+ if test -n "$merge_tool_cmd"; then
+ ( eval $merge_tool_cmd )
+ fi
+ ;;
+ esac
+
+ cleanup_temp_files
+}
+
+# Verifies that mergetool.<tool>.cmd exists
+valid_custom_tool() {
+ merge_tool_cmd="$(git config mergetool.$1.cmd)"
+ test -n "$merge_tool_cmd"
+}
+
+# Verifies that the chosen merge tool is properly setup.
+# Built-in merge tools are always valid.
+valid_tool() {
+ case "$1" in
+ kdiff3 | kompare | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge)
+ ;; # happy
+ *)
+ if ! valid_custom_tool "$1"
+ then
+ return 1
+ fi
+ ;;
+ esac
+}
+
+# Sets up the merge_tool_path variable.
+# This handles the mergetool.<tool>.path configuration.
+init_merge_tool_path() {
+ merge_tool_path=$(git config mergetool."$1".path)
+ if test -z "$merge_tool_path"; then
+ case "$1" in
+ emerge)
+ merge_tool_path=emacs
+ ;;
+ *)
+ merge_tool_path="$1"
+ ;;
+ esac
+ fi
+}
+
+# Allow the GIT_MERGE_TOOL variable to provide a default value
+test -n "$GIT_MERGE_TOOL" && merge_tool="$GIT_MERGE_TOOL"
+
+# If not merge tool was specified then use the merge.tool
+# configuration variable. If that's invalid then reset merge_tool.
+if test -z "$merge_tool"; then
+ merge_tool=$(git config merge.tool)
+ if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
+ echo >&2 "git config option merge.tool set to unknown tool: $merge_tool"
+ echo >&2 "Resetting to default..."
+ unset merge_tool
+ fi
+fi
+
+# Try to guess an appropriate merge tool if no tool has been set.
+if test -z "$merge_tool"; then
+ # We have a $DISPLAY so try some common UNIX merge tools
+ if test -n "$DISPLAY"; then
+ # If gnome then prefer meld, otherwise, prefer kdiff3 or kompare
+ if test -n "$GNOME_DESKTOP_SESSION_ID" ; then
+ merge_tool_candidates="meld kdiff3 kompare tkdiff xxdiff gvimdiff"
+ else
+ merge_tool_candidates="kdiff3 kompare tkdiff xxdiff meld gvimdiff"
+ fi
+ fi
+ if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then
+ # $EDITOR is emacs so add emerge as a candidate
+ merge_tool_candidates="$merge_tool_candidates emerge opendiff vimdiff"
+ elif echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then
+ # $EDITOR is vim so add vimdiff as a candidate
+ merge_tool_candidates="$merge_tool_candidates vimdiff opendiff emerge"
+ else
+ merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff"
+ fi
+ echo "merge tool candidates: $merge_tool_candidates"
+
+ # Loop over each candidate and stop when a valid merge tool is found.
+ for i in $merge_tool_candidates
+ do
+ init_merge_tool_path $i
+ if type "$merge_tool_path" > /dev/null 2>&1; then
+ merge_tool=$i
+ break
+ fi
+ done
+
+ if test -z "$merge_tool" ; then
+ echo "No known merge resolution program available."
+ exit 1
+ fi
+
+else
+ # A merge tool has been set, so verify that it's valid.
+ if ! valid_tool "$merge_tool"; then
+ echo >&2 "Unknown merge tool $merge_tool"
+ exit 1
+ fi
+
+ init_merge_tool_path "$merge_tool"
+
+ if test -z "$merge_tool_cmd" && ! type "$merge_tool_path" > /dev/null 2>&1; then
+ echo "The merge tool $merge_tool is not available as '$merge_tool_path'"
+ exit 1
+ fi
+fi
+
+
+# Launch the merge tool on each path provided by 'git diff'
+while test $# -gt 6
+do
+ launch_merge_tool "$1" "$2" "$5"
+ shift 7
+done
diff --git a/contrib/difftool/git-difftool.txt b/contrib/difftool/git-difftool.txt
new file mode 100644
index 0000000000..6e2610cda6
--- /dev/null
+++ b/contrib/difftool/git-difftool.txt
@@ -0,0 +1,105 @@
+git-difftool(1)
+===============
+
+NAME
+----
+git-difftool - compare changes using common merge tools
+
+SYNOPSIS
+--------
+'git difftool' [--tool=<tool>] [--no-prompt] ['git diff' options]
+
+DESCRIPTION
+-----------
+'git-difftool' is a git command that allows you to compare and edit files
+between revisions using common merge tools. At its most basic level,
+'git-difftool' does what 'git-mergetool' does but its use is for non-merge
+situations such as when preparing commits or comparing changes against
+the index.
+
+'git difftool' is a frontend to 'git diff' and accepts the same
+arguments and options.
+
+See linkgit:git-diff[1] for the full list of supported options.
+
+OPTIONS
+-------
+-t <tool>::
+--tool=<tool>::
+ Use the merge resolution program specified by <tool>.
+ Valid merge tools are:
+ kdiff3, kompare, tkdiff, meld, xxdiff, emerge,
+ vimdiff, gvimdiff, ecmerge, and opendiff
++
+If a merge resolution program is not specified, 'git-difftool'
+will use the configuration variable `merge.tool`. If the
+configuration variable `merge.tool` is not set, 'git difftool'
+will pick a suitable default.
++
+You can explicitly provide a full path to the tool by setting the
+configuration variable `mergetool.<tool>.path`. For example, you
+can configure the absolute path to kdiff3 by setting
+`mergetool.kdiff3.path`. Otherwise, 'git-difftool' assumes the
+tool is available in PATH.
++
+Instead of running one of the known merge tool programs,
+'git-difftool' can be customized to run an alternative program
+by specifying the command line to invoke in a configuration
+variable `mergetool.<tool>.cmd`.
++
+When 'git-difftool' is invoked with this tool (either through the
+`-t` or `--tool` option or the `merge.tool` configuration variable)
+the configured command line will be invoked with the following
+variables available: `$LOCAL` is set to the name of the temporary
+file containing the contents of the diff pre-image and `$REMOTE`
+is set to the name of the temporary file containing the contents
+of the diff post-image. `$BASE` is provided for compatibility
+with custom merge tool commands and has the same value as `$LOCAL`.
+
+--no-prompt::
+ Do not prompt before launching a diff tool.
+
+CONFIG VARIABLES
+----------------
+merge.tool::
+ The default merge tool to use.
++
+See the `--tool=<tool>` option above for more details.
+
+merge.keepBackup::
+ The original, unedited file content can be saved to a file with
+ a `.orig` extension. Defaults to `true` (i.e. keep the backup files).
+
+mergetool.<tool>.path::
+ Override the path for the given tool. This is useful in case
+ your tool is not in the PATH.
+
+mergetool.<tool>.cmd::
+ Specify the command to invoke the specified merge tool.
++
+See the `--tool=<tool>` option above for more details.
+
+
+SEE ALSO
+--------
+linkgit:git-diff[1]::
+ Show changes between commits, commit and working tree, etc
+
+linkgit:git-mergetool[1]::
+ Run merge conflict resolution tools to resolve merge conflicts
+
+linkgit:git-config[1]::
+ Get and set repository or global options
+
+
+AUTHOR
+------
+Written by David Aguilar <davvid@gmail.com>.
+
+Documentation
+--------------
+Documentation by David Aguilar and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/contrib/emacs/Makefile b/contrib/emacs/Makefile
index a48540a92b..24d9312941 100644
--- a/contrib/emacs/Makefile
+++ b/contrib/emacs/Makefile
@@ -2,7 +2,7 @@
EMACS = emacs
-ELC = git.elc vc-git.elc git-blame.elc
+ELC = git.elc git-blame.elc
INSTALL ?= install
INSTALL_ELC = $(INSTALL) -m 644
prefix ?= $(HOME)
diff --git a/contrib/emacs/README b/contrib/emacs/README
new file mode 100644
index 0000000000..82368bdbff
--- /dev/null
+++ b/contrib/emacs/README
@@ -0,0 +1,39 @@
+This directory contains 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:
+
+ (add-to-list 'load-path ".../git/contrib/emacs")
+ (require 'git)
+ (require 'git-blame)
+
+
+The following modules are available:
+
+* 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'.
+
+* 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.
+
+* vc-git.el:
+
+ This file used to contain the VC-mode backend for git, but it is no
+ longer distributed with git. It is now maintained as part of Emacs
+ and included in standard Emacs distributions starting from version
+ 22.2.
+
+ If you have an earlier Emacs version, upgrading to Emacs 22 is
+ recommended, since the VC mode in older Emacs is not generic enough
+ to be able to support git in a reasonable manner, and no attempt has
+ been made to backport vc-git.el.
diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el
index 09e8bae3a4..eace9c18eb 100644
--- a/contrib/emacs/git.el
+++ b/contrib/emacs/git.el
@@ -1,6 +1,6 @@
;;; git.el --- A user interface for git
-;; Copyright (C) 2005, 2006, 2007 Alexandre Julliard <julliard@winehq.org>
+;; Copyright (C) 2005, 2006, 2007, 2008, 2009 Alexandre Julliard <julliard@winehq.org>
;; Version: 1.0
@@ -34,15 +34,21 @@
;; To start: `M-x git-status'
;;
;; TODO
-;; - portability to XEmacs
;; - diff against other branch
;; - renaming files from the status buffer
;; - creating tags
;; - fetch/pull
-;; - switching branches
;; - revlist browser
;; - git-show-branch browser
-;; - menus
+;;
+
+;;; 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))
@@ -222,7 +228,7 @@ the process output as a string, or nil if the git command failed."
(with-current-buffer buffer
(cd dir)
(apply #'call-process-region start end program
- nil (list output-buffer nil) nil args))))
+ 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."
@@ -239,13 +245,15 @@ the process output as a string, or nil if the git command failed."
(defun git-run-command-region (buffer start end env &rest args)
"Run a git command with specified buffer region as input."
- (unless (eq 0 (if env
- (git-run-process-region
- buffer start end "env"
- (append (git-get-env-strings env) (list "git") args))
+ (with-temp-buffer
+ (if (eq 0 (if env
(git-run-process-region
- buffer start end "git" args)))
- (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string))))
+ 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."
@@ -397,6 +405,17 @@ the process output as a string, or nil if the git command failed."
(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
@@ -447,18 +466,16 @@ the process output as a string, or nil if the git command failed."
(setq coding-system-for-write buffer-file-coding-system))
(let ((commit
(git-get-string-sha1
- (with-output-to-string
- (with-current-buffer standard-output
- (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))))))))
- (and (git-update-ref "HEAD" commit head subject)
- commit))))
+ (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 head subject))
+ commit)))
(defun git-empty-db-p ()
"Check if the git db is empty (no commit done yet)."
@@ -513,9 +530,9 @@ the process output as a string, or nil if the git command failed."
(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."
+ "Apply FUNC to the status files names in the FILES list.
+The list must be sorted."
(when files
- (setq files (sort files #'string-lessp))
(let ((file (pop files))
(node (ewoc-nth status 0)))
(while (and file node)
@@ -528,7 +545,7 @@ the process output as a string, or nil if the git command failed."
(setq file (pop files))))))))
(defun git-set-filenames-state (status files state)
- "Set the state of a list of named files."
+ "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
@@ -562,29 +579,29 @@ the process output as a string, or nil if the git command failed."
(let* ((old-type (lsh (or old-perm 0) -9))
(new-type (lsh (or new-perm 0) -9))
(str (case new-type
- (?\100 ;; file
+ (64 ;; file
(case old-type
- (?\100 nil)
- (?\120 " (type change symlink -> file)")
- (?\160 " (type change subproject -> file)")))
- (?\120 ;; symlink
+ (64 nil)
+ (80 " (type change symlink -> file)")
+ (112 " (type change subproject -> file)")))
+ (80 ;; symlink
(case old-type
- (?\100 " (type change file -> symlink)")
- (?\160 " (type change subproject -> symlink)")
+ (64 " (type change file -> symlink)")
+ (112 " (type change subproject -> symlink)")
(t " (symlink)")))
- (?\160 ;; subproject
+ (112 ;; subproject
(case old-type
- (?\100 " (type change file -> subproject)")
- (?\120 " (type change symlink -> subproject)")
+ (64 " (type change file -> subproject)")
+ (80 " (type change symlink -> subproject)")
(t " (subproject)")))
- (?\110 nil) ;; directory (internal, not a real git state)
- (?\000 ;; deleted or unknown
+ (72 nil) ;; directory (internal, not a real git state)
+ (0 ;; deleted or unknown
(case old-type
- (?\120 " (symlink)")
- (?\160 " (subproject)")))
+ (80 " (symlink)")
+ (112 " (subproject)")))
(t (format " (unknown type %o)" new-type)))))
(cond (str (propertize str 'face 'git-status-face))
- ((eq new-type ?\110) "/")
+ ((eq new-type 72) "/")
(t ""))))
(defun git-rename-as-string (info)
@@ -733,6 +750,7 @@ Return the list of files that haven't been handled."
(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 ()
@@ -753,17 +771,18 @@ Return the list of files that haven't been handled."
(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."
+ "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 (setq files (sort files #'string-lessp))
+ (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
+ (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))))
@@ -808,13 +827,13 @@ Return the list of files that haven't been handled."
(list (ewoc-data (ewoc-locate git-status)))))
(defun git-marked-files-state (&rest states)
- "Return marked files that are in the specified 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)))
- result))
+ (nreverse result)))
(defun git-refresh-files ()
"Refresh all files that need it and clear the needs-refresh flag."
@@ -1049,7 +1068,9 @@ Return the list of files that haven't been handled."
(unless files
(push (file-relative-name (read-file-name "File to remove: " nil nil t)) files))
(if (yes-or-no-p
- (format "Remove %d file%s? " (length files) (if (> (length files) 1) "s" "")))
+ (if (cdr files)
+ (format "Remove %d files? " (length files))
+ (format "Remove %s? " (car files))))
(progn
(dolist (name files)
(ignore-errors
@@ -1068,7 +1089,9 @@ Return the list of files that haven't been handled."
added modified)
(when (and files
(yes-or-no-p
- (format "Revert %d file%s? " (length files) (if (> (length files) 1) "s" ""))))
+ (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))
@@ -1084,13 +1107,14 @@ Return the list of files that haven't been handled."
(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)))))
- (git-update-status-files (append added 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" (git-get-filenames files)))))))
+ (git-success-message "Reverted" names))))))
(defun git-resolve-file ()
"Resolve conflicts in marked file(s)."
@@ -1320,6 +1344,7 @@ Return the list of files that haven't been handled."
(log-edit-diff-function . git-log-edit-diff)) buffer)
(log-edit 'git-do-commit nil 'git-log-edit-files buffer))
(setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords))
+ (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))))
@@ -1347,14 +1372,44 @@ Return the list of files that haven't been handled."
(mapconcat #'identity msg "\n"))))
(defun git-get-commit-files (commit)
- "Retrieve the list of files modified by 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)))
- 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
@@ -1372,6 +1427,44 @@ amended version of it."
(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)
@@ -1471,6 +1564,10 @@ amended version of it."
(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)
@@ -1491,6 +1588,10 @@ amended version of it."
`("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]
diff --git a/contrib/emacs/vc-git.el b/contrib/emacs/vc-git.el
deleted file mode 100644
index b8f6be5c0a..0000000000
--- a/contrib/emacs/vc-git.el
+++ /dev/null
@@ -1,216 +0,0 @@
-;;; vc-git.el --- VC backend for the git version control system
-
-;; Copyright (C) 2006 Alexandre Julliard
-
-;; 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, write to the Free
-;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
-;; MA 02111-1307 USA
-
-;;; Commentary:
-
-;; This file contains a VC backend for the git version control
-;; system.
-;;
-;; To install: put this file on the load-path and add GIT to the list
-;; of supported backends in `vc-handled-backends'; the following line,
-;; placed in your ~/.emacs, will accomplish this:
-;;
-;; (add-to-list 'vc-handled-backends 'GIT)
-;;
-;; TODO
-;; - changelog generation
-;; - working with revisions other than HEAD
-;;
-
-(eval-when-compile (require 'cl))
-
-(defvar git-commits-coding-system 'utf-8
- "Default coding system for git commits.")
-
-(defun vc-git--run-command-string (file &rest args)
- "Run a git command on FILE and return its output as string."
- (let* ((ok t)
- (str (with-output-to-string
- (with-current-buffer standard-output
- (unless (eq 0 (apply #'call-process "git" nil '(t nil) nil
- (append args (list (file-relative-name file)))))
- (setq ok nil))))))
- (and ok str)))
-
-(defun vc-git--run-command (file &rest args)
- "Run a git command on FILE, discarding any output."
- (let ((name (file-relative-name file)))
- (eq 0 (apply #'call-process "git" nil (get-buffer "*Messages") nil (append args (list name))))))
-
-(defun vc-git-registered (file)
- "Check whether FILE is registered with git."
- (with-temp-buffer
- (let* ((dir (file-name-directory file))
- (name (file-relative-name file dir)))
- (and (ignore-errors
- (when dir (cd dir))
- (eq 0 (call-process "git" nil '(t nil) nil "ls-files" "-c" "-z" "--" name)))
- (let ((str (buffer-string)))
- (and (> (length str) (length name))
- (string= (substring str 0 (1+ (length name))) (concat name "\0"))))))))
-
-(defun vc-git-state (file)
- "git-specific version of `vc-state'."
- (let ((diff (vc-git--run-command-string file "diff-index" "-z" "HEAD" "--")))
- (if (and diff (string-match ":[0-7]\\{6\\} [0-7]\\{6\\} [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} [ADMU]\0[^\0]+\0" diff))
- 'edited
- 'up-to-date)))
-
-(defun vc-git-workfile-version (file)
- "git-specific version of `vc-workfile-version'."
- (let ((str (with-output-to-string
- (with-current-buffer standard-output
- (call-process "git" nil '(t nil) nil "symbolic-ref" "HEAD")))))
- (if (string-match "^\\(refs/heads/\\)?\\(.+\\)$" str)
- (match-string 2 str)
- str)))
-
-(defun vc-git-symbolic-commit (commit)
- "Translate COMMIT string into symbolic form.
-Returns nil if not possible."
- (and commit
- (with-temp-buffer
- (and
- (zerop
- (call-process "git" nil '(t nil) nil "name-rev"
- "--name-only" "--tags"
- commit))
- (goto-char (point-min))
- (= (forward-line 2) 1)
- (bolp)
- (buffer-substring-no-properties (point-min) (1- (point-max)))))))
-
-(defun vc-git-previous-version (file rev)
- "git-specific version of `vc-previous-version'."
- (let ((default-directory (file-name-directory (expand-file-name file)))
- (file (file-name-nondirectory file)))
- (vc-git-symbolic-commit
- (with-temp-buffer
- (and
- (zerop
- (call-process "git" nil '(t nil) nil "rev-list"
- "-2" rev "--" file))
- (goto-char (point-max))
- (bolp)
- (zerop (forward-line -1))
- (not (bobp))
- (buffer-substring-no-properties
- (point)
- (1- (point-max))))))))
-
-(defun vc-git-next-version (file rev)
- "git-specific version of `vc-next-version'."
- (let* ((default-directory (file-name-directory
- (expand-file-name file)))
- (file (file-name-nondirectory file))
- (current-rev
- (with-temp-buffer
- (and
- (zerop
- (call-process "git" nil '(t nil) nil "rev-list"
- "-1" rev "--" file))
- (goto-char (point-max))
- (bolp)
- (zerop (forward-line -1))
- (bobp)
- (buffer-substring-no-properties
- (point)
- (1- (point-max)))))))
- (and current-rev
- (vc-git-symbolic-commit
- (with-temp-buffer
- (and
- (zerop
- (call-process "git" nil '(t nil) nil "rev-list"
- "HEAD" "--" file))
- (goto-char (point-min))
- (search-forward current-rev nil t)
- (zerop (forward-line -1))
- (buffer-substring-no-properties
- (point)
- (progn (forward-line 1) (1- (point))))))))))
-
-(defun vc-git-revert (file &optional contents-done)
- "Revert FILE to the version stored in the git repository."
- (if contents-done
- (vc-git--run-command file "update-index" "--")
- (vc-git--run-command file "checkout" "HEAD")))
-
-(defun vc-git-checkout-model (file)
- 'implicit)
-
-(defun vc-git-workfile-unchanged-p (file)
- (let ((sha1 (vc-git--run-command-string file "hash-object" "--"))
- (head (vc-git--run-command-string file "ls-tree" "-z" "HEAD" "--")))
- (and head
- (string-match "[0-7]\\{6\\} blob \\([0-9a-f]\\{40\\}\\)\t[^\0]+\0" head)
- (string= (car (split-string sha1 "\n")) (match-string 1 head)))))
-
-(defun vc-git-register (file &optional rev comment)
- "Register FILE into the git version-control system."
- (vc-git--run-command file "update-index" "--add" "--"))
-
-(defun vc-git-print-log (file &optional buffer)
- (let ((name (file-relative-name file))
- (coding-system-for-read git-commits-coding-system))
- (vc-do-command buffer 'async "git" name "rev-list" "--pretty" "HEAD" "--")))
-
-(defun vc-git-diff (file &optional rev1 rev2 buffer)
- (let ((name (file-relative-name file))
- (buf (or buffer "*vc-diff*")))
- (if (and rev1 rev2)
- (vc-do-command buf 0 "git" name "diff-tree" "-p" rev1 rev2 "--")
- (vc-do-command buf 0 "git" name "diff-index" "-p" (or rev1 "HEAD") "--"))
- ; git-diff-index doesn't set exit status like diff does
- (if (vc-git-workfile-unchanged-p file) 0 1)))
-
-(defun vc-git-checkin (file rev comment)
- (let ((coding-system-for-write git-commits-coding-system))
- (vc-git--run-command file "commit" "-m" comment "--only" "--")))
-
-(defun vc-git-checkout (file &optional editable rev destfile)
- (if destfile
- (let ((fullname (substring
- (vc-git--run-command-string file "ls-files" "-z" "--full-name" "--")
- 0 -1))
- (coding-system-for-read 'no-conversion)
- (coding-system-for-write 'no-conversion))
- (with-temp-file destfile
- (eq 0 (call-process "git" nil t nil "cat-file" "blob"
- (concat (or rev "HEAD") ":" fullname)))))
- (vc-git--run-command file "checkout" (or rev "HEAD"))))
-
-(defun vc-git-annotate-command (file buf &optional rev)
- ; FIXME: rev is ignored
- (let ((name (file-relative-name file)))
- (call-process "git" nil buf nil "blame" name)))
-
-(defun vc-git-annotate-time ()
- (and (re-search-forward "[0-9a-f]+ (.* \\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\) \\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\) \\([-+0-9]+\\) +[0-9]+)" nil t)
- (vc-annotate-convert-time
- (apply #'encode-time (mapcar (lambda (match) (string-to-number (match-string match))) '(6 5 4 3 2 1 7))))))
-
-;; Not really useful since we can't do anything with the revision yet
-;;(defun vc-annotate-extract-revision-at-line ()
-;; (save-excursion
-;; (move-beginning-of-line 1)
-;; (and (looking-at "[0-9a-f]+")
-;; (buffer-substring (match-beginning 0) (match-end 0)))))
-
-(provide 'vc-git)
diff --git a/contrib/examples/git-svnimport.perl b/contrib/examples/git-svnimport.perl
index a13bb6afec..4576c4a862 100755
--- a/contrib/examples/git-svnimport.perl
+++ b/contrib/examples/git-svnimport.perl
@@ -287,9 +287,9 @@ my $last_rev = "";
my $last_branch;
my $current_rev = $opt_s || 1;
unless(-d $git_dir) {
- system("git-init");
+ system("git init");
die "Cannot init the GIT db at $git_tree: $?\n" if $?;
- system("git-read-tree");
+ system("git read-tree");
die "Cannot init an empty tree: $?\n" if $?;
$last_branch = $opt_o;
@@ -303,7 +303,7 @@ unless(-d $git_dir) {
-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
+ open(F, "git symbolic-ref HEAD |") or
die "Cannot run git-symbolic-ref: $!\n";
chomp ($last_branch = <F>);
$last_branch = basename($last_branch);
@@ -331,7 +331,7 @@ EOM
"$git_dir/refs/heads/$opt_o") == 0;
# populate index
- system('git-read-tree', $last_rev);
+ system('git', 'read-tree', $last_rev);
die "read-tree failed: $?\n" if $?;
# Get the last import timestamps
@@ -399,7 +399,7 @@ sub get_file($$$) {
my $pid = open(my $F, '-|');
die $! unless defined $pid;
if (!$pid) {
- exec("git-hash-object", "-w", $name)
+ exec("git", "hash-object", "-w", $name)
or die "Cannot create object: $!\n";
}
my $sha = <$F>;
@@ -423,7 +423,7 @@ sub get_ignore($$$$$) {
my $pid = open(my $F, '-|');
die $! unless defined $pid;
if (!$pid) {
- exec("git-hash-object", "-w", $name)
+ exec("git", "hash-object", "-w", $name)
or die "Cannot create object: $!\n";
}
my $sha = <$F>;
@@ -547,7 +547,7 @@ sub copy_path($$$$$$$$) {
my $pid = open my $f,'-|';
die $! unless defined $pid;
if (!$pid) {
- exec("git-ls-tree","-r","-z",$gitrev,$srcpath)
+ exec("git","ls-tree","-r","-z",$gitrev,$srcpath)
or die $!;
}
local $/ = "\0";
@@ -634,7 +634,7 @@ sub commit {
my $rev;
if($revision > $opt_s and defined $parent) {
- open(H,'-|',"git-rev-parse","--verify",$parent);
+ open(H,'-|',"git","rev-parse","--verify",$parent);
$rev = <H>;
close(H) or do {
print STDERR "$revision: cannot find commit '$parent'!\n";
@@ -671,7 +671,7 @@ sub commit {
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);
+ system("git", "read-tree", $rev);
die "read-tree failed for $rev: $?\n" if $?;
$last_rev = $rev;
}
@@ -740,7 +740,7 @@ sub commit {
my $pid = open my $F, "-|";
die "$!" unless defined $pid;
if (!$pid) {
- exec("git-ls-files", "-z", @o1) or die $!;
+ exec("git", "ls-files", "-z", @o1) or die $!;
}
@o1 = ();
local $/ = "\0";
@@ -758,7 +758,7 @@ sub commit {
@o2 = @o1;
@o1 = ();
}
- system("git-update-index","--force-remove","--",@o2);
+ system("git","update-index","--force-remove","--",@o2);
die "Cannot remove files: $?\n" if $?;
}
}
@@ -770,7 +770,7 @@ sub commit {
@n2 = @new;
@new = ();
}
- system("git-update-index","--add",
+ system("git","update-index","--add",
(map { ('--cacheinfo', @$_) } @n2));
die "Cannot add files: $?\n" if $?;
}
@@ -778,7 +778,7 @@ sub commit {
my $pid = open(C,"-|");
die "Cannot fork: $!" unless defined $pid;
unless($pid) {
- exec("git-write-tree");
+ exec("git","write-tree");
die "Cannot exec git-write-tree: $!\n";
}
chomp(my $tree = <C>);
@@ -830,7 +830,7 @@ sub commit {
"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);
+ "git", "commit-tree", $tree,@par);
die "Cannot exec git-commit-tree: $!\n";
}
$pw->writer();
@@ -874,7 +874,7 @@ sub commit {
$dest =~ tr/_/\./ if $opt_u;
- system('git-tag', '-f', $dest, $cid) == 0
+ system('git', 'tag', '-f', $dest, $cid) == 0
or die "Cannot create tag $dest: $!\n";
print "Created tag '$dest' on '$branch'\n" if $opt_v;
@@ -937,7 +937,7 @@ while ($to_rev < $opt_l) {
my $pid = fork();
die "Fork: $!\n" unless defined $pid;
unless($pid) {
- exec("git-repack", "-d")
+ exec("git", "repack", "-d")
or die "Cannot repack: $!\n";
}
waitpid($pid, 0);
@@ -958,7 +958,7 @@ if($orig_branch) {
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');
+ system('git', 'read-tree', '-m', '-u', 'SVN2GIT_HEAD', 'HEAD');
die "read-tree failed: $?\n" if $?;
}
} else {
@@ -966,7 +966,7 @@ if($orig_branch) {
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");
+ system('git', 'update-ref', 'HEAD', "$orig_branch");
unless ($opt_i) {
system('git checkout');
die "checkout failed: $?\n" if $?;
diff --git a/contrib/examples/git-svnimport.txt b/contrib/examples/git-svnimport.txt
index 71aad8b45b..3bb871e42f 100644
--- a/contrib/examples/git-svnimport.txt
+++ b/contrib/examples/git-svnimport.txt
@@ -114,9 +114,9 @@ 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 import in chunks of 1000
-revisions, after each chunk git repository will be repacked. To disable
-this behavior specify some big value here which is mote than number of
+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>::
diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4
index a85a7b2a58..3832f60225 100755
--- a/contrib/fast-import/git-p4
+++ b/contrib/fast-import/git-p4
@@ -442,13 +442,14 @@ def p4ChangesForPaths(depotPaths, changeRange):
output = p4_read_pipe_lines("changes " + ' '.join (["%s...%s" % (p, changeRange)
for p in depotPaths]))
- changes = []
+ changes = {}
for line in output:
- changeNum = line.split(" ")[1]
- changes.append(int(changeNum))
+ changeNum = int(line.split(" ")[1])
+ changes[changeNum] = True
- changes.sort()
- return changes
+ changelist = changes.keys()
+ changelist.sort()
+ return changelist
class Command:
def __init__(self):
diff --git a/contrib/git-resurrect.sh b/contrib/git-resurrect.sh
new file mode 100755
index 0000000000..c364dda696
--- /dev/null
+++ b/contrib/git-resurrect.sh
@@ -0,0 +1,180 @@
+#!/bin/sh
+
+USAGE="[-a] [-r] [-m] [-t] [-n] [-b <newname>] <name>"
+LONG_USAGE="git-resurrect attempts to find traces of a branch tip
+called <name>, and tries to resurrect it. Currently, the reflog is
+searched for checkout messages, and with -r also merge messages. With
+-m and -t, the history of all refs is scanned for Merge <name> into
+other/Merge <other> into <name> (respectively) commit subjects, which
+is rather slow but allows you to resurrect other people's topic
+branches."
+
+OPTIONS_SPEC="\
+git resurrect $USAGE
+--
+b,branch= save branch as <newname> instead of <name>
+a,all same as -l -r -m -t
+k,keep-going full rev-list scan (instead of first match)
+l,reflog scan reflog for checkouts (enabled by default)
+r,reflog-merges scan for merges recorded in reflog
+m,merges scan for merges into other branches (slow)
+t,merge-targets scan for merges of other branches into <name>
+n,dry-run don't recreate the branch"
+
+. git-sh-setup
+
+search_reflog () {
+ sed -ne 's~^\([^ ]*\) .*\tcheckout: moving from '"$1"' .*~\1~p' \
+ < "$GIT_DIR"/logs/HEAD
+}
+
+search_reflog_merges () {
+ git rev-parse $(
+ sed -ne 's~^[^ ]* \([^ ]*\) .*\tmerge '"$1"':.*~\1^2~p' \
+ < "$GIT_DIR"/logs/HEAD
+ )
+}
+
+_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"
+
+search_merges () {
+ git rev-list --all --grep="Merge branch '$1'" \
+ --pretty=tformat:"%P %s" |
+ sed -ne "/^$_x40 \($_x40\) Merge .*/ {s//\1/p;$early_exit}"
+}
+
+search_merge_targets () {
+ git rev-list --all --grep="Merge branch '[^']*' into $branch\$" \
+ --pretty=tformat:"%H %s" --all |
+ sed -ne "/^\($_x40\) Merge .*/ {s//\1/p;$early_exit} "
+}
+
+dry_run=
+early_exit=q
+scan_reflog=t
+scan_reflog_merges=
+scan_merges=
+scan_merge_targets=
+new_name=
+
+while test "$#" != 0; do
+ case "$1" in
+ -b|--branch)
+ shift
+ new_name="$1"
+ ;;
+ -n|--dry-run)
+ dry_run=t
+ ;;
+ --no-dry-run)
+ dry_run=
+ ;;
+ -k|--keep-going)
+ early_exit=
+ ;;
+ --no-keep-going)
+ early_exit=q
+ ;;
+ -m|--merges)
+ scan_merges=t
+ ;;
+ --no-merges)
+ scan_merges=
+ ;;
+ -l|--reflog)
+ scan_reflog=t
+ ;;
+ --no-reflog)
+ scan_reflog=
+ ;;
+ -r|--reflog_merges)
+ scan_reflog_merges=t
+ ;;
+ --no-reflog_merges)
+ scan_reflog_merges=
+ ;;
+ -t|--merge-targets)
+ scan_merge_targets=t
+ ;;
+ --no-merge-targets)
+ scan_merge_targets=
+ ;;
+ -a|--all)
+ scan_reflog=t
+ scan_reflog_merges=t
+ scan_merges=t
+ scan_merge_targets=t
+ ;;
+ --)
+ shift
+ break
+ ;;
+ *)
+ usage
+ ;;
+ esac
+ shift
+done
+
+test "$#" = 1 || usage
+
+all_strategies="$scan_reflog$scan_reflog_merges$scan_merges$scan_merge_targets"
+if test -z "$all_strategies"; then
+ die "must enable at least one of -lrmt"
+fi
+
+branch="$1"
+test -z "$new_name" && new_name="$branch"
+
+if test ! -z "$scan_reflog"; then
+ if test -r "$GIT_DIR"/logs/HEAD; then
+ candidates="$(search_reflog $branch)"
+ else
+ die 'reflog scanning requested, but' \
+ '$GIT_DIR/logs/HEAD not readable'
+ fi
+fi
+if test ! -z "$scan_reflog_merges"; then
+ if test -r "$GIT_DIR"/logs/HEAD; then
+ candidates="$candidates $(search_reflog_merges $branch)"
+ else
+ die 'reflog scanning requested, but' \
+ '$GIT_DIR/logs/HEAD not readable'
+ fi
+fi
+if test ! -z "$scan_merges"; then
+ candidates="$candidates $(search_merges $branch)"
+fi
+if test ! -z "$scan_merge_targets"; then
+ candidates="$candidates $(search_merge_targets $branch)"
+fi
+
+candidates="$(git rev-parse $candidates | sort -u)"
+
+if test -z "$candidates"; then
+ hint=
+ test "z$all_strategies" != "ztttt" \
+ && hint=" (maybe try again with -a)"
+ die "no candidates for $branch found$hint"
+fi
+
+echo "** Candidates for $branch **"
+for cmt in $candidates; do
+ git --no-pager log --pretty=tformat:"%ct:%h [%cr] %s" --abbrev-commit -1 $cmt
+done \
+| sort -n | cut -d: -f2-
+
+newest="$(git rev-list -1 $candidates)"
+if test ! -z "$dry_run"; then
+ printf "** Most recent: "
+ git --no-pager log -1 --pretty=tformat:"%h %s" $newest
+elif ! git rev-parse --verify --quiet $new_name >/dev/null; then
+ printf "** Restoring $new_name to "
+ git --no-pager log -1 --pretty=tformat:"%h %s" $newest
+ git branch $new_name $newest
+else
+ printf "Most recent: "
+ git --no-pager log -1 --pretty=tformat:"%h %s" $newest
+ echo "** $new_name already exists, doing nothing"
+fi
diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email
index 28a3c0e46e..60cbab65d3 100644
--- a/contrib/hooks/post-receive-email
+++ b/contrib/hooks/post-receive-email
@@ -615,7 +615,9 @@ show_new_revisions()
revspec=$oldrev..$newrev
fi
- git rev-parse --not --branches | grep -v $(git rev-parse $refname) |
+ other_branches=$(git for-each-ref --format='%(refname)' refs/heads/ |
+ grep -F -v $refname)
+ git rev-parse --not $other_branches |
if [ -z "$custom_showrev" ]
then
git rev-list --pretty --stdin $revspec
diff --git a/contrib/vim/README b/contrib/vim/README
index c487346eba..fca1e17251 100644
--- a/contrib/vim/README
+++ b/contrib/vim/README
@@ -5,11 +5,13 @@ automatically.
If you have an older version of vim, you can get the latest syntax
files from the vim project:
- http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/git.vim
- http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/gitcommit.vim
- http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/gitconfig.vim
- http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/gitrebase.vim
- http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/gitsendemail.vim
+ http://ftp.vim.org/pub/vim/runtime/syntax/git.vim
+ http://ftp.vim.org/pub/vim/runtime/syntax/gitcommit.vim
+ http://ftp.vim.org/pub/vim/runtime/syntax/gitconfig.vim
+ http://ftp.vim.org/pub/vim/runtime/syntax/gitrebase.vim
+ http://ftp.vim.org/pub/vim/runtime/syntax/gitsendemail.vim
+
+These files are also available via FTP at the same location.
To install: