Git development
 help / color / mirror / Atom feed
* Re: [PATCHv3] daemon: give friendlier error messages to clients
From: Jonathan Nieder @ 2011-10-15  8:26 UTC (permalink / raw)
  To: Jakub Narebski
  Cc: Sitaram Chamarty, Junio C Hamano, Jeff King, Nguyen Thai Ngoc Duy,
	git, Ilari Liusvaara, Johannes Sixt
In-Reply-To: <m3r52e7js7.fsf@localhost.localdomain>

Jakub Narebski wrote:
> Sitaram Chamarty <sitaramc@gmail.com> writes:

>> "Access denied or no such repo" is much better.  (The "not exported"
>> nuance is not relevant in this context; you can safely ignore it.)
>
> To join this bike-shedding:
>
>   "Access denied or repository not available"
>
> or just
>
>   "Repository not available"

If such details about the message matter, then I have to say that
Sitaram's "access denied or no such repository" is as close to perfect
as I can imagine.  The admin who is eventually forwarded this message
will be reminded to check two things:

 - that access is not denied, neither globally, using a whitelist,
   using filesystem permissions, nor by leaving out
   git-daemon-export-ok, and

 - that such a repo exists at all, and there was not a typo in the
   address.

^ permalink raw reply

* [PATCH] send-email: Honour SMTP domain when using TLS
From: Matthew Daley @ 2011-10-15  8:44 UTC (permalink / raw)
  To: git; +Cc: Matthew Daley

git-send-email sends two SMTP EHLOs when using TLS encryption, however
only the first, unencrypted EHLO uses the SMTP domain that can be
optionally specified by the user (--smtp-domain).  This is because the
call to hello() that produces the second, encrypted EHLO does not pass
the SMTP domain as an argument, and hence a default of
'localhost.localdomain' is used instead.

Fix by passing in the SMTP domain in this call.

Signed-off-by: Matthew Daley <mattjd@gmail.com>
---
 git-send-email.perl |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/git-send-email.perl b/git-send-email.perl
index 6885dfa..d491db9 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -1098,7 +1098,7 @@ X-Mailer: git-send-email $gitversion
 					$smtp_encryption = '';
 					# Send EHLO again to receive fresh
 					# supported commands
-					$smtp->hello();
+					$smtp->hello($smtp_domain);
 				} else {
 					die "Server does not support STARTTLS! ".$smtp->message;
 				}
-- 
1.7.2.5

^ permalink raw reply related

* Re: [PATCH 7/8] mergetools: use the correct tool for Beyond Compare 3 on Windows
From: Sebastian Schuberth @ 2011-10-15 10:27 UTC (permalink / raw)
  To: git; +Cc: msysgit
In-Reply-To: <7vobxix0pk.fsf@alter.siamese.dyndns.org>

On 15.10.2011 07:50, Junio C Hamano wrote:

> Hmm, does this only apply to Windows, or are there other platforms on
> which BC3 supplies bcomp for the exact same reason? What I am trying to

BC3 is only available for Linux and Windows, so it only applies to 
Windows currently.

-- 
Sebastian Schuberth

^ permalink raw reply

* Re: [PATCH 5/8] t9901: fix line-ending dependency on windows
From: Pat Thoyts @ 2011-10-15 12:25 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git, msysGit
In-Reply-To: <7vsjmux0z3.fsf@alter.siamese.dyndns.org>

Junio C Hamano <gitster@pobox.com> writes:

>Pat Thoyts <patthoyts@users.sourceforge.net> writes:
>
>> Signed-off-by: Pat Thoyts <patthoyts@users.sourceforge.net>
>> ---
>>  t/t9901-git-web--browse.sh |   10 +++++-----
>>  1 files changed, 5 insertions(+), 5 deletions(-)
>>
>> diff --git a/t/t9901-git-web--browse.sh b/t/t9901-git-web--browse.sh
>> index 7906e5d..1185b42 100755
>> --- a/t/t9901-git-web--browse.sh
>> +++ b/t/t9901-git-web--browse.sh
>> @@ -12,7 +12,7 @@ test_expect_success \
>>  	echo http://example.com/foo\&bar >expect &&
>>  	git config browser.custom.cmd echo &&
>>  	git web--browse --browser=custom \
>> -		http://example.com/foo\&bar >actual &&
>> +		http://example.com/foo\&bar | tr -d "\r" >actual &&
>>  	test_cmp expect actual
>>  '
>
>This is unnice for two reasons. We have web--browse five times in this
>test script, and you add tr exactly the same way to each and every one of
>them. And you are losing the error condition from each and every one of
>these web--browse invocations.
>
>Having to do the same change to all invocations of the same command
>suggests that perhaps you can and should wrap that pattern into a single
>helper, perhaps like:
>
>test_web_browse () {
>	# browser=$1 url=$2
>	git web--browse --browser="$1" "$2" >actual &&
>        tr -d '\015' <actual >text &&
>        test_cmp expect text
>}
>
>or something?

OK - The suggested code works fine so I shall re-roll this one shortly.
-- 
Pat Thoyts                            http://www.patthoyts.tk/
PGP fingerprint 2C 6E 98 07 2C 59 C8 97  10 CE 11 E6 04 E0 B9 DD

^ permalink raw reply

* Re: [PATCH 7/8] mergetools: use the correct tool for Beyond Compare 3 on Windows
From: Pat Thoyts @ 2011-10-15 12:39 UTC (permalink / raw)
  To: Sebastian Schuberth; +Cc: Junio C Hamano, Git, msysGit
In-Reply-To: <4E996012.8090002@gmail.com>

Sebastian Schuberth <sschuberth@gmail.com> writes:

>On 15.10.2011 07:50, Junio C Hamano wrote:
>
>> Hmm, does this only apply to Windows, or are there other platforms on
>> which BC3 supplies bcomp for the exact same reason? What I am trying to
>
>BC3 is only available for Linux and Windows, so it only applies to
>Windows currently.

And checking the linux distribution shows only a bcompare executable so
testing for the presence of 'bcomp' will be fine.

-- 
Pat Thoyts                            http://www.patthoyts.tk/
PGP fingerprint 2C 6E 98 07 2C 59 C8 97  10 CE 11 E6 04 E0 B9 DD

^ permalink raw reply

* [PATCH 2/9 v2] completion: optimize refs completion
From: SZEDER Gábor @ 2011-10-15 12:57 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Shawn O. Pearce, Jonathan Nieder, SZEDER Gábor
In-Reply-To: <20111014121609.GB2208@goldbirke>

After a unique command or option is completed, in most cases it is a
good thing to add a trailing a space, but sometimes it doesn't make
sense, e.g. when the completed word is an option taking an argument
('--option=') or a configuration section ('core.').  Therefore the
completion script uses the '-o nospace' option to prevent bash from
automatically appending a space to unique completions, and it has the
__gitcomp() function to add that trailing space only when necessary.
See 72e5e989 (bash: Add space after unique command name is completed.,
2007-02-04), 78d4d6a2 (bash: Support unique completion on git-config.,
2007-02-04), and b3391775 (bash: Support unique completion when
possible., 2007-02-04).

__gitcomp() therefore iterates over all possible completion words it
got as argument, and checks each word whether a trailing space is
necessary or not.  This is ok for commands, options, etc., i.e. when
the number of words is relatively small, but can be noticeably slow
for large number of refs.  However, while options might or might not
need that trailing space, refs are always handled uniformly and always
get that trailing space (or a trailing '.' for 'git config
branch.<head>.').  Since refs listed by __git_refs() & co. are
separated by newline, this allows us some optimizations with
'compgen'.

So, add a specialized variant of __gitcomp() that only deals with
possible completion words separated by a newline and uniformly appends
the trailing space to all words using 'compgen -S " "' (or any other
suffix, if specified), so no iteration over all words is needed.  But
we need to fiddle with IFS, because the default IFS containing a space
would cause the added space suffix to be stripped off when compgen's
output is stored in the COMPREPLY array.  Therefore we use only
newline as IFS, hence the requirement for the newline-separated
possible completion words.

Convert all callsites of __gitcomp() where it's called with refs, i.e.
when it gets the output of either __git_refs(), __git_heads(),
__git_tags(), __git_refs2(), __git_refs_remotes(), or the odd 'git
for-each-ref' somewhere in _git_config().  Also convert callsites
where it gets other uniformly handled newline separated word lists,
i.e. either remotes from __git_remotes(), names of set configuration
variables from __git_config_get_set_variables(), stashes, or commands.

Here are some timing results for dealing with 10000 refs.
Before:

  $ refs="$(__git_refs ~/tmp/git/repo-with-10k-refs/)"
  $ time __gitcomp "$refs"

  real	0m1.134s
  user	0m1.060s
  sys	0m0.130s

After:

  $ time __gitcomp_nl "$refs"

  real	0m0.373s
  user	0m0.360s
  sys	0m0.020s

Signed-off-by: SZEDER Gábor <szeder@ira.uka.de>
---

On Fri, Oct 14, 2011 at 02:16:09PM +0200, SZEDER Gábor wrote:
> Oops, this last hunk is wrong.

Here's the update with that buggy hunk removed.  I also updated the
comments before __gitcomp_nl() to be more explicit, and the commit
message with the IFS fiddling and the grammar errors you pointed out
earlier.

These changes don't conflict with later patches, so I resend only this
patch but not the whole series.

 contrib/completion/git-completion.bash |  115 +++++++++++++++++++------------
 1 files changed, 70 insertions(+), 45 deletions(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index c0fb6e15..daabf827 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -512,6 +512,31 @@ __gitcomp ()
 	esac
 }
 
+# Generates completion reply with compgen from newline-separated possible
+# completion words by appending a space to all of them.
+# It accepts 1 to 4 arguments:
+# 1: List of possible completion words, separated by a single newline.
+# 2: A prefix to be added to each possible completion word (optional).
+# 3: Generate possible completion matches for this word (optional).
+# 4: A suffix to be appended to each possible completion word instead of
+#    the default space (optional).  If specified but empty, nothing is
+#    appended.
+__gitcomp_nl ()
+{
+	local s=$'\n' IFS=' '$'\t'$'\n'
+	local cur_="$cur" suffix=" "
+
+	if [ $# -gt 2 ]; then
+		cur_="$3"
+		if [ $# -gt 3 ]; then
+			suffix="$4"
+		fi
+	fi
+
+	IFS=$s
+	COMPREPLY=($(compgen -P "${2-}" -S "$suffix" -W "$1" -- "$cur_"))
+}
+
 # __git_heads accepts 0 or 1 arguments (to pass to __gitdir)
 __git_heads ()
 {
@@ -716,15 +741,15 @@ __git_complete_revlist_file ()
 	*...*)
 		pfx="${cur_%...*}..."
 		cur_="${cur_#*...}"
-		__gitcomp "$(__git_refs)" "$pfx" "$cur_"
+		__gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
 		;;
 	*..*)
 		pfx="${cur_%..*}.."
 		cur_="${cur_#*..}"
-		__gitcomp "$(__git_refs)" "$pfx" "$cur_"
+		__gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
 		;;
 	*)
-		__gitcomp "$(__git_refs)"
+		__gitcomp_nl "$(__git_refs)"
 		;;
 	esac
 }
@@ -764,7 +789,7 @@ __git_complete_remote_or_refspec ()
 		c=$((++c))
 	done
 	if [ -z "$remote" ]; then
-		__gitcomp "$(__git_remotes)"
+		__gitcomp_nl "$(__git_remotes)"
 		return
 	fi
 	if [ $no_complete_refspec = 1 ]; then
@@ -789,23 +814,23 @@ __git_complete_remote_or_refspec ()
 	case "$cmd" in
 	fetch)
 		if [ $lhs = 1 ]; then
-			__gitcomp "$(__git_refs2 "$remote")" "$pfx" "$cur_"
+			__gitcomp_nl "$(__git_refs2 "$remote")" "$pfx" "$cur_"
 		else
-			__gitcomp "$(__git_refs)" "$pfx" "$cur_"
+			__gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
 		fi
 		;;
 	pull)
 		if [ $lhs = 1 ]; then
-			__gitcomp "$(__git_refs "$remote")" "$pfx" "$cur_"
+			__gitcomp_nl "$(__git_refs "$remote")" "$pfx" "$cur_"
 		else
-			__gitcomp "$(__git_refs)" "$pfx" "$cur_"
+			__gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
 		fi
 		;;
 	push)
 		if [ $lhs = 1 ]; then
-			__gitcomp "$(__git_refs)" "$pfx" "$cur_"
+			__gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
 		else
-			__gitcomp "$(__git_refs "$remote")" "$pfx" "$cur_"
+			__gitcomp_nl "$(__git_refs "$remote")" "$pfx" "$cur_"
 		fi
 		;;
 	esac
@@ -1084,7 +1109,7 @@ _git_archive ()
 		return
 		;;
 	--remote=*)
-		__gitcomp "$(__git_remotes)" "" "${cur##--remote=}"
+		__gitcomp_nl "$(__git_remotes)" "" "${cur##--remote=}"
 		return
 		;;
 	--*)
@@ -1115,7 +1140,7 @@ _git_bisect ()
 
 	case "$subcommand" in
 	bad|good|reset|skip|start)
-		__gitcomp "$(__git_refs)"
+		__gitcomp_nl "$(__git_refs)"
 		;;
 	*)
 		COMPREPLY=()
@@ -1146,9 +1171,9 @@ _git_branch ()
 		;;
 	*)
 		if [ $only_local_ref = "y" -a $has_r = "n" ]; then
-			__gitcomp "$(__git_heads)"
+			__gitcomp_nl "$(__git_heads)"
 		else
-			__gitcomp "$(__git_refs)"
+			__gitcomp_nl "$(__git_refs)"
 		fi
 		;;
 	esac
@@ -1195,7 +1220,7 @@ _git_checkout ()
 		if [ -n "$(__git_find_on_cmdline "$flags")" ]; then
 			track=''
 		fi
-		__gitcomp "$(__git_refs '' $track)"
+		__gitcomp_nl "$(__git_refs '' $track)"
 		;;
 	esac
 }
@@ -1212,7 +1237,7 @@ _git_cherry_pick ()
 		__gitcomp "--edit --no-commit"
 		;;
 	*)
-		__gitcomp "$(__git_refs)"
+		__gitcomp_nl "$(__git_refs)"
 		;;
 	esac
 }
@@ -1266,7 +1291,7 @@ _git_commit ()
 		;;
 	--reuse-message=*|--reedit-message=*|\
 	--fixup=*|--squash=*)
-		__gitcomp "$(__git_refs)" "" "${cur#*=}"
+		__gitcomp_nl "$(__git_refs)" "" "${cur#*=}"
 		return
 		;;
 	--untracked-files=*)
@@ -1297,7 +1322,7 @@ _git_describe ()
 			"
 		return
 	esac
-	__gitcomp "$(__git_refs)"
+	__gitcomp_nl "$(__git_refs)"
 }
 
 __git_diff_common_options="--stat --numstat --shortstat --summary
@@ -1456,7 +1481,7 @@ _git_grep ()
 		;;
 	esac
 
-	__gitcomp "$(__git_refs)"
+	__gitcomp_nl "$(__git_refs)"
 }
 
 _git_help ()
@@ -1514,7 +1539,7 @@ _git_ls_files ()
 
 _git_ls_remote ()
 {
-	__gitcomp "$(__git_remotes)"
+	__gitcomp_nl "$(__git_remotes)"
 }
 
 _git_ls_tree ()
@@ -1610,7 +1635,7 @@ _git_merge ()
 		__gitcomp "$__git_merge_options"
 		return
 	esac
-	__gitcomp "$(__git_refs)"
+	__gitcomp_nl "$(__git_refs)"
 }
 
 _git_mergetool ()
@@ -1630,7 +1655,7 @@ _git_mergetool ()
 
 _git_merge_base ()
 {
-	__gitcomp "$(__git_refs)"
+	__gitcomp_nl "$(__git_refs)"
 }
 
 _git_mv ()
@@ -1661,7 +1686,7 @@ _git_notes ()
 	,*)
 		case "${words[cword-1]}" in
 		--ref)
-			__gitcomp "$(__git_refs)"
+			__gitcomp_nl "$(__git_refs)"
 			;;
 		*)
 			__gitcomp "$subcommands --ref"
@@ -1670,7 +1695,7 @@ _git_notes ()
 		;;
 	add,--reuse-message=*|append,--reuse-message=*|\
 	add,--reedit-message=*|append,--reedit-message=*)
-		__gitcomp "$(__git_refs)" "" "${cur#*=}"
+		__gitcomp_nl "$(__git_refs)" "" "${cur#*=}"
 		;;
 	add,--*|append,--*)
 		__gitcomp '--file= --message= --reedit-message=
@@ -1689,7 +1714,7 @@ _git_notes ()
 		-m|-F)
 			;;
 		*)
-			__gitcomp "$(__git_refs)"
+			__gitcomp_nl "$(__git_refs)"
 			;;
 		esac
 		;;
@@ -1717,12 +1742,12 @@ _git_push ()
 {
 	case "$prev" in
 	--repo)
-		__gitcomp "$(__git_remotes)"
+		__gitcomp_nl "$(__git_remotes)"
 		return
 	esac
 	case "$cur" in
 	--repo=*)
-		__gitcomp "$(__git_remotes)" "" "${cur##--repo=}"
+		__gitcomp_nl "$(__git_remotes)" "" "${cur##--repo=}"
 		return
 		;;
 	--*)
@@ -1760,7 +1785,7 @@ _git_rebase ()
 
 		return
 	esac
-	__gitcomp "$(__git_refs)"
+	__gitcomp_nl "$(__git_refs)"
 }
 
 _git_reflog ()
@@ -1771,7 +1796,7 @@ _git_reflog ()
 	if [ -z "$subcommand" ]; then
 		__gitcomp "$subcommands"
 	else
-		__gitcomp "$(__git_refs)"
+		__gitcomp_nl "$(__git_refs)"
 	fi
 }
 
@@ -1853,23 +1878,23 @@ _git_config ()
 {
 	case "$prev" in
 	branch.*.remote)
-		__gitcomp "$(__git_remotes)"
+		__gitcomp_nl "$(__git_remotes)"
 		return
 		;;
 	branch.*.merge)
-		__gitcomp "$(__git_refs)"
+		__gitcomp_nl "$(__git_refs)"
 		return
 		;;
 	remote.*.fetch)
 		local remote="${prev#remote.}"
 		remote="${remote%.fetch}"
-		__gitcomp "$(__git_refs_remotes "$remote")"
+		__gitcomp_nl "$(__git_refs_remotes "$remote")"
 		return
 		;;
 	remote.*.push)
 		local remote="${prev#remote.}"
 		remote="${remote%.push}"
-		__gitcomp "$(git --git-dir="$(__gitdir)" \
+		__gitcomp_nl "$(git --git-dir="$(__gitdir)" \
 			for-each-ref --format='%(refname):%(refname)' \
 			refs/heads)"
 		return
@@ -1916,7 +1941,7 @@ _git_config ()
 		return
 		;;
 	--get|--get-all|--unset|--unset-all)
-		__gitcomp "$(__git_config_get_set_variables)"
+		__gitcomp_nl "$(__git_config_get_set_variables)"
 		return
 		;;
 	*.*)
@@ -1942,7 +1967,7 @@ _git_config ()
 		;;
 	branch.*)
 		local pfx="${cur%.*}." cur_="${cur#*.}"
-		__gitcomp "$(__git_heads)" "$pfx" "$cur_" "."
+		__gitcomp_nl "$(__git_heads)" "$pfx" "$cur_" "."
 		return
 		;;
 	guitool.*.*)
@@ -1971,7 +1996,7 @@ _git_config ()
 	pager.*)
 		local pfx="${cur%.*}." cur_="${cur#*.}"
 		__git_compute_all_commands
-		__gitcomp "$__git_all_commands" "$pfx" "$cur_"
+		__gitcomp_nl "$__git_all_commands" "$pfx" "$cur_"
 		return
 		;;
 	remote.*.*)
@@ -1984,7 +2009,7 @@ _git_config ()
 		;;
 	remote.*)
 		local pfx="${cur%.*}." cur_="${cur#*.}"
-		__gitcomp "$(__git_remotes)" "$pfx" "$cur_" "."
+		__gitcomp_nl "$(__git_remotes)" "$pfx" "$cur_" "."
 		return
 		;;
 	url.*.*)
@@ -2285,7 +2310,7 @@ _git_remote ()
 
 	case "$subcommand" in
 	rename|rm|show|prune)
-		__gitcomp "$(__git_remotes)"
+		__gitcomp_nl "$(__git_remotes)"
 		;;
 	update)
 		local i c='' IFS=$'\n'
@@ -2303,7 +2328,7 @@ _git_remote ()
 
 _git_replace ()
 {
-	__gitcomp "$(__git_refs)"
+	__gitcomp_nl "$(__git_refs)"
 }
 
 _git_reset ()
@@ -2316,7 +2341,7 @@ _git_reset ()
 		return
 		;;
 	esac
-	__gitcomp "$(__git_refs)"
+	__gitcomp_nl "$(__git_refs)"
 }
 
 _git_revert ()
@@ -2327,7 +2352,7 @@ _git_revert ()
 		return
 		;;
 	esac
-	__gitcomp "$(__git_refs)"
+	__gitcomp_nl "$(__git_refs)"
 }
 
 _git_rm ()
@@ -2426,7 +2451,7 @@ _git_stash ()
 			COMPREPLY=()
 			;;
 		show,*|apply,*|drop,*|pop,*|branch,*)
-			__gitcomp "$(git --git-dir="$(__gitdir)" stash list \
+			__gitcomp_nl "$(git --git-dir="$(__gitdir)" stash list \
 					| sed -n -e 's/:.*//p')"
 			;;
 		*)
@@ -2560,7 +2585,7 @@ _git_tag ()
 		i="${words[c]}"
 		case "$i" in
 		-d|-v)
-			__gitcomp "$(__git_tags)"
+			__gitcomp_nl "$(__git_tags)"
 			return
 			;;
 		-f)
@@ -2576,13 +2601,13 @@ _git_tag ()
 		;;
 	-*|tag)
 		if [ $f = 1 ]; then
-			__gitcomp "$(__git_tags)"
+			__gitcomp_nl "$(__git_tags)"
 		else
 			COMPREPLY=()
 		fi
 		;;
 	*)
-		__gitcomp "$(__git_refs)"
+		__gitcomp_nl "$(__git_refs)"
 		;;
 	esac
 }
-- 
1.7.7.197.g04a3e

^ permalink raw reply related

* Re: [PATCH 2/8] t1402: Ignore a few cases that must fail due to DOS path expansion
From: Pat Thoyts @ 2011-10-15 13:49 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git, msysGit, Johannes Schindelin
In-Reply-To: <7vwrc6x1cp.fsf@alter.siamese.dyndns.org>

Junio C Hamano <gitster@pobox.com> writes:

>Pat Thoyts <patthoyts@users.sourceforge.net> writes:
>
>>  t/t1402-check-ref-format.sh |   15 +++++++++------
>
>Didn't we see a different patch that attempts to address the same issue
>recently on the list from J6t, or is this a fix for a different problem?
>

You are correct - I'll leave this out of this series then. j6t's patch
is an alternative fix for the same problem.

-- 
Pat Thoyts                            http://www.patthoyts.tk/
PGP fingerprint 2C 6E 98 07 2C 59 C8 97  10 CE 11 E6 04 E0 B9 DD

^ permalink raw reply

* [PATCH 0/7] Some patches from msysGit (round 2)
From: Pat Thoyts @ 2011-10-15 14:05 UTC (permalink / raw)
  To: Git; +Cc: Junio C Hamano, msysGit, Pat Thoyts

This collects some recent patches from the msysGit tree that clear up
test issues on Windows.

This second version incorporates suggestions received from round 1 to:
  avoid duplicating code in t9901 web-browse tests
  drop the t1402 changes in favour of another change from J6t (on pu)
  test for the presence of 'bcomp' in bc3 mergetool

Johannes Schindelin (3):
  t1020: disable the pwd test on MinGW
  t9001: do not fail only due to CR/LF issues
  t9300: do not run --cat-blob-fd related tests on MinGW

Pat Thoyts (3):
  t9901: fix line-ending dependency on windows
  mergetools: use the correct tool for Beyond Compare 3 on Windows
  mingw: ensure sockets are initialized before calling gethostname

Sebastian Schuberth (1):
  git-svn: On MSYS, escape and quote SVN_SSH also if set by the user

 compat/mingw.c             |    7 +++++++
 compat/mingw.h             |    3 +++
 git-svn.perl               |   15 +++++++--------
 mergetools/bc3             |    7 ++++++-
 t/t1020-subdirectory.sh    |    2 +-
 t/t9001-send-email.sh      |    1 +
 t/t9300-fast-import.sh     |    8 ++++----
 t/t9901-git-web--browse.sh |   32 +++++++++++++++++---------------
 8 files changed, 46 insertions(+), 29 deletions(-)

-- 
1.7.7.1.gbba15

^ permalink raw reply

* [PATCH 1/7] t1020: disable the pwd test on MinGW
From: Pat Thoyts @ 2011-10-15 14:05 UTC (permalink / raw)
  To: Git; +Cc: Junio C Hamano, msysGit, Johannes Schindelin
In-Reply-To: <1318687520-19522-1-git-send-email-patthoyts@users.sourceforge.net>

From: Johannes Schindelin <johannes.schindelin@gmx.de>

It fails both for line ending and for DOS path reasons.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 t/t1020-subdirectory.sh |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/t/t1020-subdirectory.sh b/t/t1020-subdirectory.sh
index 3b1b985..e23ac0e 100755
--- a/t/t1020-subdirectory.sh
+++ b/t/t1020-subdirectory.sh
@@ -118,7 +118,7 @@ test_expect_success 'alias expansion' '
 	)
 '
 
-test_expect_success '!alias expansion' '
+test_expect_success NOT_MINGW '!alias expansion' '
 	pwd >expect &&
 	(
 		git config alias.test !pwd &&
-- 
1.7.7.1.gbba15

^ permalink raw reply related

* [PATCH 2/7] t9001: do not fail only due to CR/LF issues
From: Pat Thoyts @ 2011-10-15 14:05 UTC (permalink / raw)
  To: Git; +Cc: Junio C Hamano, msysGit, Johannes Schindelin
In-Reply-To: <1318687520-19522-1-git-send-email-patthoyts@users.sourceforge.net>

From: Johannes Schindelin <johannes.schindelin@gmx.de>

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 t/t9001-send-email.sh |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 87b4acc..8c12c65 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -23,6 +23,7 @@ test_expect_success $PREREQ \
       echo do
       echo "  echo \"!\$a!\""
       echo "done >commandline\$output"
+      test_have_prereq MINGW && echo "dos2unix commandline\$output"
       echo "cat > msgtxt\$output"
       ) >fake.sendmail &&
      chmod +x ./fake.sendmail &&
-- 
1.7.7.1.gbba15

^ permalink raw reply related

* [PATCH 5/7] t9300: do not run --cat-blob-fd related tests on MinGW
From: Pat Thoyts @ 2011-10-15 14:05 UTC (permalink / raw)
  To: Git; +Cc: Junio C Hamano, msysGit, Johannes Schindelin
In-Reply-To: <1318687520-19522-1-git-send-email-patthoyts@users.sourceforge.net>

From: Johannes Schindelin <johannes.schindelin@gmx.de>

As diagnosed by Johannes Sixt, msys.dll does not hand through file
descriptors > 2 to child processes, so these test cases cannot passes when
run through an MSys bash.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 t/t9300-fast-import.sh |    8 ++++----
 1 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
index bd32b91..438aaf6 100755
--- a/t/t9300-fast-import.sh
+++ b/t/t9300-fast-import.sh
@@ -2237,7 +2237,7 @@ test_expect_success 'R: cat-blob-fd must be a nonnegative integer' '
 	test_must_fail git fast-import --cat-blob-fd=-1 </dev/null
 '
 
-test_expect_success 'R: print old blob' '
+test_expect_success NOT_MINGW 'R: print old blob' '
 	blob=$(echo "yes it can" | git hash-object -w --stdin) &&
 	cat >expect <<-EOF &&
 	${blob} blob 11
@@ -2249,7 +2249,7 @@ test_expect_success 'R: print old blob' '
 	test_cmp expect actual
 '
 
-test_expect_success 'R: in-stream cat-blob-fd not respected' '
+test_expect_success NOT_MINGW 'R: in-stream cat-blob-fd not respected' '
 	echo hello >greeting &&
 	blob=$(git hash-object -w greeting) &&
 	cat >expect <<-EOF &&
@@ -2270,7 +2270,7 @@ test_expect_success 'R: in-stream cat-blob-fd not respected' '
 	test_cmp expect actual.1
 '
 
-test_expect_success 'R: print new blob' '
+test_expect_success NOT_MINGW 'R: print new blob' '
 	blob=$(echo "yep yep yep" | git hash-object --stdin) &&
 	cat >expect <<-EOF &&
 	${blob} blob 12
@@ -2288,7 +2288,7 @@ test_expect_success 'R: print new blob' '
 	test_cmp expect actual
 '
 
-test_expect_success 'R: print new blob by sha1' '
+test_expect_success NOT_MINGW 'R: print new blob by sha1' '
 	blob=$(echo "a new blob named by sha1" | git hash-object --stdin) &&
 	cat >expect <<-EOF &&
 	${blob} blob 25
-- 
1.7.7.1.gbba15

^ permalink raw reply related

* [PATCH 4/7] t9901: fix line-ending dependency on windows
From: Pat Thoyts @ 2011-10-15 14:05 UTC (permalink / raw)
  To: Git; +Cc: Junio C Hamano, msysGit, Pat Thoyts
In-Reply-To: <1318687520-19522-1-git-send-email-patthoyts@users.sourceforge.net>

Signed-off-by: Pat Thoyts <patthoyts@users.sourceforge.net>
---
 t/t9901-git-web--browse.sh |   32 +++++++++++++++++---------------
 1 files changed, 17 insertions(+), 15 deletions(-)

diff --git a/t/t9901-git-web--browse.sh b/t/t9901-git-web--browse.sh
index 7906e5d..69513f1 100755
--- a/t/t9901-git-web--browse.sh
+++ b/t/t9901-git-web--browse.sh
@@ -7,31 +7,35 @@ This test checks that git web--browse can handle various valid URLs.'
 
 . ./test-lib.sh
 
+test_web_browse () {
+	# browser=$1 url=$2
+	git web--browse --browser="$1" "$2" >actual &&
+	tr -d '\015' <actual >text &&
+	test_cmp expect text
+}
+
 test_expect_success \
 	'URL with an ampersand in it' '
 	echo http://example.com/foo\&bar >expect &&
 	git config browser.custom.cmd echo &&
-	git web--browse --browser=custom \
-		http://example.com/foo\&bar >actual &&
-	test_cmp expect actual
+	test_web_browse custom \
+		http://example.com/foo\&bar
 '
 
 test_expect_success \
 	'URL with a semi-colon in it' '
 	echo http://example.com/foo\;bar >expect &&
 	git config browser.custom.cmd echo &&
-	git web--browse --browser=custom \
-		http://example.com/foo\;bar >actual &&
-	test_cmp expect actual
+	test_web_browse custom \
+		http://example.com/foo\;bar
 '
 
 test_expect_success \
 	'URL with a hash in it' '
 	echo http://example.com/foo#bar >expect &&
 	git config browser.custom.cmd echo &&
-	git web--browse --browser=custom \
-		http://example.com/foo#bar >actual &&
-	test_cmp expect actual
+	test_web_browse custom \
+		http://example.com/foo#bar
 '
 
 test_expect_success \
@@ -43,9 +47,8 @@ test_expect_success \
 	EOF
 	chmod +x "fake browser" &&
 	git config browser.w3m.path "`pwd`/fake browser" &&
-	git web--browse --browser=w3m \
-		http://example.com/foo >actual &&
-	test_cmp expect actual
+	test_web_browse w3m \
+		http://example.com/foo
 '
 
 test_expect_success \
@@ -58,9 +61,8 @@ test_expect_success \
 			done
 		}
 		f" &&
-	git web--browse --browser=custom \
-		http://example.com/foo >actual &&
-	test_cmp expect actual
+	test_web_browse custom \
+		http://example.com/foo
 '
 
 test_done
-- 
1.7.7.1.gbba15

^ permalink raw reply related

* [PATCH 6/7] mergetools: use the correct tool for Beyond Compare 3 on Windows
From: Pat Thoyts @ 2011-10-15 14:05 UTC (permalink / raw)
  To: Git; +Cc: Junio C Hamano, msysGit, Pat Thoyts
In-Reply-To: <1318687520-19522-1-git-send-email-patthoyts@users.sourceforge.net>

On Windows the bcompare tool launches a graphical program and does
not wait for it to terminate. A separate 'bcomp' tool is provided which
will wait for the view to exit so we use this instead.

Reported-by: Werner BEROUX <werner@beroux.com>
Signed-off-by: Pat Thoyts <patthoyts@users.sourceforge.net>
---
 mergetools/bc3 |    7 ++++++-
 1 files changed, 6 insertions(+), 1 deletions(-)

diff --git a/mergetools/bc3 b/mergetools/bc3
index 27b3dd4..b6319d2 100644
--- a/mergetools/bc3
+++ b/mergetools/bc3
@@ -16,5 +16,10 @@ merge_cmd () {
 }
 
 translate_merge_tool_path() {
-	echo bcompare
+	if type bcomp >/dev/null 2>/dev/null
+	then
+		echo bcomp
+	else
+		echo bcompare
+	fi
 }
-- 
1.7.7.1.gbba15

^ permalink raw reply related

* [PATCH 7/7] mingw: ensure sockets are initialized before calling gethostname
From: Pat Thoyts @ 2011-10-15 14:05 UTC (permalink / raw)
  To: Git; +Cc: Junio C Hamano, msysGit, Pat Thoyts
In-Reply-To: <1318687520-19522-1-git-send-email-patthoyts@users.sourceforge.net>

If the Windows sockets subsystem has not been initialized yet then an
attempt to get the hostname returns an error and prints a warning to the
console. This solves this issue for msysGit as seen with 'git fetch'.

Signed-off-by: Pat Thoyts <patthoyts@users.sourceforge.net>
---
 compat/mingw.c |    7 +++++++
 compat/mingw.h |    3 +++
 2 files changed, 10 insertions(+), 0 deletions(-)

diff --git a/compat/mingw.c b/compat/mingw.c
index 8947418..efdc703 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -1321,6 +1321,13 @@ static void ensure_socket_initialization(void)
 	initialized = 1;
 }
 
+#undef gethostname
+int mingw_gethostname(char *name, int namelen)
+{
+    ensure_socket_initialization();
+    return gethostname(name, namelen);
+}
+
 #undef gethostbyname
 struct hostent *mingw_gethostbyname(const char *host)
 {
diff --git a/compat/mingw.h b/compat/mingw.h
index ce9dd98..fecf0d0 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -190,6 +190,9 @@ char *mingw_getcwd(char *pointer, int len);
 char *mingw_getenv(const char *name);
 #define getenv mingw_getenv
 
+int mingw_gethostname(char *host, int namelen);
+#define gethostname mingw_gethostname
+
 struct hostent *mingw_gethostbyname(const char *host);
 #define gethostbyname mingw_gethostbyname
 
-- 
1.7.7.1.gbba15

^ permalink raw reply related

* [PATCH 3/7] git-svn: On MSYS, escape and quote SVN_SSH also if set by the user
From: Pat Thoyts @ 2011-10-15 14:05 UTC (permalink / raw)
  To: Git; +Cc: Junio C Hamano, msysGit, Sebastian Schuberth
In-Reply-To: <1318687520-19522-1-git-send-email-patthoyts@users.sourceforge.net>

From: Sebastian Schuberth <sschuberth@gmail.com>

While GIT_SSH does not require any escaping / quoting (e.g. for paths
containing spaces), SVN_SSH requires it due to its use in a Perl script.

Previously, SVN_SSH has only been escaped and quoted automatically if it
was unset and thus derived from GIT_SSH. For user convenience, do the
escaping and quoting also for a SVN_SSH set by the user. This way, the
user is able to use the same unescaped and unquoted syntax for GIT_SSH
and SVN_SSH.

Signed-off-by: Sebastian Schuberth <sschuberth@gmail.com>
---
 git-svn.perl |   15 +++++++--------
 1 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/git-svn.perl b/git-svn.perl
index a0410f0..3b33379 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -22,14 +22,13 @@ $Git::SVN::default_ref_id = $ENV{GIT_SVN_ID} || 'git-svn';
 $Git::SVN::Ra::_log_window_size = 100;
 $Git::SVN::_minimize_url = 'unset';
 
-if (! exists $ENV{SVN_SSH}) {
-	if (exists $ENV{GIT_SSH}) {
-		$ENV{SVN_SSH} = $ENV{GIT_SSH};
-		if ($^O eq 'msys') {
-			$ENV{SVN_SSH} =~ s/\\/\\\\/g;
-			$ENV{SVN_SSH} =~ s/(.*)/"$1"/;
-		}
-	}
+if (! exists $ENV{SVN_SSH} && exists $ENV{GIT_SSH}) {
+	$ENV{SVN_SSH} = $ENV{GIT_SSH};
+}
+
+if (exists $ENV{SVN_SSH} && $^O eq 'msys') {
+	$ENV{SVN_SSH} =~ s/\\/\\\\/g;
+	$ENV{SVN_SSH} =~ s/(.*)/"$1"/;
 }
 
 $Git::SVN::Log::TZ = $ENV{TZ};
-- 
1.7.7.1.gbba15

^ permalink raw reply related

* Re: interrupting "git rebase" (Re: git rebase +)
From: Martin von Zweigbergk @ 2011-10-15 15:23 UTC (permalink / raw)
  To: Jonathan Nieder; +Cc: Johannes Sixt, Adam Piatyszek, git
In-Reply-To: <20111014052653.GA5052@elie.hsd1.il.comcast.net>

On Thu, Oct 13, 2011 at 10:26 PM, Jonathan Nieder <jrnieder@gmail.com> wrote:
> Johannes Sixt wrote:
>
>> Hitting Ctrl-C during git-rebase results undefined behavior. git-rebase is
>> a shell script and was never designed to operate in any form of atomicity.
>>
>> You should have let it run until it stopped.
>
> Wait, really?  That's bad, and unlike most git commands.

If Ctrl-C is pressed while the state is being written, it could be
left with incomplete information, yes. It has been like that forever I
think. I'll put it on my todo list, but it will not be my top priority
(and I have very little git time now anyways).

> If interrupting the rebase leaves the repository in a state that
>
>        rm -fr .git/rebase-apply
>        git reset --hard <appropriate commit name>
>
> cannot recover from, I'd consider it a serious problem.

I agree, but I don't know of any way that can happen. Even "git rebase
--abort" should always work (in the sense that
.git/rebase-[apply|merge] would be deleted), although it will not
return to the original branch if some state is missing.

> By the way, what happened to the "git rebase --abort-softly" synonym
> for "rm -fr .git/rebase-apply" discussed a while ago?

I think we simply did not agree on a syntax, but here was also some
discussion about future plans for the sequencer. I remember seeing
some discussions about making "git reset --hard" remove the sequencer
state, but I don't remember the conclusion. It is not clear to me what
is ok to implement in git-rebase nowadays and what would just be
double work if it needs to be re-implemented in the sequencer.

Martin

^ permalink raw reply

* [PATCH v2 0/6] git-p4 tests, filetypes, shell metacharacters
From: Pete Wyckoff @ 2011-10-15 15:53 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Luke Diamand, Chris Li

This series of six patches has been reviewed on the mailing list
and should be ready for inclusion.

1    - Clean up git-p4 tests, including Junio review comments

2..4 - Handle p4 filetypes better, including Chris's suggested
       utf16 fix

5    - Fix p4 keyword parsing

6    - From Luke: avoid using the shell, so filenames with metacharacters
       are not errantly expanded

Luke Diamand (1):
      git-p4: handle files with shell metacharacters

Pete Wyckoff (5):
      git-p4 tests: refactor and cleanup
      git-p4: handle utf16 filetype properly
      git-p4: recognize all p4 filetypes
      git-p4: stop ignoring apple filetype
      git-p4: keyword flattening fixes

 contrib/fast-import/git-p4     |  287 +++++++++++++-------
 t/lib-git-p4.sh                |   74 +++++
 t/t9800-git-p4.sh              |  574 ++++++++++++++++++----------------------
 t/t9801-git-p4-branch.sh       |  233 ++++++++++++++++
 t/t9802-git-p4-filetype.sh     |  108 ++++++++
 t/t9803-git-shell-metachars.sh |   64 +++++
 6 files changed, 918 insertions(+), 422 deletions(-)
 create mode 100644 t/lib-git-p4.sh
 create mode 100755 t/t9801-git-p4-branch.sh
 create mode 100755 t/t9802-git-p4-filetype.sh
 create mode 100755 t/t9803-git-shell-metachars.sh

^ permalink raw reply

* [PATCH 1/6] git-p4 tests: refactor and cleanup
From: Pete Wyckoff @ 2011-10-15 15:55 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Luke Diamand, Chris Li
In-Reply-To: <20111015155358.GA29436@arf.padd.com>

Introduce a library for functions that are common to
multiple git-p4 test files.

Be a bit more clever about starting and stopping p4d.
Specify a unique port number for each test, so that
tests can run in parallel.  Start p4d not in daemon mode,
and save the pid, to be able to kill it cleanly later.
Never kill p4d at startup; always shutdown cleanly.

Handle directory changes better.  Always chdir inside
a subshell, and remove any post-test directory changes.

Clean up whitespace, and use test_cmp and test_must_fail
more consistently.

Separate the tests related to detecting p4 branches
into their own file, and add a few more.

Signed-off-by: Pete Wyckoff <pw@padd.com>
---
 t/lib-git-p4.sh          |   74 ++++++
 t/t9800-git-p4.sh        |  574 ++++++++++++++++++++--------------------------
 t/t9801-git-p4-branch.sh |  233 +++++++++++++++++++
 3 files changed, 558 insertions(+), 323 deletions(-)
 create mode 100644 t/lib-git-p4.sh
 create mode 100755 t/t9801-git-p4-branch.sh

diff --git a/t/lib-git-p4.sh b/t/lib-git-p4.sh
new file mode 100644
index 0000000..412adec
--- /dev/null
+++ b/t/lib-git-p4.sh
@@ -0,0 +1,74 @@
+#
+# Library code for git-p4 tests
+#
+
+. ./test-lib.sh
+
+if ! test_have_prereq PYTHON; then
+	skip_all='skipping git-p4 tests; python not available'
+	test_done
+fi
+( p4 -h && p4d -h ) >/dev/null 2>&1 || {
+	skip_all='skipping git-p4 tests; no p4 or p4d'
+	test_done
+}
+
+GITP4="$GIT_BUILD_DIR/contrib/fast-import/git-p4"
+
+# Try to pick a unique port: guess a large number, then hope
+# no more than one of each test is running.
+#
+# This does not handle the case where somebody else is running the
+# same tests and has chosen the same ports.
+testid=${this_test#t}
+git_p4_test_start=9800
+P4DPORT=$((10669 + (testid - git_p4_test_start)))
+
+export P4PORT=localhost:$P4DPORT
+export P4CLIENT=client
+
+db="$TRASH_DIRECTORY/db"
+cli="$TRASH_DIRECTORY/cli"
+git="$TRASH_DIRECTORY/git"
+pidfile="$TRASH_DIRECTORY/p4d.pid"
+
+start_p4d() {
+	mkdir -p "$db" "$cli" "$git" &&
+	(
+		p4d -q -r "$db" -p $P4DPORT &
+		echo $! >"$pidfile"
+	) &&
+	for i in 1 2 3 4 5 ; do
+		p4 info >/dev/null 2>&1 && break || true &&
+		echo waiting for p4d to start &&
+		sleep 1
+	done &&
+	# complain if it never started
+	p4 info >/dev/null &&
+	(
+		cd "$cli" &&
+		p4 client -i <<-EOF
+		Client: client
+		Description: client
+		Root: $cli
+		View: //depot/... //client/...
+		EOF
+	)
+}
+
+kill_p4d() {
+	pid=$(cat "$pidfile")
+	# it had better exist for the first kill
+	kill $pid &&
+	for i in 1 2 3 4 5 ; do
+		kill $pid >/dev/null 2>&1 || break
+		sleep 1
+	done &&
+	# complain if it would not die
+	test_must_fail kill $pid >/dev/null 2>&1 &&
+	rm -rf "$db" "$cli" "$pidfile"
+}
+
+cleanup_git() {
+	rm -rf "$git"
+}
diff --git a/t/t9800-git-p4.sh b/t/t9800-git-p4.sh
index 01ba041..296aee9 100755
--- a/t/t9800-git-p4.sh
+++ b/t/t9800-git-p4.sh
@@ -2,76 +2,51 @@
 
 test_description='git-p4 tests'
 
-. ./test-lib.sh
+. ./lib-git-p4.sh
 
-( p4 -h && p4d -h ) >/dev/null 2>&1 || {
-	skip_all='skipping git-p4 tests; no p4 or p4d'
-	test_done
-}
-
-GITP4=$GIT_BUILD_DIR/contrib/fast-import/git-p4
-P4DPORT=10669
-
-export P4PORT=localhost:$P4DPORT
-
-db="$TRASH_DIRECTORY/db"
-cli="$TRASH_DIRECTORY/cli"
-git="$TRASH_DIRECTORY/git"
-
-test_debug 'echo p4d -q -d -r "$db" -p $P4DPORT'
-test_expect_success setup '
-	mkdir -p "$db" &&
-	p4d -q -d -r "$db" -p $P4DPORT &&
-	mkdir -p "$cli" &&
-	mkdir -p "$git" &&
-	export P4PORT=localhost:$P4DPORT
+test_expect_success 'start p4d' '
+	start_p4d
 '
 
 test_expect_success 'add p4 files' '
-	cd "$cli" &&
-	p4 client -i <<-EOF &&
-	Client: client
-	Description: client
-	Root: $cli
-	View: //depot/... //client/...
-	EOF
-	export P4CLIENT=client &&
-	echo file1 >file1 &&
-	p4 add file1 &&
-	p4 submit -d "file1" &&
-	echo file2 >file2 &&
-	p4 add file2 &&
-	p4 submit -d "file2" &&
-	cd "$TRASH_DIRECTORY"
+	(
+		cd "$cli" &&
+		echo file1 >file1 &&
+		p4 add file1 &&
+		p4 submit -d "file1" &&
+		echo file2 >file2 &&
+		p4 add file2 &&
+		p4 submit -d "file2"
+	)
 '
 
-cleanup_git() {
-	cd "$TRASH_DIRECTORY" &&
-	rm -rf "$git" &&
-	mkdir "$git"
-}
-
 test_expect_success 'basic git-p4 clone' '
 	"$GITP4" clone --dest="$git" //depot &&
 	test_when_finished cleanup_git &&
-	cd "$git" &&
-	git log --oneline >lines &&
-	test_line_count = 1 lines
+	(
+		cd "$git" &&
+		git log --oneline >lines &&
+		test_line_count = 1 lines
+	)
 '
 
 test_expect_success 'git-p4 clone @all' '
 	"$GITP4" clone --dest="$git" //depot@all &&
 	test_when_finished cleanup_git &&
-	cd "$git" &&
-	git log --oneline >lines &&
-	test_line_count = 2 lines
+	(
+		cd "$git" &&
+		git log --oneline >lines &&
+		test_line_count = 2 lines
+	)
 '
 
 test_expect_success 'git-p4 sync uninitialized repo' '
 	test_create_repo "$git" &&
 	test_when_finished cleanup_git &&
-	cd "$git" &&
-	test_must_fail "$GITP4" sync
+	(
+		cd "$git" &&
+		test_must_fail "$GITP4" sync
+	)
 '
 
 #
@@ -81,17 +56,19 @@ test_expect_success 'git-p4 sync uninitialized repo' '
 test_expect_success 'git-p4 sync new branch' '
 	test_create_repo "$git" &&
 	test_when_finished cleanup_git &&
-	cd "$git" &&
-	test_commit head &&
-	"$GITP4" sync --branch=refs/remotes/p4/depot //depot@all &&
-	git log --oneline p4/depot >lines &&
-	test_line_count = 2 lines
+	(
+		cd "$git" &&
+		test_commit head &&
+		"$GITP4" sync --branch=refs/remotes/p4/depot //depot@all &&
+		git log --oneline p4/depot >lines &&
+		test_line_count = 2 lines
+	)
 '
 
 test_expect_success 'exit when p4 fails to produce marshaled output' '
 	badp4dir="$TRASH_DIRECTORY/badp4dir" &&
-	mkdir -p "$badp4dir" &&
-	test_when_finished "rm -rf $badp4dir" &&
+	mkdir "$badp4dir" &&
+	test_when_finished "rm \"$badp4dir/p4\" && rmdir \"$badp4dir\"" &&
 	cat >"$badp4dir"/p4 <<-EOF &&
 	#!$SHELL_PATH
 	exit 1
@@ -103,61 +80,61 @@ test_expect_success 'exit when p4 fails to produce marshaled output' '
 '
 
 test_expect_success 'add p4 files with wildcards in the names' '
-	cd "$cli" &&
-	echo file-wild-hash >file-wild#hash &&
-	echo file-wild-star >file-wild\*star &&
-	echo file-wild-at >file-wild@at &&
-	echo file-wild-percent >file-wild%percent &&
-	p4 add -f file-wild* &&
-	p4 submit -d "file wildcards"
+	(
+		cd "$cli" &&
+		echo file-wild-hash >file-wild#hash &&
+		echo file-wild-star >file-wild\*star &&
+		echo file-wild-at >file-wild@at &&
+		echo file-wild-percent >file-wild%percent &&
+		p4 add -f file-wild* &&
+		p4 submit -d "file wildcards"
+	)
 '
 
 test_expect_success 'wildcard files git-p4 clone' '
 	"$GITP4" clone --dest="$git" //depot &&
 	test_when_finished cleanup_git &&
-	cd "$git" &&
-	test -f file-wild#hash &&
-	test -f file-wild\*star &&
-	test -f file-wild@at &&
-	test -f file-wild%percent
+	(
+		cd "$git" &&
+		test -f file-wild#hash &&
+		test -f file-wild\*star &&
+		test -f file-wild@at &&
+		test -f file-wild%percent
+	)
 '
 
 test_expect_success 'clone bare' '
 	"$GITP4" clone --dest="$git" --bare //depot &&
 	test_when_finished cleanup_git &&
-	cd "$git" &&
-	test ! -d .git &&
-	bare=`git config --get core.bare` &&
-	test "$bare" = true
+	(
+		cd "$git" &&
+		test ! -d .git &&
+		bare=`git config --get core.bare` &&
+		test "$bare" = true
+	)
 '
 
 p4_add_user() {
-    name=$1
-    fullname=$2
-    p4 user -f -i <<EOF &&
-User: $name
-Email: $name@localhost
-FullName: $fullname
-EOF
-    p4 passwd -P secret $name
+	name=$1 fullname=$2 &&
+	p4 user -f -i <<-EOF &&
+	User: $name
+	Email: $name@localhost
+	FullName: $fullname
+	EOF
+	p4 passwd -P secret $name
 }
 
 p4_grant_admin() {
-    name=$1
-    p4 protect -o |\
-	awk "{print}END{print \"    admin user $name * //depot/...\"}" |\
-	p4 protect -i
+	name=$1 &&
+	{
+		p4 protect -o &&
+		echo "    admin user $name * //depot/..."
+	} | p4 protect -i
 }
 
 p4_check_commit_author() {
-    file=$1
-    user=$2
-    if p4 changes -m 1 //depot/$file | grep $user > /dev/null ; then
-	return 0
-    else
-	echo "file $file not modified by user $user" 1>&2
-	return 1
-    fi
+	file=$1 user=$2 &&
+	p4 changes -m 1 //depot/$file | grep -q $user
 }
 
 make_change_by_user() {
@@ -174,15 +151,17 @@ test_expect_success 'preserve users' '
 	p4_grant_admin alice &&
 	"$GITP4" clone --dest="$git" //depot &&
 	test_when_finished cleanup_git &&
-	cd "$git" &&
-	echo "username: a change by alice" >> file1 &&
-	echo "username: a change by bob" >> file2 &&
-	git commit --author "Alice <alice@localhost>" -m "a change by alice" file1 &&
-	git commit --author "Bob <bob@localhost>" -m "a change by bob" file2 &&
-	git config git-p4.skipSubmitEditCheck true &&
-	P4EDITOR=touch P4USER=alice P4PASSWD=secret "$GITP4" commit --preserve-user &&
-	p4_check_commit_author file1 alice &&
-	p4_check_commit_author file2 bob
+	(
+		cd "$git" &&
+		echo "username: a change by alice" >>file1 &&
+		echo "username: a change by bob" >>file2 &&
+		git commit --author "Alice <alice@localhost>" -m "a change by alice" file1 &&
+		git commit --author "Bob <bob@localhost>" -m "a change by bob" file2 &&
+		git config git-p4.skipSubmitEditCheck true &&
+		P4EDITOR=touch P4USER=alice P4PASSWD=secret "$GITP4" commit --preserve-user &&
+		p4_check_commit_author file1 alice &&
+		p4_check_commit_author file2 bob
+	)
 '
 
 # Test username support, submitting as bob, who lacks admin rights. Should
@@ -190,32 +169,37 @@ test_expect_success 'preserve users' '
 test_expect_success 'refuse to preserve users without perms' '
 	"$GITP4" clone --dest="$git" //depot &&
 	test_when_finished cleanup_git &&
-	cd "$git" &&
-	git config git-p4.skipSubmitEditCheck true &&
-	echo "username-noperms: a change by alice" >> file1 &&
-	git commit --author "Alice <alice@localhost>" -m "perms: a change by alice" file1 &&
-	! P4EDITOR=touch P4USER=bob P4PASSWD=secret "$GITP4" commit --preserve-user &&
-	! git diff --exit-code HEAD..p4/master > /dev/null
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEditCheck true &&
+		echo "username-noperms: a change by alice" >>file1 &&
+		git commit --author "Alice <alice@localhost>" -m "perms: a change by alice" file1 &&
+		P4EDITOR=touch P4USER=bob P4PASSWD=secret test_must_fail "$GITP4" commit --preserve-user &&
+		test_must_fail git diff --exit-code HEAD..p4/master
+	)
 '
 
 # What happens with unknown author? Without allowMissingP4Users it should fail.
 test_expect_success 'preserve user where author is unknown to p4' '
 	"$GITP4" clone --dest="$git" //depot &&
 	test_when_finished cleanup_git &&
-	cd "$git" &&
-	git config git-p4.skipSubmitEditCheck true &&
-	echo "username-bob: a change by bob" >> file1 &&
-	git commit --author "Bob <bob@localhost>" -m "preserve: a change by bob" file1 &&
-	echo "username-unknown: a change by charlie" >> file1 &&
-	git commit --author "Charlie <charlie@localhost>" -m "preserve: a change by charlie" file1 &&
-	! P4EDITOR=touch P4USER=alice P4PASSWD=secret "$GITP4" commit --preserve-user &&
-	! git diff --exit-code HEAD..p4/master > /dev/null &&
-	echo "$0: repeat with allowMissingP4Users enabled" &&
-	git config git-p4.allowMissingP4Users true &&
-	git config git-p4.preserveUser true &&
-	P4EDITOR=touch P4USER=alice P4PASSWD=secret "$GITP4" commit &&
-	git diff --exit-code HEAD..p4/master > /dev/null &&
-	p4_check_commit_author file1 alice
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEditCheck true &&
+		echo "username-bob: a change by bob" >>file1 &&
+		git commit --author "Bob <bob@localhost>" -m "preserve: a change by bob" file1 &&
+		echo "username-unknown: a change by charlie" >>file1 &&
+		git commit --author "Charlie <charlie@localhost>" -m "preserve: a change by charlie" file1 &&
+		P4EDITOR=touch P4USER=alice P4PASSWD=secret test_must_fail "$GITP4" commit --preserve-user &&
+		test_must_fail git diff --exit-code HEAD..p4/master &&
+
+		echo "$0: repeat with allowMissingP4Users enabled" &&
+		git config git-p4.allowMissingP4Users true &&
+		git config git-p4.preserveUser true &&
+		P4EDITOR=touch P4USER=alice P4PASSWD=secret "$GITP4" commit &&
+		git diff --exit-code HEAD..p4/master &&
+		p4_check_commit_author file1 alice
+	)
 '
 
 # If we're *not* using --preserve-user, git-p4 should warn if we're submitting
@@ -225,28 +209,30 @@ test_expect_success 'preserve user where author is unknown to p4' '
 test_expect_success 'not preserving user with mixed authorship' '
 	"$GITP4" clone --dest="$git" //depot &&
 	test_when_finished cleanup_git &&
-	cd "$git" &&
-	git config git-p4.skipSubmitEditCheck true &&
-	p4_add_user derek Derek &&
-
-	make_change_by_user usernamefile3 Derek derek@localhost &&
-	P4EDITOR=cat P4USER=alice P4PASSWD=secret "$GITP4" commit >actual &&
-	grep "git author derek@localhost does not match" actual &&
-
-	make_change_by_user usernamefile3 Charlie charlie@localhost &&
-	P4EDITOR=cat P4USER=alice P4PASSWD=secret "$GITP4" commit >actual &&
-	grep "git author charlie@localhost does not match" actual &&
-
-	make_change_by_user usernamefile3 alice alice@localhost &&
-	P4EDITOR=cat P4USER=alice P4PASSWD=secret "$GITP4" commit >actual &&
-	! grep "git author.*does not match" actual &&
-
-	git config git-p4.skipUserNameCheck true &&
-	make_change_by_user usernamefile3 Charlie charlie@localhost &&
-	P4EDITOR=cat P4USER=alice P4PASSWD=secret "$GITP4" commit >actual &&
-	! grep "git author.*does not match" actual &&
-
-	p4_check_commit_author usernamefile3 alice
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEditCheck true &&
+		p4_add_user derek Derek &&
+
+		make_change_by_user usernamefile3 Derek derek@localhost &&
+		P4EDITOR=cat P4USER=alice P4PASSWD=secret "$GITP4" commit |\
+		grep "git author derek@localhost does not match" &&
+
+		make_change_by_user usernamefile3 Charlie charlie@localhost &&
+		P4EDITOR=cat P4USER=alice P4PASSWD=secret "$GITP4" commit |\
+		grep "git author charlie@localhost does not match" &&
+
+		make_change_by_user usernamefile3 alice alice@localhost &&
+		P4EDITOR=cat P4USER=alice P4PASSWD=secret "$GITP4" |\
+		test_must_fail grep "git author.*does not match" &&
+
+		git config git-p4.skipUserNameCheck true &&
+		make_change_by_user usernamefile3 Charlie charlie@localhost &&
+		P4EDITOR=cat P4USER=alice P4PASSWD=secret "$GITP4" commit |\
+		test_must_fail grep "git author.*does not match" &&
+
+		p4_check_commit_author usernamefile3 alice
+	)
 '
 
 marshal_dump() {
@@ -263,10 +249,12 @@ test_expect_success 'initial import time from top change time' '
 	sleep 3 &&
 	"$GITP4" clone --dest="$git" //depot &&
 	test_when_finished cleanup_git &&
-	cd "$git" &&
-	gittime=$(git show -s --raw --pretty=format:%at HEAD) &&
-	echo $p4time $gittime &&
-	test $p4time = $gittime
+	(
+		cd "$git" &&
+		gittime=$(git show -s --raw --pretty=format:%at HEAD) &&
+		echo $p4time $gittime &&
+		test $p4time = $gittime
+	)
 '
 
 # Rename a file and confirm that rename is not detected in P4.
@@ -279,47 +267,49 @@ test_expect_success 'initial import time from top change time' '
 test_expect_success 'detect renames' '
 	"$GITP4" clone --dest="$git" //depot@all &&
 	test_when_finished cleanup_git &&
-	cd "$git" &&
-	git config git-p4.skipSubmitEditCheck true &&
-
-	git mv file1 file4 &&
-	git commit -a -m "Rename file1 to file4" &&
-	git diff-tree -r -M HEAD &&
-	"$GITP4" submit &&
-	p4 filelog //depot/file4 &&
-	! p4 filelog //depot/file4 | grep -q "branch from" &&
-
-	git mv file4 file5 &&
-	git commit -a -m "Rename file4 to file5" &&
-	git diff-tree -r -M HEAD &&
-	git config git-p4.detectRenames true &&
-	"$GITP4" submit &&
-	p4 filelog //depot/file5 &&
-	p4 filelog //depot/file5 | grep -q "branch from //depot/file4" &&
-
-	git mv file5 file6 &&
-	echo update >>file6 &&
-	git add file6 &&
-	git commit -a -m "Rename file5 to file6 with changes" &&
-	git diff-tree -r -M HEAD &&
-	level=$(git diff-tree -r -M HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/R0*//") &&
-	test -n "$level" && test "$level" -gt 0 && test "$level" -lt 98 &&
-	git config git-p4.detectRenames $((level + 2)) &&
-	"$GITP4" submit &&
-	p4 filelog //depot/file6 &&
-	! p4 filelog //depot/file6 | grep -q "branch from" &&
-
-	git mv file6 file7 &&
-	echo update >>file7 &&
-	git add file7 &&
-	git commit -a -m "Rename file6 to file7 with changes" &&
-	git diff-tree -r -M HEAD &&
-	level=$(git diff-tree -r -M HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/R0*//") &&
-	test -n "$level" && test "$level" -gt 2 && test "$level" -lt 100 &&
-	git config git-p4.detectRenames $((level - 2)) &&
-	"$GITP4" submit &&
-	p4 filelog //depot/file7 &&
-	p4 filelog //depot/file7 | grep -q "branch from //depot/file6"
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEditCheck true &&
+
+		git mv file1 file4 &&
+		git commit -a -m "Rename file1 to file4" &&
+		git diff-tree -r -M HEAD &&
+		"$GITP4" submit &&
+		p4 filelog //depot/file4 &&
+		p4 filelog //depot/file4 | test_must_fail grep -q "branch from" &&
+
+		git mv file4 file5 &&
+		git commit -a -m "Rename file4 to file5" &&
+		git diff-tree -r -M HEAD &&
+		git config git-p4.detectRenames true &&
+		"$GITP4" submit &&
+		p4 filelog //depot/file5 &&
+		p4 filelog //depot/file5 | grep -q "branch from //depot/file4" &&
+
+		git mv file5 file6 &&
+		echo update >>file6 &&
+		git add file6 &&
+		git commit -a -m "Rename file5 to file6 with changes" &&
+		git diff-tree -r -M HEAD &&
+		level=$(git diff-tree -r -M HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/R0*//") &&
+		test -n "$level" && test "$level" -gt 0 && test "$level" -lt 98 &&
+		git config git-p4.detectRenames $((level + 2)) &&
+		"$GITP4" submit &&
+		p4 filelog //depot/file6 &&
+		p4 filelog //depot/file6 | test_must_fail grep -q "branch from" &&
+
+		git mv file6 file7 &&
+		echo update >>file7 &&
+		git add file7 &&
+		git commit -a -m "Rename file6 to file7 with changes" &&
+		git diff-tree -r -M HEAD &&
+		level=$(git diff-tree -r -M HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/R0*//") &&
+		test -n "$level" && test "$level" -gt 2 && test "$level" -lt 100 &&
+		git config git-p4.detectRenames $((level - 2)) &&
+		"$GITP4" submit &&
+		p4 filelog //depot/file7 &&
+		p4 filelog //depot/file7 | grep -q "branch from //depot/file6"
+	)
 '
 
 # Copy a file and confirm that copy is not detected in P4.
@@ -336,141 +326,79 @@ test_expect_success 'detect renames' '
 test_expect_success 'detect copies' '
 	"$GITP4" clone --dest="$git" //depot@all &&
 	test_when_finished cleanup_git &&
-	cd "$git" &&
-	git config git-p4.skipSubmitEditCheck true &&
-
-	cp file2 file8 &&
-	git add file8 &&
-	git commit -a -m "Copy file2 to file8" &&
-	git diff-tree -r -C HEAD &&
-	"$GITP4" submit &&
-	p4 filelog //depot/file8 &&
-	! p4 filelog //depot/file8 | grep -q "branch from" &&
-
-	cp file2 file9 &&
-	git add file9 &&
-	git commit -a -m "Copy file2 to file9" &&
-	git diff-tree -r -C HEAD &&
-	git config git-p4.detectCopies true &&
-	"$GITP4" submit &&
-	p4 filelog //depot/file9 &&
-	! p4 filelog //depot/file9 | grep -q "branch from" &&
-
-	echo "file2" >>file2 &&
-	cp file2 file10 &&
-	git add file2 file10 &&
-	git commit -a -m "Modify and copy file2 to file10" &&
-	git diff-tree -r -C HEAD &&
-	"$GITP4" submit &&
-	p4 filelog //depot/file10 &&
-	p4 filelog //depot/file10 | grep -q "branch from //depot/file" &&
-
-	cp file2 file11 &&
-	git add file11 &&
-	git commit -a -m "Copy file2 to file11" &&
-	git diff-tree -r -C --find-copies-harder HEAD &&
-	src=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) &&
-	test "$src" = file10 &&
-	git config git-p4.detectCopiesHarder true &&
-	"$GITP4" submit &&
-	p4 filelog //depot/file11 &&
-	p4 filelog //depot/file11 | grep -q "branch from //depot/file" &&
-
-	cp file2 file12 &&
-	echo "some text" >>file12 &&
-	git add file12 &&
-	git commit -a -m "Copy file2 to file12 with changes" &&
-	git diff-tree -r -C --find-copies-harder HEAD &&
-	level=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/C0*//") &&
-	test -n "$level" && test "$level" -gt 0 && test "$level" -lt 98 &&
-	src=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) &&
-	test "$src" = file10 &&
-	git config git-p4.detectCopies $((level + 2)) &&
-	"$GITP4" submit &&
-	p4 filelog //depot/file12 &&
-	! p4 filelog //depot/file12 | grep -q "branch from" &&
-
-	cp file2 file13 &&
-	echo "different text" >>file13 &&
-	git add file13 &&
-	git commit -a -m "Copy file2 to file13 with changes" &&
-	git diff-tree -r -C --find-copies-harder HEAD &&
-	level=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/C0*//") &&
-	test -n "$level" && test "$level" -gt 2 && test "$level" -lt 100 &&
-	src=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) &&
-	test "$src" = file10 &&
-	git config git-p4.detectCopies $((level - 2)) &&
-	"$GITP4" submit &&
-	p4 filelog //depot/file13 &&
-	p4 filelog //depot/file13 | grep -q "branch from //depot/file"
-'
-
-# Create a simple branch structure in P4 depot to check if it is correctly
-# cloned.
-test_expect_success 'add simple p4 branches' '
-	cd "$cli" &&
-	mkdir branch1 &&
-	cd branch1 &&
-	echo file1 >file1 &&
-	echo file2 >file2 &&
-	p4 add file1 file2 &&
-	p4 submit -d "branch1" &&
-	p4 integrate //depot/branch1/... //depot/branch2/... &&
-	p4 submit -d "branch2" &&
-	echo file3 >file3 &&
-	p4 add file3 &&
-	p4 submit -d "add file3 in branch1" &&
-	p4 open file2 &&
-	echo update >>file2 &&
-	p4 submit -d "update file2 in branch1" &&
-	p4 integrate //depot/branch1/... //depot/branch3/... &&
-	p4 submit -d "branch3" &&
-	cd "$TRASH_DIRECTORY"
-'
-
-# Configure branches through git-config and clone them.
-# All files are tested to make sure branches were cloned correctly.
-# Finally, make an update to branch1 on P4 side to check if it is imported
-# correctly by git-p4.
-test_expect_success 'git-p4 clone simple branches' '
-	test_when_finished cleanup_git &&
-	test_create_repo "$git" &&
-	cd "$git" &&
-	git config git-p4.branchList branch1:branch2 &&
-	git config --add git-p4.branchList branch1:branch3 &&
-	"$GITP4" clone --dest=. --detect-branches //depot@all &&
-	git log --all --graph --decorate --stat &&
-	git reset --hard p4/depot/branch1 &&
-	test -f file1 &&
-	test -f file2 &&
-	test -f file3 &&
-	grep -q update file2 &&
-	git reset --hard p4/depot/branch2 &&
-	test -f file1 &&
-	test -f file2 &&
-	test ! -f file3 &&
-	! grep -q update file2 &&
-	git reset --hard p4/depot/branch3 &&
-	test -f file1 &&
-	test -f file2 &&
-	test -f file3 &&
-	grep -q update file2 &&
-	cd "$cli" &&
-	cd branch1 &&
-	p4 edit file2 &&
-	echo file2_ >>file2 &&
-	p4 submit -d "update file2 in branch1" &&
-	cd "$git" &&
-	git reset --hard p4/depot/branch1 &&
-	"$GITP4" rebase &&
-	grep -q file2_ file2
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEditCheck true &&
+
+		cp file2 file8 &&
+		git add file8 &&
+		git commit -a -m "Copy file2 to file8" &&
+		git diff-tree -r -C HEAD &&
+		"$GITP4" submit &&
+		p4 filelog //depot/file8 &&
+		p4 filelog //depot/file8 | test_must_fail grep -q "branch from" &&
+
+		cp file2 file9 &&
+		git add file9 &&
+		git commit -a -m "Copy file2 to file9" &&
+		git diff-tree -r -C HEAD &&
+		git config git-p4.detectCopies true &&
+		"$GITP4" submit &&
+		p4 filelog //depot/file9 &&
+		p4 filelog //depot/file9 | test_must_fail grep -q "branch from" &&
+
+		echo "file2" >>file2 &&
+		cp file2 file10 &&
+		git add file2 file10 &&
+		git commit -a -m "Modify and copy file2 to file10" &&
+		git diff-tree -r -C HEAD &&
+		"$GITP4" submit &&
+		p4 filelog //depot/file10 &&
+		p4 filelog //depot/file10 | grep -q "branch from //depot/file" &&
+
+		cp file2 file11 &&
+		git add file11 &&
+		git commit -a -m "Copy file2 to file11" &&
+		git diff-tree -r -C --find-copies-harder HEAD &&
+		src=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) &&
+		test "$src" = file10 &&
+		git config git-p4.detectCopiesHarder true &&
+		"$GITP4" submit &&
+		p4 filelog //depot/file11 &&
+		p4 filelog //depot/file11 | grep -q "branch from //depot/file" &&
+
+		cp file2 file12 &&
+		echo "some text" >>file12 &&
+		git add file12 &&
+		git commit -a -m "Copy file2 to file12 with changes" &&
+		git diff-tree -r -C --find-copies-harder HEAD &&
+		level=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/C0*//") &&
+		test -n "$level" && test "$level" -gt 0 && test "$level" -lt 98 &&
+		src=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) &&
+		test "$src" = file10 &&
+		git config git-p4.detectCopies $((level + 2)) &&
+		"$GITP4" submit &&
+		p4 filelog //depot/file12 &&
+		p4 filelog //depot/file12 | test_must_fail grep -q "branch from" &&
+
+		cp file2 file13 &&
+		echo "different text" >>file13 &&
+		git add file13 &&
+		git commit -a -m "Copy file2 to file13 with changes" &&
+		git diff-tree -r -C --find-copies-harder HEAD &&
+		level=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/C0*//") &&
+		test -n "$level" && test "$level" -gt 2 && test "$level" -lt 100 &&
+		src=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) &&
+		test "$src" = file10 &&
+		git config git-p4.detectCopies $((level - 2)) &&
+		"$GITP4" submit &&
+		p4 filelog //depot/file13 &&
+		p4 filelog //depot/file13 | grep -q "branch from //depot/file"
+	)
 '
 
-test_expect_success 'shutdown' '
-	pid=`pgrep -f p4d` &&
-	test -n "$pid" &&
-	test_debug "ps wl `echo $pid`" &&
-	kill $pid
+test_expect_success 'kill p4d' '
+	kill_p4d
 '
 
 test_done
diff --git a/t/t9801-git-p4-branch.sh b/t/t9801-git-p4-branch.sh
new file mode 100755
index 0000000..a25f18d
--- /dev/null
+++ b/t/t9801-git-p4-branch.sh
@@ -0,0 +1,233 @@
+#!/bin/sh
+
+test_description='git-p4 p4 branching tests'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+	start_p4d
+'
+
+#
+# 1: //depot/main/f1
+# 2: //depot/main/f2
+# 3: integrate //depot/main/... -> //depot/branch1/...
+# 4: //depot/main/f4
+# 5: //depot/branch1/f5
+# .: named branch branch2
+# 6: integrate -b branch2
+# 7: //depot/branch2/f7
+# 8: //depot/main/f8
+#
+test_expect_success 'basic p4 branches' '
+	(
+		cd "$cli" &&
+		mkdir -p main &&
+
+		echo f1 >main/f1 &&
+		p4 add main/f1 &&
+		p4 submit -d "main/f1" &&
+
+		echo f2 >main/f2 &&
+		p4 add main/f2 &&
+		p4 submit -d "main/f2" &&
+
+		p4 integrate //depot/main/... //depot/branch1/... &&
+		p4 submit -d "integrate main to branch1" &&
+
+		echo f4 >main/f4 &&
+		p4 add main/f4 &&
+		p4 submit -d "main/f4" &&
+
+		echo f5 >branch1/f5 &&
+		p4 add branch1/f5 &&
+		p4 submit -d "branch1/f5" &&
+
+		p4 branch -i <<-EOF &&
+		Branch: branch2
+		View: //depot/main/... //depot/branch2/...
+		EOF
+
+		p4 integrate -b branch2 &&
+		p4 submit -d "integrate main to branch2" &&
+
+		echo f7 >branch2/f7 &&
+		p4 add branch2/f7 &&
+		p4 submit -d "branch2/f7" &&
+
+		echo f8 >main/f8 &&
+		p4 add main/f8 &&
+		p4 submit -d "main/f8"
+	)
+'
+
+test_expect_success 'import main, no branch detection' '
+	test_when_finished cleanup_git &&
+	"$GITP4" clone --dest="$git" //depot/main@all &&
+	(
+		cd "$git" &&
+		git log --oneline --graph --decorate --all &&
+		git rev-list master >wc &&
+		test_line_count = 4 wc
+	)
+'
+
+test_expect_success 'import branch1, no branch detection' '
+	test_when_finished cleanup_git &&
+	"$GITP4" clone --dest="$git" //depot/branch1@all &&
+	(
+		cd "$git" &&
+		git log --oneline --graph --decorate --all &&
+		git rev-list master >wc &&
+		test_line_count = 2 wc
+	)
+'
+
+test_expect_success 'import branch2, no branch detection' '
+	test_when_finished cleanup_git &&
+	"$GITP4" clone --dest="$git" //depot/branch2@all &&
+	(
+		cd "$git" &&
+		git log --oneline --graph --decorate --all &&
+		git rev-list master >wc &&
+		test_line_count = 2 wc
+	)
+'
+
+test_expect_success 'import depot, no branch detection' '
+	test_when_finished cleanup_git &&
+	"$GITP4" clone --dest="$git" //depot@all &&
+	(
+		cd "$git" &&
+		git log --oneline --graph --decorate --all &&
+		git rev-list master >wc &&
+		test_line_count = 8 wc
+	)
+'
+
+test_expect_success 'import depot, branch detection' '
+	test_when_finished cleanup_git &&
+	"$GITP4" clone --dest="$git" --detect-branches //depot@all &&
+	(
+		cd "$git" &&
+
+		git log --oneline --graph --decorate --all &&
+
+		# 4 main commits
+		git rev-list master >wc &&
+		test_line_count = 4 wc &&
+
+		# 3 main, 1 integrate, 1 on branch2
+		git rev-list p4/depot/branch2 >wc &&
+		test_line_count = 5 wc &&
+
+		# no branch1, since no p4 branch created for it
+		test_must_fail git show-ref p4/depot/branch1
+	)
+'
+
+test_expect_success 'import depot, branch detection, branchList branch definition' '
+	test_when_finished cleanup_git &&
+	test_create_repo "$git" &&
+	(
+		cd "$git" &&
+		git config git-p4.branchList main:branch1 &&
+		"$GITP4" clone --dest=. --detect-branches //depot@all &&
+
+		git log --oneline --graph --decorate --all &&
+
+		# 4 main commits
+		git rev-list master >wc &&
+		test_line_count = 4 wc &&
+
+		# 3 main, 1 integrate, 1 on branch2
+		git rev-list p4/depot/branch2 >wc &&
+		test_line_count = 5 wc &&
+
+		# 2 main, 1 integrate, 1 on branch1
+		git rev-list p4/depot/branch1 >wc &&
+		test_line_count = 4 wc
+	)
+'
+
+test_expect_success 'restart p4d' '
+	kill_p4d &&
+	start_p4d
+'
+
+#
+# 1: //depot/branch1/file1
+#    //depot/branch1/file2
+# 2: integrate //depot/branch1/... -> //depot/branch2/...
+# 3: //depot/branch1/file3
+# 4: //depot/branch1/file2 (edit)
+# 5: integrate //depot/branch1/... -> //depot/branch3/...
+#
+## Create a simple branch structure in P4 depot.
+test_expect_success 'add simple p4 branches' '
+	(
+		cd "$cli" &&
+		mkdir branch1 &&
+		cd branch1 &&
+		echo file1 >file1 &&
+		echo file2 >file2 &&
+		p4 add file1 file2 &&
+		p4 submit -d "branch1" &&
+		p4 integrate //depot/branch1/... //depot/branch2/... &&
+		p4 submit -d "branch2" &&
+		echo file3 >file3 &&
+		p4 add file3 &&
+		p4 submit -d "add file3 in branch1" &&
+		p4 open file2 &&
+		echo update >>file2 &&
+		p4 submit -d "update file2 in branch1" &&
+		p4 integrate //depot/branch1/... //depot/branch3/... &&
+		p4 submit -d "branch3"
+	)
+'
+
+# Configure branches through git-config and clone them.
+# All files are tested to make sure branches were cloned correctly.
+# Finally, make an update to branch1 on P4 side to check if it is imported
+# correctly by git-p4.
+test_expect_success 'git-p4 clone simple branches' '
+	test_when_finished cleanup_git &&
+	test_create_repo "$git" &&
+	(
+		cd "$git" &&
+		git config git-p4.branchList branch1:branch2 &&
+		git config --add git-p4.branchList branch1:branch3 &&
+		"$GITP4" clone --dest=. --detect-branches //depot@all &&
+		git log --all --graph --decorate --stat &&
+		git reset --hard p4/depot/branch1 &&
+		test -f file1 &&
+		test -f file2 &&
+		test -f file3 &&
+		grep -q update file2 &&
+		git reset --hard p4/depot/branch2 &&
+		test -f file1 &&
+		test -f file2 &&
+		test ! -f file3 &&
+		test_must_fail grep -q update file2 &&
+		git reset --hard p4/depot/branch3 &&
+		test -f file1 &&
+		test -f file2 &&
+		test -f file3 &&
+		grep -q update file2 &&
+		cd "$cli" &&
+		cd branch1 &&
+		p4 edit file2 &&
+		echo file2_ >>file2 &&
+		p4 submit -d "update file2 in branch3" &&
+		cd "$git" &&
+		git reset --hard p4/depot/branch1 &&
+		"$GITP4" rebase &&
+		grep -q file2_ file2
+	)
+'
+
+test_expect_success 'kill p4d' '
+	kill_p4d
+'
+
+test_done
-- 
1.7.6.3

^ permalink raw reply related

* [PATCH 2/6] git-p4: handle utf16 filetype properly
From: Pete Wyckoff @ 2011-10-15 15:56 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Luke Diamand, Chris Li
In-Reply-To: <20111015155358.GA29436@arf.padd.com>

One of the filetypes that p4 supports is utf16.  Its behavior is
odd in this case.  The data delivered through "p4 -G print" is
not encoded in utf16, although "p4 print -o" will produce the
proper utf16-encoded file.

When dealing with this filetype, discard the data from -G, and
instead read the contents directly.

An alternate approach would be to try to encode the data in
python.  That worked for true utf16 files, but for other files
marked as utf16, p4 delivers mangled text in no recognizable encoding.

Add a test case to check utf16 handling, and +k and +ko handling.

Reported-by: Chris Li <git@chrisli.org>
Acked-by: Luke Diamand <luke@diamand.org>
Signed-off-by: Pete Wyckoff <pw@padd.com>
---
 contrib/fast-import/git-p4 |   11 +++++
 t/t9802-git-p4-filetype.sh |  108 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 119 insertions(+), 0 deletions(-)
 create mode 100755 t/t9802-git-p4-filetype.sh

diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4
index 2f7b270..e69caf3 100755
--- a/contrib/fast-import/git-p4
+++ b/contrib/fast-import/git-p4
@@ -1238,6 +1238,15 @@ class P4Sync(Command, P4UserMap):
             data = ''.join(contents)
             contents = [data[:-1]]
 
+        if file['type'].startswith("utf16"):
+            # p4 delivers different text in the python output to -G
+            # than it does when using "print -o", or normal p4 client
+            # operations.  utf16 is converted to ascii or utf8, perhaps.
+            # But ascii text saved as -t utf16 is completely mangled.
+            # Invoke print -o to get the real contents.
+            text = p4_read_pipe('print -q -o - "%s"' % file['depotFile'])
+            contents = [ text ]
+
         if self.isWindows and file["type"].endswith("text"):
             mangled = []
             for data in contents:
@@ -1245,6 +1254,8 @@ class P4Sync(Command, P4UserMap):
                 mangled.append(data)
             contents = mangled
 
+        # Note that we do not try to de-mangle keywords on utf16 files,
+        # even though in theory somebody may want that.
         if file['type'] in ('text+ko', 'unicode+ko', 'binary+ko'):
             contents = map(lambda text: re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text), contents)
         elif file['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'):
diff --git a/t/t9802-git-p4-filetype.sh b/t/t9802-git-p4-filetype.sh
new file mode 100755
index 0000000..cf07e6d
--- /dev/null
+++ b/t/t9802-git-p4-filetype.sh
@@ -0,0 +1,108 @@
+#!/bin/sh
+
+test_description='git-p4 p4 filetype tests'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+	start_p4d
+'
+
+test_expect_success 'utf-16 file create' '
+	(
+		cd "$cli" &&
+
+		# p4 saves this verbatim
+		echo -e "three\nline\ntext" >f-ascii &&
+		p4 add -t text f-ascii &&
+
+		# p4 adds \377\376 header
+		cp f-ascii f-ascii-as-utf16 &&
+		p4 add -t utf16 f-ascii-as-utf16 &&
+
+		# p4 saves this exactly as iconv produced it
+		echo -e "three\nline\ntext" | iconv -f ascii -t utf-16 >f-utf16 &&
+		p4 add -t utf16 f-utf16 &&
+
+		# this also is unchanged
+		cp f-utf16 f-utf16-as-text &&
+		p4 add -t text f-utf16-as-text &&
+
+		p4 submit -d "f files" &&
+
+		# force update of client files
+		p4 sync -f
+	)
+'
+
+test_expect_success 'utf-16 file test' '
+	test_when_finished cleanup_git &&
+	"$GITP4" clone --dest="$git" //depot@all &&
+	(
+		cd "$git" &&
+
+		test_cmp "$cli/f-ascii" f-ascii &&
+		test_cmp "$cli/f-ascii-as-utf16" f-ascii-as-utf16 &&
+		test_cmp "$cli/f-utf16" f-utf16 &&
+		test_cmp "$cli/f-utf16-as-text" f-utf16-as-text
+	)
+'
+
+test_expect_success 'keyword file create' '
+	(
+		cd "$cli" &&
+
+		echo -e "id\n\$Id\$\n\$Author\$\ntext" >k-text-k &&
+		p4 add -t text+k k-text-k &&
+
+		cp k-text-k k-text-ko &&
+		p4 add -t text+ko k-text-ko &&
+
+		cat k-text-k | iconv -f ascii -t utf-16 >k-utf16-k &&
+		p4 add -t utf16+k k-utf16-k &&
+
+		cp k-utf16-k k-utf16-ko &&
+		p4 add -t utf16+ko k-utf16-ko &&
+
+		p4 submit -d "k files" &&
+		p4 sync -f
+	)
+'
+
+build_smush() {
+	cat >k_smush.py <<-EOF &&
+	import re, sys
+	sys.stdout.write(re.sub(r'(?i)\\\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\\\$', r'$\1$', sys.stdin.read()))
+	EOF
+	cat >ko_smush.py <<-EOF
+	import re, sys
+	sys.stdout.write(re.sub(r'(?i)\\\$(Id|Header):[^$]*\\\$', r'$\1$', sys.stdin.read()))
+	EOF
+}
+
+test_expect_success 'keyword file test' '
+	build_smush &&
+	test_when_finished rm -f k_smush.py ko_smush.py &&
+	test_when_finished cleanup_git &&
+	"$GITP4" clone --dest="$git" //depot@all &&
+	(
+		cd "$git" &&
+
+		# text, ensure unexpanded
+		python "$TRASH_DIRECTORY/k_smush.py" <"$cli/k-text-k" >cli-k-text-k-smush &&
+		test_cmp cli-k-text-k-smush k-text-k &&
+		python "$TRASH_DIRECTORY/ko_smush.py" <"$cli/k-text-ko" >cli-k-text-ko-smush &&
+		test_cmp cli-k-text-ko-smush k-text-ko &&
+
+		# utf16, even though p4 expands keywords, git-p4 does not
+		# try to undo that
+		test_cmp "$cli/k-utf16-k" k-utf16-k &&
+		test_cmp "$cli/k-utf16-ko" k-utf16-ko
+	)
+'
+
+test_expect_success 'kill p4d' '
+	kill_p4d
+'
+
+test_done
-- 
1.7.6.3

^ permalink raw reply related

* [PATCH 3/6] git-p4: recognize all p4 filetypes
From: Pete Wyckoff @ 2011-10-15 15:57 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Luke Diamand, Chris Li
In-Reply-To: <20111015155358.GA29436@arf.padd.com>

The previous code was approximate in the filetypes it recognized.
Put in the canonical list and be more careful about matching
elements of the file type.

This might change behavior in some cases, hopefully for the
better.  Windows newline mangling will now happen on all
text files.  Previously some like "text+ko" were oddly exempt.

Files with multiple combinations of modifiers, like "text+klx",
are now recognized for keyword expansion.  I expect these to be
seen only rarely.

Acked-by: Luke Diamand <luke@diamand.org>
Signed-off-by: Pete Wyckoff <pw@padd.com>
---
 contrib/fast-import/git-p4 |   71 ++++++++++++++++++++++++++++++++------------
 1 files changed, 52 insertions(+), 19 deletions(-)

diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4
index e69caf3..0490ca5 100755
--- a/contrib/fast-import/git-p4
+++ b/contrib/fast-import/git-p4
@@ -118,13 +118,41 @@ def p4_system(cmd):
     real_cmd = p4_build_cmd(cmd)
     return system(real_cmd)
 
-def isP4Exec(kind):
-    """Determine if a Perforce 'kind' should have execute permission
+#
+# Canonicalize the p4 type and return a tuple of the
+# base type, plus any modifiers.  See "p4 help filetypes"
+# for a list and explanation.
+#
+def split_p4_type(p4type):
+
+    p4_filetypes_historical = {
+        "ctempobj": "binary+Sw",
+        "ctext": "text+C",
+        "cxtext": "text+Cx",
+        "ktext": "text+k",
+        "kxtext": "text+kx",
+        "ltext": "text+F",
+        "tempobj": "binary+FSw",
+        "ubinary": "binary+F",
+        "uresource": "resource+F",
+        "uxbinary": "binary+Fx",
+        "xbinary": "binary+x",
+        "xltext": "text+Fx",
+        "xtempobj": "binary+Swx",
+        "xtext": "text+x",
+        "xunicode": "unicode+x",
+        "xutf16": "utf16+x",
+    }
+    if p4type in p4_filetypes_historical:
+        p4type = p4_filetypes_historical[p4type]
+    mods = ""
+    s = p4type.split("+")
+    base = s[0]
+    mods = ""
+    if len(s) > 1:
+        mods = s[1]
+    return (base, mods)
 
-    'p4 help filetypes' gives a list of the types.  If it starts with 'x',
-    or x follows one of a few letters.  Otherwise, if there is an 'x' after
-    a plus sign, it is also executable"""
-    return (re.search(r"(^[cku]?x)|\+.*x", kind) != None)
 
 def setP4ExecBit(file, mode):
     # Reopens an already open file and changes the execute bit to match
@@ -1229,16 +1257,18 @@ class P4Sync(Command, P4UserMap):
         if verbose:
             sys.stderr.write("%s\n" % relPath)
 
-        mode = "644"
-        if isP4Exec(file["type"]):
-            mode = "755"
-        elif file["type"] == "symlink":
-            mode = "120000"
-            # p4 print on a symlink contains "target\n", so strip it off
+        (type_base, type_mods) = split_p4_type(file["type"])
+
+        git_mode = "100644"
+        if "x" in type_mods:
+            git_mode = "100755"
+        if type_base == "symlink":
+            git_mode = "120000"
+            # p4 print on a symlink contains "target\n"; remove the newline
             data = ''.join(contents)
             contents = [data[:-1]]
 
-        if file['type'].startswith("utf16"):
+        if type_base == "utf16":
             # p4 delivers different text in the python output to -G
             # than it does when using "print -o", or normal p4 client
             # operations.  utf16 is converted to ascii or utf8, perhaps.
@@ -1247,7 +1277,9 @@ class P4Sync(Command, P4UserMap):
             text = p4_read_pipe('print -q -o - "%s"' % file['depotFile'])
             contents = [ text ]
 
-        if self.isWindows and file["type"].endswith("text"):
+        # Perhaps windows wants unicode, utf16 newlines translated too;
+        # but this is not doing it.
+        if self.isWindows and type_base == "text":
             mangled = []
             for data in contents:
                 data = data.replace("\r\n", "\n")
@@ -1256,12 +1288,13 @@ 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 file['type'] in ('text+ko', 'unicode+ko', 'binary+ko'):
-            contents = map(lambda text: re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text), contents)
-        elif file['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'):
-            contents = map(lambda text: re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$\n]*\$',r'$\1$', text), contents)
+        if type_base in ("text", "unicode", "binary"):
+            if "ko" in type_mods:
+                contents = map(lambda text: re.sub(r'(?i)\$(Id|Header):[^$]*\$', r'$\1$', text), contents)
+            elif "k" in type_mods:
+                contents = map(lambda text: re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$\n]*\$', r'$\1$', text), contents)
 
-        self.gitStream.write("M %s inline %s\n" % (mode, relPath))
+        self.gitStream.write("M %s inline %s\n" % (git_mode, relPath))
 
         # total length...
         length = 0
-- 
1.7.6.3

^ permalink raw reply related

* [PATCH 4/6] git-p4: stop ignoring apple filetype
From: Pete Wyckoff @ 2011-10-15 15:59 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Luke Diamand, Chris Li
In-Reply-To: <20111015155358.GA29436@arf.padd.com>

Currently "apple" filetype is ignored explicitly, and the file is
not even included in the git repository.  This seems wrong.
Remove this, letting it be treated like a "binary" filetype.

Acked-by: Luke Diamand <luke@diamand.org>
Signed-off-by: Pete Wyckoff <pw@padd.com>
---
 contrib/fast-import/git-p4 |    5 -----
 1 files changed, 0 insertions(+), 5 deletions(-)

diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4
index 0490ca5..6b91595 100755
--- a/contrib/fast-import/git-p4
+++ b/contrib/fast-import/git-p4
@@ -1247,11 +1247,6 @@ class P4Sync(Command, P4UserMap):
     # - helper for streamP4Files
 
     def streamOneP4File(self, file, contents):
-        if file["type"] == "apple":
-            print "\nfile %s is a strange apple file that forks. Ignoring" % \
-                file['depotFile']
-            return
-
         relPath = self.stripRepoPath(file['depotFile'], self.branchPrefixes)
         relPath = self.wildcard_decode(relPath)
         if verbose:
-- 
1.7.6.3

^ permalink raw reply related

* [PATCH 5/6] git-p4: keyword flattening fixes
From: Pete Wyckoff @ 2011-10-15 16:00 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Luke Diamand, Chris Li
In-Reply-To: <20111015155358.GA29436@arf.padd.com>

Join the text before looking for keywords.  There is nothing to
prevent the p4 output marshaller from splitting in the middle of a
keyword, although it has never been known to happen.

Also remove the (?i) regexp modifier; perforce keywords are
documented as case-sensitive.

Remove the "\n" end-character match.  I don't know why that is
in there, and every keyword in a fairly large production p4 repository
always ends with a $.

Acked-by: Luke Diamand <luke@diamand.org>
Signed-off-by: Pete Wyckoff <pw@padd.com>
---
 contrib/fast-import/git-p4 |    8 ++++++--
 1 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4
index 6b91595..55b1667 100755
--- a/contrib/fast-import/git-p4
+++ b/contrib/fast-import/git-p4
@@ -1285,9 +1285,13 @@ class P4Sync(Command, P4UserMap):
         # even though in theory somebody may want that.
         if type_base in ("text", "unicode", "binary"):
             if "ko" in type_mods:
-                contents = map(lambda text: re.sub(r'(?i)\$(Id|Header):[^$]*\$', r'$\1$', text), contents)
+                text = ''.join(contents)
+                text = re.sub(r'\$(Id|Header):[^$]*\$', r'$\1$', text)
+                contents = [ text ]
             elif "k" in type_mods:
-                contents = map(lambda text: re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$\n]*\$', r'$\1$', text), contents)
+                text = ''.join(contents)
+                text = re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$', r'$\1$', text)
+                contents = [ text ]
 
         self.gitStream.write("M %s inline %s\n" % (git_mode, relPath))
 
-- 
1.7.6.3

^ permalink raw reply related

* [PATCH 6/6] git-p4: handle files with shell metacharacters
From: Pete Wyckoff @ 2011-10-15 16:02 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Luke Diamand, Chris Li
In-Reply-To: <20111015155358.GA29436@arf.padd.com>

From: Luke Diamand <luke@diamand.org>

git-p4 used to simply pass strings into system() and popen(), and
relied on the shell doing the necessary expansion. This though meant
that shell metacharacters in file names would be corrupted - for
example files with $ or space in them.

Switch to using subprocess.Popen() and friends, and pass in explicit
arrays in the places where it matters. This then avoids needing shell
expansion.

Add trivial helper functions for some common perforce operations. Add
test case.

[pw: test cleanup]

Signed-off-by: Luke Diamand <luke@diamand.org>
Signed-off-by: Pete Wyckoff <pw@padd.com>
---
 contrib/fast-import/git-p4     |  200 ++++++++++++++++++++++++---------------
 t/t9803-git-shell-metachars.sh |   64 +++++++++++++
 2 files changed, 187 insertions(+), 77 deletions(-)
 create mode 100755 t/t9803-git-shell-metachars.sh

diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4
index 55b1667..f885d70 100755
--- a/contrib/fast-import/git-p4
+++ b/contrib/fast-import/git-p4
@@ -22,36 +22,39 @@ def p4_build_cmd(cmd):
     location. It means that hooking into the environment, or other configuration
     can be done more easily.
     """
-    real_cmd = "%s " % "p4"
+    real_cmd = ["p4"]
 
     user = gitConfig("git-p4.user")
     if len(user) > 0:
-        real_cmd += "-u %s " % user
+        real_cmd += ["-u",user]
 
     password = gitConfig("git-p4.password")
     if len(password) > 0:
-        real_cmd += "-P %s " % password
+        real_cmd += ["-P", password]
 
     port = gitConfig("git-p4.port")
     if len(port) > 0:
-        real_cmd += "-p %s " % port
+        real_cmd += ["-p", port]
 
     host = gitConfig("git-p4.host")
     if len(host) > 0:
-        real_cmd += "-h %s " % host
+        real_cmd += ["-h", host]
 
     client = gitConfig("git-p4.client")
     if len(client) > 0:
-        real_cmd += "-c %s " % client
+        real_cmd += ["-c", client]
 
-    real_cmd += "%s" % (cmd)
-    if verbose:
-        print real_cmd
+
+    if isinstance(cmd,basestring):
+        real_cmd = ' '.join(real_cmd) + ' ' + cmd
+    else:
+        real_cmd += cmd
     return real_cmd
 
 def chdir(dir):
-    if os.name == 'nt':
-        os.environ['PWD']=dir
+    # P4 uses the PWD environment variable rather than getcwd(). Since we're
+    # not using the shell, we have to set it ourselves.
+    os.environ['PWD']=dir
     os.chdir(dir)
 
 def die(msg):
@@ -61,29 +64,34 @@ def die(msg):
         sys.stderr.write(msg + "\n")
         sys.exit(1)
 
-def write_pipe(c, str):
+def write_pipe(c, stdin):
     if verbose:
-        sys.stderr.write('Writing pipe: %s\n' % c)
+        sys.stderr.write('Writing pipe: %s\n' % str(c))
 
-    pipe = os.popen(c, 'w')
-    val = pipe.write(str)
-    if pipe.close():
-        die('Command failed: %s' % c)
+    expand = isinstance(c,basestring)
+    p = subprocess.Popen(c, stdin=subprocess.PIPE, shell=expand)
+    pipe = p.stdin
+    val = pipe.write(stdin)
+    pipe.close()
+    if p.wait():
+        die('Command failed: %s' % str(c))
 
     return val
 
-def p4_write_pipe(c, str):
+def p4_write_pipe(c, stdin):
     real_cmd = p4_build_cmd(c)
-    return write_pipe(real_cmd, str)
+    return write_pipe(real_cmd, stdin)
 
 def read_pipe(c, ignore_error=False):
     if verbose:
-        sys.stderr.write('Reading pipe: %s\n' % c)
+        sys.stderr.write('Reading pipe: %s\n' % str(c))
 
-    pipe = os.popen(c, 'rb')
+    expand = isinstance(c,basestring)
+    p = subprocess.Popen(c, stdout=subprocess.PIPE, shell=expand)
+    pipe = p.stdout
     val = pipe.read()
-    if pipe.close() and not ignore_error:
-        die('Command failed: %s' % c)
+    if p.wait() and not ignore_error:
+        die('Command failed: %s' % str(c))
 
     return val
 
@@ -93,12 +101,14 @@ def p4_read_pipe(c, ignore_error=False):
 
 def read_pipe_lines(c):
     if verbose:
-        sys.stderr.write('Reading pipe: %s\n' % c)
-    ## todo: check return status
-    pipe = os.popen(c, 'rb')
+        sys.stderr.write('Reading pipe: %s\n' % str(c))
+
+    expand = isinstance(c, basestring)
+    p = subprocess.Popen(c, stdout=subprocess.PIPE, shell=expand)
+    pipe = p.stdout
     val = pipe.readlines()
-    if pipe.close():
-        die('Command failed: %s' % c)
+    if pipe.close() or p.wait():
+        die('Command failed: %s' % str(c))
 
     return val
 
@@ -108,15 +118,37 @@ def p4_read_pipe_lines(c):
     return read_pipe_lines(real_cmd)
 
 def system(cmd):
+    expand = isinstance(cmd,basestring)
     if verbose:
-        sys.stderr.write("executing %s\n" % cmd)
-    if os.system(cmd) != 0:
-        die("command failed: %s" % cmd)
+        sys.stderr.write("executing %s\n" % str(cmd))
+    subprocess.check_call(cmd, shell=expand)
 
 def p4_system(cmd):
     """Specifically invoke p4 as the system command. """
     real_cmd = p4_build_cmd(cmd)
-    return system(real_cmd)
+    expand = isinstance(real_cmd, basestring)
+    subprocess.check_call(real_cmd, shell=expand)
+
+def p4_integrate(src, dest):
+    p4_system(["integrate", "-Dt", src, dest])
+
+def p4_sync(path):
+    p4_system(["sync", path])
+
+def p4_add(f):
+    p4_system(["add", f])
+
+def p4_delete(f):
+    p4_system(["delete", f])
+
+def p4_edit(f):
+    p4_system(["edit", f])
+
+def p4_revert(f):
+    p4_system(["revert", f])
+
+def p4_reopen(type, file):
+    p4_system(["reopen", "-t", type, file])
 
 #
 # Canonicalize the p4 type and return a tuple of the
@@ -167,12 +199,12 @@ def setP4ExecBit(file, mode):
         if p4Type[-1] == "+":
             p4Type = p4Type[0:-1]
 
-    p4_system("reopen -t %s %s" % (p4Type, file))
+    p4_reopen(p4Type, file)
 
 def getP4OpenedType(file):
     # Returns the perforce file type for the given file.
 
-    result = p4_read_pipe("opened %s" % file)
+    result = p4_read_pipe(["opened", file])
     match = re.match(".*\((.+)\)\r?$", result)
     if match:
         return match.group(1)
@@ -228,9 +260,17 @@ def isModeExecChanged(src_mode, dst_mode):
     return isModeExec(src_mode) != isModeExec(dst_mode)
 
 def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None):
-    cmd = p4_build_cmd("-G %s" % (cmd))
+
+    if isinstance(cmd,basestring):
+        cmd = "-G " + cmd
+        expand = True
+    else:
+        cmd = ["-G"] + cmd
+        expand = False
+
+    cmd = p4_build_cmd(cmd)
     if verbose:
-        sys.stderr.write("Opening pipe: %s\n" % cmd)
+        sys.stderr.write("Opening pipe: %s\n" % str(cmd))
 
     # Use a temporary file to avoid deadlocks without
     # subprocess.communicate(), which would put another copy
@@ -238,11 +278,16 @@ def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None):
     stdin_file = None
     if stdin is not None:
         stdin_file = tempfile.TemporaryFile(prefix='p4-stdin', mode=stdin_mode)
-        stdin_file.write(stdin)
+        if isinstance(stdin,basestring):
+            stdin_file.write(stdin)
+        else:
+            for i in stdin:
+                stdin_file.write(i + '\n')
         stdin_file.flush()
         stdin_file.seek(0)
 
-    p4 = subprocess.Popen(cmd, shell=True,
+    p4 = subprocess.Popen(cmd,
+                          shell=expand,
                           stdin=stdin_file,
                           stdout=subprocess.PIPE)
 
@@ -275,7 +320,7 @@ def p4Where(depotPath):
     if not depotPath.endswith("/"):
         depotPath += "/"
     depotPath = depotPath + "..."
-    outputList = p4CmdList("where %s" % depotPath)
+    outputList = p4CmdList(["where", depotPath])
     output = None
     for entry in outputList:
         if "depotFile" in entry:
@@ -477,8 +522,10 @@ def originP4BranchesExist():
 
 def p4ChangesForPaths(depotPaths, changeRange):
     assert depotPaths
-    output = p4_read_pipe_lines("changes " + ' '.join (["%s...%s" % (p, changeRange)
-                                                        for p in depotPaths]))
+    cmd = ['changes']
+    for p in depotPaths:
+        cmd += ["%s...%s" % (p, changeRange)]
+    output = p4_read_pipe_lines(cmd)
 
     changes = {}
     for line in output:
@@ -561,7 +608,7 @@ class P4Debug(Command):
 
     def run(self, args):
         j = 0
-        for output in p4CmdList(" ".join(args)):
+        for output in p4CmdList(args):
             print 'Element: %d' % j
             j += 1
             print output
@@ -715,7 +762,7 @@ class P4Submit(Command, P4UserMap):
                 break
         if not client:
             die("could not get client spec")
-        results = p4CmdList("changes -c %s -m 1" % client)
+        results = p4CmdList(["changes", "-c", client, "-m", "1"])
         for r in results:
             if r.has_key('change'):
                 return r['change']
@@ -778,7 +825,7 @@ class P4Submit(Command, P4UserMap):
         # remove lines in the Files section that show changes to files outside the depot path we're committing into
         template = ""
         inFilesSection = False
-        for line in p4_read_pipe_lines("change -o"):
+        for line in p4_read_pipe_lines(['change', '-o']):
             if line.endswith("\r\n"):
                 line = line[:-2] + "\n"
             if inFilesSection:
@@ -835,7 +882,7 @@ class P4Submit(Command, P4UserMap):
             modifier = diff['status']
             path = diff['src']
             if modifier == "M":
-                p4_system("edit \"%s\"" % path)
+                p4_edit(path)
                 if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
                     filesToChangeExecBit[path] = diff['dst_mode']
                 editedFiles.add(path)
@@ -850,21 +897,21 @@ class P4Submit(Command, P4UserMap):
                     filesToAdd.remove(path)
             elif modifier == "C":
                 src, dest = diff['src'], diff['dst']
-                p4_system("integrate -Dt \"%s\" \"%s\"" % (src, dest))
+                p4_integrate(src, dest)
                 if diff['src_sha1'] != diff['dst_sha1']:
-                    p4_system("edit \"%s\"" % (dest))
+                    p4_edit(dest)
                 if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
-                    p4_system("edit \"%s\"" % (dest))
+                    p4_edit(dest)
                     filesToChangeExecBit[dest] = diff['dst_mode']
                 os.unlink(dest)
                 editedFiles.add(dest)
             elif modifier == "R":
                 src, dest = diff['src'], diff['dst']
-                p4_system("integrate -Dt \"%s\" \"%s\"" % (src, dest))
+                p4_integrate(src, dest)
                 if diff['src_sha1'] != diff['dst_sha1']:
-                    p4_system("edit \"%s\"" % (dest))
+                    p4_edit(dest)
                 if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
-                    p4_system("edit \"%s\"" % (dest))
+                    p4_edit(dest)
                     filesToChangeExecBit[dest] = diff['dst_mode']
                 os.unlink(dest)
                 editedFiles.add(dest)
@@ -887,9 +934,9 @@ class P4Submit(Command, P4UserMap):
             if response == "s":
                 print "Skipping! Good luck with the next patches..."
                 for f in editedFiles:
-                    p4_system("revert \"%s\"" % f);
+                    p4_revert(f)
                 for f in filesToAdd:
-                    system("rm %s" %f)
+                    os.remove(f)
                 return
             elif response == "a":
                 os.system(applyPatchCmd)
@@ -910,10 +957,10 @@ class P4Submit(Command, P4UserMap):
         system(applyPatchCmd)
 
         for f in filesToAdd:
-            p4_system("add \"%s\"" % f)
+            p4_add(f)
         for f in filesToDelete:
-            p4_system("revert \"%s\"" % f)
-            p4_system("delete \"%s\"" % f)
+            p4_revert(f)
+            p4_delete(f)
 
         # Set/clear executable bits
         for f in filesToChangeExecBit.keys():
@@ -935,7 +982,7 @@ class P4Submit(Command, P4UserMap):
                 del(os.environ["P4DIFF"])
             diff = ""
             for editedFile in editedFiles:
-                diff += p4_read_pipe("diff -du %r" % editedFile)
+                diff += p4_read_pipe(['diff', '-du', editedFile])
 
             newdiff = ""
             for newFile in filesToAdd:
@@ -987,7 +1034,7 @@ class P4Submit(Command, P4UserMap):
                 submitTemplate = message[:message.index(separatorLine)]
                 if self.isWindows:
                     submitTemplate = submitTemplate.replace("\r\n", "\n")
-                p4_write_pipe("submit -i", submitTemplate)
+                p4_write_pipe(['submit', '-i'], submitTemplate)
 
                 if self.preserveUser:
                     if p4User:
@@ -998,10 +1045,10 @@ class P4Submit(Command, P4UserMap):
 
             else:
                 for f in editedFiles:
-                    p4_system("revert \"%s\"" % f);
+                    p4_revert(f)
                 for f in filesToAdd:
-                    p4_system("revert \"%s\"" % f);
-                    system("rm %s" %f)
+                    p4_revert(f)
+                    os.remove(f)
 
             os.remove(fileName)
         else:
@@ -1054,8 +1101,7 @@ class P4Submit(Command, P4UserMap):
 
         chdir(self.clientPath)
         print "Synchronizing p4 checkout..."
-        p4_system("sync ...")
-
+        p4_sync("...")
         self.check()
 
         commits = []
@@ -1269,7 +1315,7 @@ class P4Sync(Command, P4UserMap):
             # operations.  utf16 is converted to ascii or utf8, perhaps.
             # But ascii text saved as -t utf16 is completely mangled.
             # Invoke print -o to get the real contents.
-            text = p4_read_pipe('print -q -o - "%s"' % file['depotFile'])
+            text = p4_read_pipe(['print', '-q', '-o', '-', file['depotFile']])
             contents = [ text ]
 
         # Perhaps windows wants unicode, utf16 newlines translated too;
@@ -1365,10 +1411,11 @@ class P4Sync(Command, P4UserMap):
             def streamP4FilesCbSelf(entry):
                 self.streamP4FilesCb(entry)
 
-            p4CmdList("-x - print",
-                '\n'.join(['%s#%s' % (f['path'], f['rev'])
-                                                  for f in filesToRead]),
-                cb=streamP4FilesCbSelf)
+            fileArgs = ['%s#%s' % (f['path'], f['rev']) for f in filesToRead]
+
+            p4CmdList(["-x", "-", "print"],
+                      stdin=fileArgs,
+                      cb=streamP4FilesCbSelf)
 
             # do the last chunk
             if self.stream_file.has_key('depotFile'):
@@ -1429,8 +1476,8 @@ class P4Sync(Command, P4UserMap):
             if self.verbose:
                 print "Change %s is labelled %s" % (change, labelDetails)
 
-            files = p4CmdList("files " + ' '.join (["%s...@%s" % (p, change)
-                                                    for p in branchPrefixes]))
+            files = p4CmdList(["files"] + ["%s...@%s" % (p, change)
+                                                    for p in branchPrefixes])
 
             if len(files) == len(labelRevisions):
 
@@ -1478,9 +1525,9 @@ class P4Sync(Command, P4UserMap):
             newestChange = 0
             if self.verbose:
                 print "Querying files for label %s" % label
-            for file in p4CmdList("files "
-                                  +  ' '.join (["%s...@%s" % (p, label)
-                                                for p in self.depotPaths])):
+            for file in p4CmdList(["files"] +
+                                      ["%s...@%s" % (p, label)
+                                          for p in self.depotPaths]):
                 revisions[file["depotFile"]] = file["rev"]
                 change = int(file["change"])
                 if change > newestChange:
@@ -1735,10 +1782,9 @@ class P4Sync(Command, P4UserMap):
         newestRevision = 0
 
         fileCnt = 0
-        for info in p4CmdList("files "
-                              +  ' '.join(["%s...%s"
-                                           % (p, revision)
-                                           for p in self.depotPaths])):
+        fileArgs = ["%s...%s" % (p,revision) for p in self.depotPaths]
+
+        for info in p4CmdList(["files"] + fileArgs):
 
             if 'code' in info and info['code'] == 'error':
                 sys.stderr.write("p4 returned an error: %s\n"
diff --git a/t/t9803-git-shell-metachars.sh b/t/t9803-git-shell-metachars.sh
new file mode 100755
index 0000000..db04375
--- /dev/null
+++ b/t/t9803-git-shell-metachars.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+
+test_description='git-p4 transparency to shell metachars in filenames'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+	start_p4d
+'
+
+test_expect_success 'init depot' '
+	(
+		cd "$cli" &&
+		echo file1 >file1 &&
+		p4 add file1 &&
+		p4 submit -d "file1"
+	)
+'
+
+test_expect_success 'shell metachars in filenames' '
+	"$GITP4" clone --dest="$git" //depot &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEditCheck true &&
+		echo f1 >foo\$bar &&
+		git add foo\$bar &&
+		echo f2 >"file with spaces" &&
+		git add "file with spaces" &&
+		git commit -m "add files" &&
+		P4EDITOR=touch "$GITP4" submit
+	) &&
+	(
+		cd "$cli" &&
+		p4 sync ... &&
+		test -e "file with spaces" &&
+		test -e "foo\$bar"
+	)
+'
+
+test_expect_success 'deleting with shell metachars' '
+	"$GITP4" clone --dest="$git" //depot &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEditCheck true &&
+		git rm foo\$bar &&
+		git rm file\ with\ spaces &&
+		git commit -m "remove files" &&
+		P4EDITOR=touch "$GITP4" submit
+	) &&
+	(
+		cd "$cli" &&
+		p4 sync ... &&
+		test ! -e "file with spaces" &&
+		test ! -e foo\$bar
+	)
+'
+
+test_expect_success 'kill p4d' '
+	kill_p4d
+'
+
+test_done
-- 
1.7.6.3

^ permalink raw reply related

* [PATCH] fmt-merge-msg.c: Fix an "dubious one-bit signed bitfield" sparse error
From: Ramsay Jones @ 2011-10-15 17:46 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: GIT Mailing-list


Signed-off-by: Ramsay Jones <ramsay@ramsay1.demon.co.uk>
---

Hi Junio,

This was originally part of the patch I sent last week (applied to next
as commit 273c7032 - environment.c: Fix an sparse "symbol not declared" warning,
09-10-2011), since they both blame to commit 898eacd8 (fmt-merge-msg: use 
branch.$name.description, 06-10-2011).

However, sparse only issued this particular error on Linux, and not cygwin
or MinGW, so I decided to remove this part of the patch until I could
investigate further.

So, I had a look this afternoon and, *literally*, after no more than a
minute I realized why that is ... *blush*. Well, it may already be obvious,
that the Makefile has -Wno-one-bit-signed-bitfield set in the SPARSE_FLAGS
for cygwin and MinGW! *ahem* :-P  (now, who could have done that!)

This was mainly for the benefit of MinGW, since it otherwise issues 558 errors
(2 * 279 compilations); cygwin only results in 2 errors as follows:

        SP compat/cygwin.c
    /usr/include/w32api/winuser.h:3083:28: error: dubious one-bit signed bitfield
    /usr/include/w32api/winuser.h:3084:25: error: dubious one-bit signed bitfield

since only compat/cygwin.c, indirectly, includes that win32 header file.

Unfortunately, you can't tell sparse not to issue errors/warnings for system
header files. I could have lived with just the two errors on cygwin, but I wanted
at least one platform to be free of sparse errors/warnings. I remember thinking
that it would be OK to turn the messages off because I would notice them on
Linux anyway! Ha! :-P

BTW, t7800-difftool.sh fails for me on the next branch; I haven't investigated yet.

ATB,
Ramsay Jones

 builtin/fmt-merge-msg.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index 255a50f..7da6ff6 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -37,7 +37,7 @@ struct src_data {
 
 struct origin_data {
 	unsigned char sha1[20];
-	int is_local_branch:1;
+	unsigned int is_local_branch:1;
 };
 
 static void init_src_data(struct src_data *data)
-- 
1.7.7

^ permalink raw reply related

* [PATCH] grep: fix error message mention --exclude
From: Bert Wesarg @ 2011-10-15 18:36 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Bert Wesarg

Missing rename from --exclude to --standard-exclude.

Signed-off-by: Bert Wesarg <bert.wesarg@googlemail.com>
---
 builtin/grep.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/builtin/grep.c b/builtin/grep.c
index 024b878..7d0779f 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -1064,7 +1064,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
 			die(_("--no-index or --untracked cannot be used with revs."));
 		hit = grep_directory(&opt, &pathspec, use_exclude);
 	} else if (0 <= opt_exclude) {
-		die(_("--exclude or --no-exclude cannot be used for tracked contents."));
+		die(_("--[no-]exclude-standard cannot be used for tracked contents."));
 	} else if (!list.nr) {
 		if (!cached)
 			setup_work_tree();
-- 
1.7.6.789.gb4599

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox