diff options
Diffstat (limited to 'contrib')
-rwxr-xr-x | contrib/completion/git-completion.bash | 59 | ||||
-rw-r--r-- | contrib/diff-highlight/README | 109 | ||||
-rwxr-xr-x | contrib/diff-highlight/diff-highlight | 109 | ||||
-rw-r--r-- | contrib/diffall/README | 31 | ||||
-rwxr-xr-x | contrib/diffall/git-diffall | 257 | ||||
-rwxr-xr-x | contrib/fast-import/git-p4 | 217 | ||||
-rwxr-xr-x | contrib/hooks/post-receive-email | 7 |
7 files changed, 671 insertions, 118 deletions
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 99fea1db28..31f714da92 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -60,18 +60,6 @@ # per-repository basis by setting the bash.showUpstream config # variable. # -# -# To submit patches: -# -# *) Read Documentation/SubmittingPatches -# *) Send all patches to the current maintainer: -# -# "Shawn O. Pearce" <spearce@spearce.org> -# -# *) Always CC the Git mailing list: -# -# git@vger.kernel.org -# if [[ -n ${ZSH_VERSION-} ]]; then autoload -U +X bashcompinit && bashcompinit @@ -150,7 +138,7 @@ __git_ps1_show_upstream () svn_upstream=${svn_upstream[ ${#svn_upstream[@]} - 2 ]} svn_upstream=${svn_upstream%@*} local n_stop="${#svn_remote[@]}" - for ((n=1; n <= n_stop; ++n)); do + for ((n=1; n <= n_stop; n++)); do svn_upstream=${svn_upstream#${svn_remote[$n]}} done @@ -179,10 +167,8 @@ __git_ps1_show_upstream () for commit in $commits do case "$commit" in - "<"*) let ++behind - ;; - *) let ++ahead - ;; + "<"*) ((behind++)) ;; + *) ((ahead++)) ;; esac done count="$behind $ahead" @@ -299,13 +285,13 @@ __git_ps1 () fi fi if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ]; then - git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$" + git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$" fi if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ]; then - if [ -n "$(git ls-files --others --exclude-standard)" ]; then - u="%" - fi + if [ -n "$(git ls-files --others --exclude-standard)" ]; then + u="%" + fi fi if [ -n "${GIT_PS1_SHOWUPSTREAM-}" ]; then @@ -314,7 +300,7 @@ __git_ps1 () fi local f="$w$i$s$u" - printf "${1:- (%s)}" "$c${b##refs/heads/}${f:+ $f}$r$p" + printf -- "${1:- (%s)}" "$c${b##refs/heads/}${f:+ $f}$r$p" fi } @@ -739,6 +725,9 @@ __git_complete_remote_or_refspec () { local cur_="$cur" cmd="${words[1]}" local i c=2 remote="" pfx="" lhs=1 no_complete_refspec=0 + if [ "$cmd" = "remote" ]; then + ((c++)) + fi while [ $c -lt $cword ]; do i="${words[c]}" case "$i" in @@ -756,7 +745,7 @@ __git_complete_remote_or_refspec () -*) ;; *) remote="$i"; break ;; esac - c=$((++c)) + ((c++)) done if [ -z "$remote" ]; then __gitcomp_nl "$(__git_remotes)" @@ -789,7 +778,7 @@ __git_complete_remote_or_refspec () __gitcomp_nl "$(__git_refs)" "$pfx" "$cur_" fi ;; - pull) + pull|remote) if [ $lhs = 1 ]; then __gitcomp_nl "$(__git_refs "$remote")" "$pfx" "$cur_" else @@ -996,7 +985,7 @@ __git_find_on_cmdline () return fi done - c=$((++c)) + ((c++)) done } @@ -1007,7 +996,7 @@ __git_has_doubledash () if [ "--" = "${words[c]}" ]; then return 0 fi - c=$((++c)) + ((c++)) done return 1 } @@ -1130,7 +1119,7 @@ _git_branch () -d|-m) only_local_ref="y" ;; -r) has_r="y" ;; esac - c=$((++c)) + ((c++)) done case "$cur" in @@ -1138,7 +1127,7 @@ _git_branch () __gitcomp " --color --no-color --verbose --abbrev= --no-abbrev --track --no-track --contains --merged --no-merged - --set-upstream --edit-description + --set-upstream --edit-description --list " ;; *) @@ -2104,6 +2093,7 @@ _git_config () core.whitespace core.worktree diff.autorefreshindex + diff.statGraphWidth diff.external diff.ignoreSubmodules diff.mnemonicprefix @@ -2290,7 +2280,7 @@ _git_config () _git_remote () { - local subcommands="add rename rm show prune update set-head" + local subcommands="add rename rm set-head set-branches set-url show prune update" local subcommand="$(__git_find_on_cmdline "$subcommands")" if [ -z "$subcommand" ]; then __gitcomp "$subcommands" @@ -2298,9 +2288,12 @@ _git_remote () fi case "$subcommand" in - rename|rm|show|prune) + rename|rm|set-url|show|prune) __gitcomp_nl "$(__git_remotes)" ;; + set-head|set-branches) + __git_complete_remote_or_refspec + ;; update) local i c='' IFS=$'\n' for i in $(git --git-dir="$(__gitdir)" config --get-regexp "remotes\..*" 2>/dev/null); do @@ -2513,7 +2506,7 @@ _git_svn () __gitcomp " --merge --strategy= --verbose --dry-run --fetch-all --no-rebase --commit-url - --revision $cmt_opts $fc_opts + --revision --interactive $cmt_opts $fc_opts " ;; set-tree,--*) @@ -2581,7 +2574,7 @@ _git_tag () f=1 ;; esac - c=$((++c)) + ((c++)) done case "$prev" in @@ -2634,7 +2627,7 @@ _git () --help) command="help"; break ;; *) command="$i"; break ;; esac - c=$((++c)) + ((c++)) done if [ -z "$command" ]; then diff --git a/contrib/diff-highlight/README b/contrib/diff-highlight/README index 1b7b6df8eb..502e03b305 100644 --- a/contrib/diff-highlight/README +++ b/contrib/diff-highlight/README @@ -14,13 +14,15 @@ Instead, this script post-processes the line-oriented diff, finds pairs of lines, and highlights the differing segments. It's currently very simple and stupid about doing these tasks. In particular: - 1. It will only highlight a pair of lines if they are the only two - lines in a hunk. It could instead try to match up "before" and - "after" lines for a given hunk into pairs of similar lines. - However, this may end up visually distracting, as the paired - lines would have other highlighted lines in between them. And in - practice, the lines which most need attention called to their - small, hard-to-see changes are touching only a single line. + 1. It will only highlight hunks in which the number of removed and + added lines is the same, and it will pair lines within the hunk by + position (so the first removed line is compared to the first added + line, and so forth). This is simple and tends to work well in + practice. More complex changes don't highlight well, so we tend to + exclude them due to the "same number of removed and added lines" + restriction. Or even if we do try to highlight them, they end up + not highlighting because of our "don't highlight if the whole line + would be highlighted" rule. 2. It will find the common prefix and suffix of two lines, and consider everything in the middle to be "different". It could @@ -55,3 +57,96 @@ following in your git configuration: show = diff-highlight | less diff = diff-highlight | less --------------------------------------------- + +Bugs +---- + +Because diff-highlight relies on heuristics to guess which parts of +changes are important, there are some cases where the highlighting is +more distracting than useful. Fortunately, these cases are rare in +practice, and when they do occur, the worst case is simply a little +extra highlighting. This section documents some cases known to be +sub-optimal, in case somebody feels like working on improving the +heuristics. + +1. Two changes on the same line get highlighted in a blob. For example, + highlighting: + +---------------------------------------------- +-foo(buf, size); ++foo(obj->buf, obj->size); +---------------------------------------------- + + yields (where the inside of "+{}" would be highlighted): + +---------------------------------------------- +-foo(buf, size); ++foo(+{obj->buf, obj->}size); +---------------------------------------------- + + whereas a more semantically meaningful output would be: + +---------------------------------------------- +-foo(buf, size); ++foo(+{obj->}buf, +{obj->}size); +---------------------------------------------- + + Note that doing this right would probably involve a set of + content-specific boundary patterns, similar to word-diff. Otherwise + you get junk like: + +----------------------------------------------------- +-this line has some -{i}nt-{ere}sti-{ng} text on it ++this line has some +{fa}nt+{a}sti+{c} text on it +----------------------------------------------------- + + which is less readable than the current output. + +2. The multi-line matching assumes that lines in the pre- and post-image + match by position. This is often the case, but can be fooled when a + line is removed from the top and a new one added at the bottom (or + vice versa). Unless the lines in the middle are also changed, diffs + will show this as two hunks, and it will not get highlighted at all + (which is good). But if the lines in the middle are changed, the + highlighting can be misleading. Here's a pathological case: + +----------------------------------------------------- +-one +-two +-three +-four ++two 2 ++three 3 ++four 4 ++five 5 +----------------------------------------------------- + + which gets highlighted as: + +----------------------------------------------------- +-one +-t-{wo} +-three +-f-{our} ++two 2 ++t+{hree 3} ++four 4 ++f+{ive 5} +----------------------------------------------------- + + because it matches "two" to "three 3", and so forth. It would be + nicer as: + +----------------------------------------------------- +-one +-two +-three +-four ++two +{2} ++three +{3} ++four +{4} ++five 5 +----------------------------------------------------- + + which would probably involve pre-matching the lines into pairs + according to some heuristic. diff --git a/contrib/diff-highlight/diff-highlight b/contrib/diff-highlight/diff-highlight index d8938982e4..c4404d49c9 100755 --- a/contrib/diff-highlight/diff-highlight +++ b/contrib/diff-highlight/diff-highlight @@ -1,28 +1,37 @@ #!/usr/bin/perl +use warnings FATAL => 'all'; +use strict; + # Highlight by reversing foreground and background. You could do # other things like bold or underline if you prefer. my $HIGHLIGHT = "\x1b[7m"; my $UNHIGHLIGHT = "\x1b[27m"; my $COLOR = qr/\x1b\[[0-9;]*m/; +my $BORING = qr/$COLOR|\s/; -my @window; +my @removed; +my @added; +my $in_hunk; while (<>) { - # We highlight only single-line changes, so we need - # a 4-line window to make a decision on whether - # to highlight. - push @window, $_; - next if @window < 4; - if ($window[0] =~ /^$COLOR*(\@| )/ && - $window[1] =~ /^$COLOR*-/ && - $window[2] =~ /^$COLOR*\+/ && - $window[3] !~ /^$COLOR*\+/) { - print shift @window; - show_pair(shift @window, shift @window); + if (!$in_hunk) { + print; + $in_hunk = /^$COLOR*\@/; + } + elsif (/^$COLOR*-/) { + push @removed, $_; + } + elsif (/^$COLOR*\+/) { + push @added, $_; } else { - print shift @window; + show_hunk(\@removed, \@added); + @removed = (); + @added = (); + + print; + $in_hunk = /^$COLOR*[\@ ]/; } # Most of the time there is enough output to keep things streaming, @@ -38,23 +47,40 @@ while (<>) { } } -# Special case a single-line hunk at the end of file. -if (@window == 3 && - $window[0] =~ /^$COLOR*(\@| )/ && - $window[1] =~ /^$COLOR*-/ && - $window[2] =~ /^$COLOR*\+/) { - print shift @window; - show_pair(shift @window, shift @window); -} - -# And then flush any remaining lines. -while (@window) { - print shift @window; -} +# Flush any queued hunk (this can happen when there is no trailing context in +# the final diff of the input). +show_hunk(\@removed, \@added); exit 0; -sub show_pair { +sub show_hunk { + my ($a, $b) = @_; + + # If one side is empty, then there is nothing to compare or highlight. + if (!@$a || !@$b) { + print @$a, @$b; + return; + } + + # If we have mismatched numbers of lines on each side, we could try to + # be clever and match up similar lines. But for now we are simple and + # stupid, and only handle multi-line hunks that remove and add the same + # number of lines. + if (@$a != @$b) { + print @$a, @$b; + return; + } + + my @queue; + for (my $i = 0; $i < @$a; $i++) { + my ($rm, $add) = highlight_pair($a->[$i], $b->[$i]); + print $rm; + push @queue, $add; + } + print @queue; +} + +sub highlight_pair { my @a = split_line(shift); my @b = split_line(shift); @@ -101,8 +127,14 @@ sub show_pair { } } - print highlight(\@a, $pa, $sa); - print highlight(\@b, $pb, $sb); + if (is_pair_interesting(\@a, $pa, $sa, \@b, $pb, $sb)) { + return highlight_line(\@a, $pa, $sa), + highlight_line(\@b, $pb, $sb); + } + else { + return join('', @a), + join('', @b); + } } sub split_line { @@ -111,7 +143,7 @@ sub split_line { split /($COLOR*)/; } -sub highlight { +sub highlight_line { my ($line, $prefix, $suffix) = @_; return join('', @@ -122,3 +154,20 @@ sub highlight { @{$line}[($suffix+1)..$#$line] ); } + +# Pairs are interesting to highlight only if we are going to end up +# highlighting a subset (i.e., not the whole line). Otherwise, the highlighting +# is just useless noise. We can detect this by finding either a matching prefix +# or suffix (disregarding boring bits like whitespace and colorization). +sub is_pair_interesting { + my ($a, $pa, $sa, $b, $pb, $sb) = @_; + my $prefix_a = join('', @$a[0..($pa-1)]); + my $prefix_b = join('', @$b[0..($pb-1)]); + my $suffix_a = join('', @$a[($sa+1)..$#$a]); + my $suffix_b = join('', @$b[($sb+1)..$#$b]); + + return $prefix_a !~ /^$COLOR*-$BORING*$/ || + $prefix_b !~ /^$COLOR*\+$BORING*$/ || + $suffix_a !~ /^$BORING*$/ || + $suffix_b !~ /^$BORING*$/; +} diff --git a/contrib/diffall/README b/contrib/diffall/README new file mode 100644 index 0000000000..507f17dcd6 --- /dev/null +++ b/contrib/diffall/README @@ -0,0 +1,31 @@ +The git-diffall script provides a directory based diff mechanism +for git. + +To determine what diff viewer is used, the script requires either +the 'diff.tool' or 'merge.tool' configuration option to be set. + +This script is compatible with most common forms used to specify a +range of revisions to diff: + + 1. git diffall: shows diff between working tree and staged changes + 2. git diffall --cached [<commit>]: shows diff between staged + changes and HEAD (or other named commit) + 3. git diffall <commit>: shows diff between working tree and named + commit + 4. git diffall <commit> <commit>: show diff between two named commits + 5. git diffall <commit>..<commit>: same as above + 6. git diffall <commit>...<commit>: show the changes on the branch + containing and up to the second, starting at a common ancestor + of both <commit> + +Note: all forms take an optional path limiter [-- <path>*] + +The '--extcmd=<command>' option allows the user to specify a custom +command for viewing diffs. When given, configured defaults are +ignored and the script runs $command $LOCAL $REMOTE. Additionally, +$BASE is set in the environment. + +This script is based on an example provided by Thomas Rast on the +Git list [1]: + +[1] http://thread.gmane.org/gmane.comp.version-control.git/124807 diff --git a/contrib/diffall/git-diffall b/contrib/diffall/git-diffall new file mode 100755 index 0000000000..84f2b654d7 --- /dev/null +++ b/contrib/diffall/git-diffall @@ -0,0 +1,257 @@ +#!/bin/sh +# Copyright 2010 - 2012, Tim Henigan <tim.henigan@gmail.com> +# +# Perform a directory diff between commits in the repository using +# the external diff or merge tool specified in the user's config. + +USAGE='[--cached] [--copy-back] [-x|--extcmd=<command>] <commit>{0,2} [-- <path>*] + + --cached Compare to the index rather than the working tree. + + --copy-back Copy files back to the working tree when the diff + tool exits (in case they were modified by the + user). This option is only valid if the diff + compared with the working tree. + + -x=<command> + --extcmd=<command> Specify a custom command for viewing diffs. + git-diffall ignores the configured defaults and + runs $command $LOCAL $REMOTE when this option is + specified. Additionally, $BASE is set in the + environment. +' + +SUBDIRECTORY_OK=1 +. "$(git --exec-path)/git-sh-setup" + +TOOL_MODE=diff +. "$(git --exec-path)/git-mergetool--lib" + +merge_tool="$(get_merge_tool)" +if test -z "$merge_tool" +then + echo "Error: Either the 'diff.tool' or 'merge.tool' option must be set." + usage +fi + +start_dir=$(pwd) + +# All the file paths returned by the diff command are relative to the root +# of the working copy. So if the script is called from a subdirectory, it +# must switch to the root of working copy before trying to use those paths. +cdup=$(git rev-parse --show-cdup) && +cd "$cdup" || { + echo >&2 "Cannot chdir to $cdup, the toplevel of the working tree" + exit 1 +} + +# set up temp dir +tmp=$(perl -e 'use File::Temp qw(tempdir); + $t=tempdir("/tmp/git-diffall.XXXXX") or exit(1); + print $t') || exit 1 +trap 'rm -rf "$tmp"' EXIT + +left= +right= +paths= +dashdash_seen= +compare_staged= +merge_base= +left_dir= +right_dir= +diff_tool= +copy_back= + +while test $# != 0 +do + case "$1" in + -h|--h|--he|--hel|--help) + usage + ;; + --cached) + compare_staged=1 + ;; + --copy-back) + copy_back=1 + ;; + -x|--e|--ex|--ext|--extc|--extcm|--extcmd) + if test $# = 1 + then + echo You must specify the tool for use with --extcmd + usage + else + diff_tool=$2 + shift + fi + ;; + --) + dashdash_seen=1 + ;; + -*) + echo Invalid option: "$1" + usage + ;; + *) + # could be commit, commit range or path limiter + case "$1" in + *...*) + left=${1%...*} + right=${1#*...} + merge_base=1 + ;; + *..*) + left=${1%..*} + right=${1#*..} + ;; + *) + if test -n "$dashdash_seen" + then + paths="$paths$1 " + elif test -z "$left" + then + left=$1 + elif test -z "$right" + then + right=$1 + else + paths="$paths$1 " + fi + ;; + esac + ;; + esac + shift +done + +# Determine the set of files which changed +if test -n "$left" && test -n "$right" +then + left_dir="cmt-$(git rev-parse --short $left)" + right_dir="cmt-$(git rev-parse --short $right)" + + if test -n "$compare_staged" + then + usage + elif test -n "$merge_base" + then + git diff --name-only "$left"..."$right" -- $paths >"$tmp/filelist" + else + git diff --name-only "$left" "$right" -- $paths >"$tmp/filelist" + fi +elif test -n "$left" +then + left_dir="cmt-$(git rev-parse --short $left)" + + if test -n "$compare_staged" + then + right_dir="staged" + git diff --name-only --cached "$left" -- $paths >"$tmp/filelist" + else + right_dir="working_tree" + git diff --name-only "$left" -- $paths >"$tmp/filelist" + fi +else + left_dir="HEAD" + + if test -n "$compare_staged" + then + right_dir="staged" + git diff --name-only --cached -- $paths >"$tmp/filelist" + else + right_dir="working_tree" + git diff --name-only -- $paths >"$tmp/filelist" + fi +fi + +# Exit immediately if there are no diffs +if test ! -s "$tmp/filelist" +then + exit 0 +fi + +if test -n "$copy_back" && test "$right_dir" != "working_tree" +then + echo "--copy-back is only valid when diff includes the working tree." + exit 1 +fi + +# Create the named tmp directories that will hold the files to be compared +mkdir -p "$tmp/$left_dir" "$tmp/$right_dir" + +# Populate the tmp/right_dir directory with the files to be compared +while read name +do + if test -n "$right" + then + ls_list=$(git ls-tree $right "$name") + if test -n "$ls_list" + then + mkdir -p "$tmp/$right_dir/$(dirname "$name")" + git show "$right":"$name" >"$tmp/$right_dir/$name" || true + fi + elif test -n "$compare_staged" + then + ls_list=$(git ls-files -- "$name") + if test -n "$ls_list" + then + mkdir -p "$tmp/$right_dir/$(dirname "$name")" + git show :"$name" >"$tmp/$right_dir/$name" + fi + else + if test -e "$name" + then + mkdir -p "$tmp/$right_dir/$(dirname "$name")" + cp "$name" "$tmp/$right_dir/$name" + fi + fi +done < "$tmp/filelist" + +# Populate the tmp/left_dir directory with the files to be compared +while read name +do + if test -n "$left" + then + ls_list=$(git ls-tree $left "$name") + if test -n "$ls_list" + then + mkdir -p "$tmp/$left_dir/$(dirname "$name")" + git show "$left":"$name" >"$tmp/$left_dir/$name" || true + fi + else + if test -n "$compare_staged" + then + ls_list=$(git ls-tree HEAD "$name") + if test -n "$ls_list" + then + mkdir -p "$tmp/$left_dir/$(dirname "$name")" + git show HEAD:"$name" >"$tmp/$left_dir/$name" + fi + else + mkdir -p "$tmp/$left_dir/$(dirname "$name")" + git show :"$name" >"$tmp/$left_dir/$name" + fi + fi +done < "$tmp/filelist" + +LOCAL="$tmp/$left_dir" +REMOTE="$tmp/$right_dir" + +if test -n "$diff_tool" +then + export BASE + eval $diff_tool '"$LOCAL"' '"$REMOTE"' +else + run_merge_tool "$merge_tool" false +fi + +# Copy files back to the working dir, if requested +if test -n "$copy_back" && test "$right_dir" = "working_tree" +then + cd "$start_dir" + git_top_dir=$(git rev-parse --show-toplevel) + find "$tmp/$right_dir" -type f | + while read file + do + cp "$file" "$git_top_dir/${file#$tmp/$right_dir/}" + done +fi diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index a78d9c5493..c5362c4c11 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -10,7 +10,7 @@ import optparse, sys, os, marshal, subprocess, shelve import tempfile, getopt, os.path, time, platform -import re +import re, shutil verbose = False @@ -38,7 +38,7 @@ def p4_build_cmd(cmd): host = gitConfig("git-p4.host") if len(host) > 0: - real_cmd += ["-h", host] + real_cmd += ["-H", host] client = gitConfig("git-p4.client") if len(client) > 0: @@ -186,6 +186,47 @@ def split_p4_type(p4type): mods = s[1] return (base, mods) +# +# return the raw p4 type of a file (text, text+ko, etc) +# +def p4_type(file): + results = p4CmdList(["fstat", "-T", "headType", file]) + return results[0]['headType'] + +# +# Given a type base and modifier, return a regexp matching +# the keywords that can be expanded in the file +# +def p4_keywords_regexp_for_type(base, type_mods): + if base in ("text", "unicode", "binary"): + kwords = None + if "ko" in type_mods: + kwords = 'Id|Header' + elif "k" in type_mods: + kwords = 'Id|Header|Author|Date|DateTime|Change|File|Revision' + else: + return None + pattern = r""" + \$ # Starts with a dollar, followed by... + (%s) # one of the keywords, followed by... + (:[^$]+)? # possibly an old expansion, followed by... + \$ # another dollar + """ % kwords + return pattern + else: + return None + +# +# Given a file, return a regexp matching the possible +# RCS keywords that will be expanded, or None for files +# with kw expansion turned off. +# +def p4_keywords_regexp_for_file(file): + if not os.path.exists(file): + return None + else: + (type_base, type_mods) = split_p4_type(p4_type(file)) + return p4_keywords_regexp_for_type(type_base, type_mods) def setP4ExecBit(file, mode): # Reopens an already open file and changes the execute bit to match @@ -555,6 +596,46 @@ def p4PathStartsWith(path, prefix): return path.lower().startswith(prefix.lower()) return path.startswith(prefix) +def getClientSpec(): + """Look at the p4 client spec, create a View() object that contains + all the mappings, and return it.""" + + specList = p4CmdList("client -o") + if len(specList) != 1: + die('Output from "client -o" is %d lines, expecting 1' % + len(specList)) + + # dictionary of all client parameters + entry = specList[0] + + # just the keys that start with "View" + view_keys = [ k for k in entry.keys() if k.startswith("View") ] + + # hold this new View + view = View() + + # append the lines, in order, to the view + for view_num in range(len(view_keys)): + k = "View%d" % view_num + if k not in view_keys: + die("Expected view key %s missing" % k) + view.append(entry[k]) + + return view + +def getClientRoot(): + """Grab the client directory.""" + + output = p4CmdList("client -o") + if len(output) != 1: + die('Output from "client -o" is %d lines, expecting 1' % len(output)) + + entry = output[0] + if "Root" not in entry: + die('Client has no "Root"') + + return entry["Root"] + class Command: def __init__(self): self.usage = "usage: %prog [options]" @@ -753,6 +834,29 @@ class P4Submit(Command, P4UserMap): return result + def patchRCSKeywords(self, file, pattern): + # Attempt to zap the RCS keywords in a p4 controlled file matching the given pattern + (handle, outFileName) = tempfile.mkstemp(dir='.') + try: + outFile = os.fdopen(handle, "w+") + inFile = open(file, "r") + regexp = re.compile(pattern, re.VERBOSE) + for line in inFile.readlines(): + line = regexp.sub(r'$\1$', line) + outFile.write(line) + inFile.close() + outFile.close() + # Forcibly overwrite the original file + os.unlink(file) + shutil.move(outFileName, file) + except: + # cleanup our temporary file + os.unlink(outFileName) + print "Failed to strip RCS keywords in %s" % file + raise + + print "Patched up RCS keywords in %s" % file + def p4UserForCommit(self,id): # Return the tuple (perforce user,git email) for a given git commit id self.getUserMapFromPerforceServer() @@ -918,6 +1022,7 @@ class P4Submit(Command, P4UserMap): filesToDelete = set() editedFiles = set() filesToChangeExecBit = {} + for line in diff: diff = parseDiffTreeEntry(line) modifier = diff['status'] @@ -964,9 +1069,45 @@ class P4Submit(Command, P4UserMap): patchcmd = diffcmd + " | git apply " tryPatchCmd = patchcmd + "--check -" applyPatchCmd = patchcmd + "--check --apply -" + patch_succeeded = True if os.system(tryPatchCmd) != 0: + fixed_rcs_keywords = False + patch_succeeded = False print "Unfortunately applying the change failed!" + + # Patch failed, maybe it's just RCS keyword woes. Look through + # the patch to see if that's possible. + if gitConfig("git-p4.attemptRCSCleanup","--bool") == "true": + file = None + pattern = None + kwfiles = {} + for file in editedFiles | filesToDelete: + # did this file's delta contain RCS keywords? + pattern = p4_keywords_regexp_for_file(file) + + if pattern: + # this file is a possibility...look for RCS keywords. + regexp = re.compile(pattern, re.VERBOSE) + for line in read_pipe_lines(["git", "diff", "%s^..%s" % (id, id), file]): + if regexp.search(line): + if verbose: + print "got keyword match on %s in %s in %s" % (pattern, line, file) + kwfiles[file] = pattern + break + + for file in kwfiles: + if verbose: + print "zapping %s with %s" % (line,pattern) + self.patchRCSKeywords(file, kwfiles[file]) + fixed_rcs_keywords = True + + if fixed_rcs_keywords: + print "Retrying the patch with RCS keywords cleaned up" + if os.system(tryPatchCmd) == 0: + patch_succeeded = True + + if not patch_succeeded: print "What do you want to do?" response = "x" while response != "s" and response != "a" and response != "w": @@ -1119,11 +1260,20 @@ class P4Submit(Command, P4UserMap): print "Internal error: cannot locate perforce depot path from existing branches" sys.exit(128) - self.clientPath = p4Where(self.depotPath) + self.useClientSpec = False + if gitConfig("git-p4.useclientspec", "--bool") == "true": + self.useClientSpec = True + if self.useClientSpec: + self.clientSpecDirs = getClientSpec() - if len(self.clientPath) == 0: - print "Error: Cannot locate perforce checkout of %s in client view" % self.depotPath - sys.exit(128) + if self.useClientSpec: + # all files are relative to the client spec + self.clientPath = getClientRoot() + else: + self.clientPath = p4Where(self.depotPath) + + if self.clientPath == "": + die("Error: Cannot locate perforce checkout of %s in client view" % self.depotPath) print "Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath) self.oldWorkingDirectory = os.getcwd() @@ -1429,6 +1579,7 @@ class P4Sync(Command, P4UserMap): self.p4BranchesInGit = [] self.cloneExclude = [] self.useClientSpec = False + self.useClientSpec_from_options = False self.clientSpecDirs = None self.tempBranches = [] self.tempBranchLocation = "git-p4-tmp" @@ -1585,15 +1736,12 @@ class P4Sync(Command, P4UserMap): # Note that we do not try to de-mangle keywords on utf16 files, # even though in theory somebody may want that. - if type_base in ("text", "unicode", "binary"): - if "ko" in type_mods: - text = ''.join(contents) - text = re.sub(r'\$(Id|Header):[^$]*\$', r'$\1$', text) - contents = [ text ] - elif "k" in type_mods: - text = ''.join(contents) - text = re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$', r'$\1$', text) - contents = [ text ] + pattern = p4_keywords_regexp_for_type(type_base, type_mods) + if pattern: + regexp = re.compile(pattern, re.VERBOSE) + text = ''.join(contents) + text = regexp.sub(r'$\1$', text) + contents = [ text ] self.gitStream.write("M %s inline %s\n" % (git_mode, relPath)) @@ -2125,33 +2273,6 @@ class P4Sync(Command, P4UserMap): print self.gitError.read() - def getClientSpec(self): - specList = p4CmdList("client -o") - if len(specList) != 1: - die('Output from "client -o" is %d lines, expecting 1' % - len(specList)) - - # dictionary of all client parameters - entry = specList[0] - - # just the keys that start with "View" - view_keys = [ k for k in entry.keys() if k.startswith("View") ] - - # hold this new View - view = View() - - # append the lines, in order, to the view - for view_num in range(len(view_keys)): - k = "View%d" % view_num - if k not in view_keys: - die("Expected view key %s missing" % k) - view.append(entry[k]) - - self.clientSpecDirs = view - if self.verbose: - for i, m in enumerate(self.clientSpecDirs.mappings): - print "clientSpecDirs %d: %s" % (i, str(m)) - def run(self, args): self.depotPaths = [] self.changeRange = "" @@ -2184,11 +2305,15 @@ class P4Sync(Command, P4UserMap): if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes and gitBranchExists(self.branch): system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch)) - if not self.useClientSpec: + # accept either the command-line option, or the configuration variable + if self.useClientSpec: + # will use this after clone to set the variable + self.useClientSpec_from_options = True + else: if gitConfig("git-p4.useclientspec", "--bool") == "true": self.useClientSpec = True if self.useClientSpec: - self.getClientSpec() + self.clientSpecDirs = getClientSpec() # TODO: should always look at previous commits, # merge with previous imports, if possible. @@ -2509,6 +2634,10 @@ class P4Clone(P4Sync): else: print "Could not detect main branch. No checkout/master branch created." + # auto-set this variable if invoked with --use-client-spec + if self.useClientSpec_from_options: + system("git config --bool git-p4.useclientspec true") + return True class P4Branches(Command): diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index ba077c13f9..01af9df15e 100755 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -85,7 +85,6 @@ prep_for_email() oldrev=$(git rev-parse $1) newrev=$(git rev-parse $2) refname="$3" - maxlines=$4 # --- Interpret # 0000->1234 (create) @@ -461,7 +460,7 @@ generate_delete_branch_email() { echo " was $oldrev" echo "" - echo $LOGEND + echo $LOGBEGIN git show -s --pretty=oneline $oldrev echo $LOGEND } @@ -561,7 +560,7 @@ generate_delete_atag_email() { echo " was $oldrev" echo "" - echo $LOGEND + echo $LOGBEGIN git show -s --pretty=oneline $oldrev echo $LOGEND } @@ -626,7 +625,7 @@ generate_delete_general_email() { echo " was $oldrev" echo "" - echo $LOGEND + echo $LOGBEGIN git show -s --pretty=oneline $oldrev echo $LOGEND } |