{"thread":{"id":"28694","subject":"[PATCH v3 0/6] git-p4 tests, filetypes, shell metacharacters","startedAt":"2011-10-16T14:42:15Z","lastAt":"2011-10-16T22:48:33Z","messageCount":12,"participants":["Pete Wyckoff","Stefano Lattarini","Junio C Hamano"],"isPatch":true,"patchVersion":3,"patchTotal":6},"messages":[{"id":"177783","messageId":"20111016144215.GC22144@arf.padd.com","threadId":"28694","inReplyTo":null,"subject":"[PATCH v3 0/6] git-p4 tests, filetypes, shell metacharacters","fromName":"Pete Wyckoff","fromEmail":"pw@padd.com","sentAt":"2011-10-16T14:42:15Z","receivedAt":"2011-10-16T14:42:15Z","isPatch":true,"sender":{"key":"pw@padd.com","avatar":null},"body":"This series of six patches has been reviewed on the mailing list\nand should be ready for inclusion.  Changes from v2 to v3 are\nshell style and compatibility issues helpfully pointed out by\nStefano.\n\n1    - Clean up git-p4 tests, including Junio review comments\n\n2..4 - Handle p4 filetypes better, including Chris's suggested\n       utf16 fix\n\n5    - Fix p4 keyword parsing\n\n6    - From Luke: avoid using the shell, so filenames with metacharacters\n       are not errantly expanded\n\nLuke Diamand (1):\n      git-p4: handle files with shell metacharacters\n\nPete Wyckoff (5):\n      git-p4 tests: refactor and cleanup\n      git-p4: handle utf16 filetype properly\n      git-p4: recognize all p4 filetypes\n      git-p4: stop ignoring apple filetype\n      git-p4: keyword flattening fixes\n\n contrib/fast-import/git-p4     |  287 ++++++++++++++--------\n t/lib-git-p4.sh                |   74 ++++++\n t/t9800-git-p4.sh              |  544 +++++++++++++++++-----------------------\n t/t9801-git-p4-branch.sh       |  233 +++++++++++++++++\n t/t9802-git-p4-filetype.sh     |  108 ++++++++\n t/t9803-git-shell-metachars.sh |   64 +++++\n 6 files changed, 903 insertions(+), 407 deletions(-)\n create mode 100644 t/lib-git-p4.sh\n create mode 100755 t/t9801-git-p4-branch.sh\n create mode 100755 t/t9802-git-p4-filetype.sh\n create mode 100755 t/t9803-git-shell-metachars.sh\n"},{"id":"177784","messageId":"20111016144319.GD22144@arf.padd.com","threadId":"28694","inReplyTo":"20111016144215.GC22144@arf.padd.com","subject":"[PATCH 1/6] git-p4 tests: refactor and cleanup","fromName":"Pete Wyckoff","fromEmail":"pw@padd.com","sentAt":"2011-10-16T14:43:19Z","receivedAt":"2011-10-16T14:43:19Z","isPatch":true,"sender":{"key":"pw@padd.com","avatar":null},"body":"Introduce a library for functions that are common to\nmultiple git-p4 test files.\n\nBe a bit more clever about starting and stopping p4d.\nSpecify a unique port number for each test, so that\ntests can run in parallel.  Start p4d not in daemon mode,\nand save the pid, to be able to kill it cleanly later.\nNever kill p4d at startup; always shutdown cleanly.\n\nHandle directory changes better.  Always chdir inside\na subshell, and remove any post-test directory changes.\n\nClean up whitespace, and use test_cmp and test_must_fail\nmore consistently.\n\nSeparate the tests related to detecting p4 branches\ninto their own file, and add a few more.\n\nAcked-by: Luke Diamand <luke@diamand.org>\nSigned-off-by: Pete Wyckoff <pw@padd.com>\n---\n t/lib-git-p4.sh          |   74 +++++++\n t/t9800-git-p4.sh        |  544 ++++++++++++++++++++--------------------------\n t/t9801-git-p4-branch.sh |  233 ++++++++++++++++++++\n 3 files changed, 543 insertions(+), 308 deletions(-)\n create mode 100644 t/lib-git-p4.sh\n create mode 100755 t/t9801-git-p4-branch.sh\n\ndiff --git a/t/lib-git-p4.sh b/t/lib-git-p4.sh\nnew file mode 100644\nindex 0000000..a870f9a\n--- /dev/null\n+++ b/t/lib-git-p4.sh\n@@ -0,0 +1,74 @@\n+#\n+# Library code for git-p4 tests\n+#\n+\n+. ./test-lib.sh\n+\n+if ! test_have_prereq PYTHON; then\n+\tskip_all='skipping git-p4 tests; python not available'\n+\ttest_done\n+fi\n+( p4 -h && p4d -h ) >/dev/null 2>&1 || {\n+\tskip_all='skipping git-p4 tests; no p4 or p4d'\n+\ttest_done\n+}\n+\n+GITP4=\"$GIT_BUILD_DIR/contrib/fast-import/git-p4\"\n+\n+# Try to pick a unique port: guess a large number, then hope\n+# no more than one of each test is running.\n+#\n+# This does not handle the case where somebody else is running the\n+# same tests and has chosen the same ports.\n+testid=${this_test#t}\n+git_p4_test_start=9800\n+P4DPORT=$((10669 + ($testid - $git_p4_test_start)))\n+\n+export P4PORT=localhost:$P4DPORT\n+export P4CLIENT=client\n+\n+db=\"$TRASH_DIRECTORY/db\"\n+cli=\"$TRASH_DIRECTORY/cli\"\n+git=\"$TRASH_DIRECTORY/git\"\n+pidfile=\"$TRASH_DIRECTORY/p4d.pid\"\n+\n+start_p4d() {\n+\tmkdir -p \"$db\" \"$cli\" \"$git\" &&\n+\t(\n+\t\tp4d -q -r \"$db\" -p $P4DPORT &\n+\t\techo $! >\"$pidfile\"\n+\t) &&\n+\tfor i in 1 2 3 4 5 ; do\n+\t\tp4 info >/dev/null 2>&1 && break || true &&\n+\t\techo waiting for p4d to start &&\n+\t\tsleep 1\n+\tdone &&\n+\t# complain if it never started\n+\tp4 info >/dev/null &&\n+\t(\n+\t\tcd \"$cli\" &&\n+\t\tp4 client -i <<-EOF\n+\t\tClient: client\n+\t\tDescription: client\n+\t\tRoot: $cli\n+\t\tView: //depot/... //client/...\n+\t\tEOF\n+\t)\n+}\n+\n+kill_p4d() {\n+\tpid=$(cat \"$pidfile\")\n+\t# it had better exist for the first kill\n+\tkill $pid &&\n+\tfor i in 1 2 3 4 5 ; do\n+\t\tkill $pid >/dev/null 2>&1 || break\n+\t\tsleep 1\n+\tdone &&\n+\t# complain if it would not die\n+\ttest_must_fail kill $pid >/dev/null 2>&1 &&\n+\trm -rf \"$db\" \"$cli\" \"$pidfile\"\n+}\n+\n+cleanup_git() {\n+\trm -rf \"$git\"\n+}\ndiff --git a/t/t9800-git-p4.sh b/t/t9800-git-p4.sh\nindex 01ba041..8fbed66 100755\n--- a/t/t9800-git-p4.sh\n+++ b/t/t9800-git-p4.sh\n@@ -2,76 +2,51 @@\n \n test_description='git-p4 tests'\n \n-. ./test-lib.sh\n+. ./lib-git-p4.sh\n \n-( p4 -h && p4d -h ) >/dev/null 2>&1 || {\n-\tskip_all='skipping git-p4 tests; no p4 or p4d'\n-\ttest_done\n-}\n-\n-GITP4=$GIT_BUILD_DIR/contrib/fast-import/git-p4\n-P4DPORT=10669\n-\n-export P4PORT=localhost:$P4DPORT\n-\n-db=\"$TRASH_DIRECTORY/db\"\n-cli=\"$TRASH_DIRECTORY/cli\"\n-git=\"$TRASH_DIRECTORY/git\"\n-\n-test_debug 'echo p4d -q -d -r \"$db\" -p $P4DPORT'\n-test_expect_success setup '\n-\tmkdir -p \"$db\" &&\n-\tp4d -q -d -r \"$db\" -p $P4DPORT &&\n-\tmkdir -p \"$cli\" &&\n-\tmkdir -p \"$git\" &&\n-\texport P4PORT=localhost:$P4DPORT\n+test_expect_success 'start p4d' '\n+\tstart_p4d\n '\n \n test_expect_success 'add p4 files' '\n-\tcd \"$cli\" &&\n-\tp4 client -i <<-EOF &&\n-\tClient: client\n-\tDescription: client\n-\tRoot: $cli\n-\tView: //depot/... //client/...\n-\tEOF\n-\texport P4CLIENT=client &&\n-\techo file1 >file1 &&\n-\tp4 add file1 &&\n-\tp4 submit -d \"file1\" &&\n-\techo file2 >file2 &&\n-\tp4 add file2 &&\n-\tp4 submit -d \"file2\" &&\n-\tcd \"$TRASH_DIRECTORY\"\n+\t(\n+\t\tcd \"$cli\" &&\n+\t\techo file1 >file1 &&\n+\t\tp4 add file1 &&\n+\t\tp4 submit -d \"file1\" &&\n+\t\techo file2 >file2 &&\n+\t\tp4 add file2 &&\n+\t\tp4 submit -d \"file2\"\n+\t)\n '\n \n-cleanup_git() {\n-\tcd \"$TRASH_DIRECTORY\" &&\n-\trm -rf \"$git\" &&\n-\tmkdir \"$git\"\n-}\n-\n test_expect_success 'basic git-p4 clone' '\n \t\"$GITP4\" clone --dest=\"$git\" //depot &&\n \ttest_when_finished cleanup_git &&\n-\tcd \"$git\" &&\n-\tgit log --oneline >lines &&\n-\ttest_line_count = 1 lines\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgit log --oneline >lines &&\n+\t\ttest_line_count = 1 lines\n+\t)\n '\n \n test_expect_success 'git-p4 clone @all' '\n \t\"$GITP4\" clone --dest=\"$git\" //depot@all &&\n \ttest_when_finished cleanup_git &&\n-\tcd \"$git\" &&\n-\tgit log --oneline >lines &&\n-\ttest_line_count = 2 lines\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgit log --oneline >lines &&\n+\t\ttest_line_count = 2 lines\n+\t)\n '\n \n test_expect_success 'git-p4 sync uninitialized repo' '\n \ttest_create_repo \"$git\" &&\n \ttest_when_finished cleanup_git &&\n-\tcd \"$git\" &&\n-\ttest_must_fail \"$GITP4\" sync\n+\t(\n+\t\tcd \"$git\" &&\n+\t\ttest_must_fail \"$GITP4\" sync\n+\t)\n '\n \n #\n@@ -81,17 +56,19 @@ test_expect_success 'git-p4 sync uninitialized repo' '\n test_expect_success 'git-p4 sync new branch' '\n \ttest_create_repo \"$git\" &&\n \ttest_when_finished cleanup_git &&\n-\tcd \"$git\" &&\n-\ttest_commit head &&\n-\t\"$GITP4\" sync --branch=refs/remotes/p4/depot //depot@all &&\n-\tgit log --oneline p4/depot >lines &&\n-\ttest_line_count = 2 lines\n+\t(\n+\t\tcd \"$git\" &&\n+\t\ttest_commit head &&\n+\t\t\"$GITP4\" sync --branch=refs/remotes/p4/depot //depot@all &&\n+\t\tgit log --oneline p4/depot >lines &&\n+\t\ttest_line_count = 2 lines\n+\t)\n '\n \n test_expect_success 'exit when p4 fails to produce marshaled output' '\n \tbadp4dir=\"$TRASH_DIRECTORY/badp4dir\" &&\n-\tmkdir -p \"$badp4dir\" &&\n-\ttest_when_finished \"rm -rf $badp4dir\" &&\n+\tmkdir \"$badp4dir\" &&\n+\ttest_when_finished \"rm \\\"$badp4dir/p4\\\" && rmdir \\\"$badp4dir\\\"\" &&\n \tcat >\"$badp4dir\"/p4 <<-EOF &&\n \t#!$SHELL_PATH\n \texit 1\n@@ -103,61 +80,61 @@ test_expect_success 'exit when p4 fails to produce marshaled output' '\n '\n \n test_expect_success 'add p4 files with wildcards in the names' '\n-\tcd \"$cli\" &&\n-\techo file-wild-hash >file-wild#hash &&\n-\techo file-wild-star >file-wild\\*star &&\n-\techo file-wild-at >file-wild@at &&\n-\techo file-wild-percent >file-wild%percent &&\n-\tp4 add -f file-wild* &&\n-\tp4 submit -d \"file wildcards\"\n+\t(\n+\t\tcd \"$cli\" &&\n+\t\techo file-wild-hash >file-wild#hash &&\n+\t\techo file-wild-star >file-wild\\*star &&\n+\t\techo file-wild-at >file-wild@at &&\n+\t\techo file-wild-percent >file-wild%percent &&\n+\t\tp4 add -f file-wild* &&\n+\t\tp4 submit -d \"file wildcards\"\n+\t)\n '\n \n test_expect_success 'wildcard files git-p4 clone' '\n \t\"$GITP4\" clone --dest=\"$git\" //depot &&\n \ttest_when_finished cleanup_git &&\n-\tcd \"$git\" &&\n-\ttest -f file-wild#hash &&\n-\ttest -f file-wild\\*star &&\n-\ttest -f file-wild@at &&\n-\ttest -f file-wild%percent\n+\t(\n+\t\tcd \"$git\" &&\n+\t\ttest -f file-wild#hash &&\n+\t\ttest -f file-wild\\*star &&\n+\t\ttest -f file-wild@at &&\n+\t\ttest -f file-wild%percent\n+\t)\n '\n \n test_expect_success 'clone bare' '\n \t\"$GITP4\" clone --dest=\"$git\" --bare //depot &&\n \ttest_when_finished cleanup_git &&\n-\tcd \"$git\" &&\n-\ttest ! -d .git &&\n-\tbare=`git config --get core.bare` &&\n-\ttest \"$bare\" = true\n+\t(\n+\t\tcd \"$git\" &&\n+\t\ttest ! -d .git &&\n+\t\tbare=`git config --get core.bare` &&\n+\t\ttest \"$bare\" = true\n+\t)\n '\n \n p4_add_user() {\n-    name=$1\n-    fullname=$2\n-    p4 user -f -i <<EOF &&\n-User: $name\n-Email: $name@localhost\n-FullName: $fullname\n-EOF\n-    p4 passwd -P secret $name\n+\tname=$1 fullname=$2 &&\n+\tp4 user -f -i <<-EOF &&\n+\tUser: $name\n+\tEmail: $name@localhost\n+\tFullName: $fullname\n+\tEOF\n+\tp4 passwd -P secret $name\n }\n \n p4_grant_admin() {\n-    name=$1\n-    p4 protect -o |\\\n-\tawk \"{print}END{print \\\"    admin user $name * //depot/...\\\"}\" |\\\n-\tp4 protect -i\n+\tname=$1 &&\n+\t{\n+\t\tp4 protect -o &&\n+\t\techo \"    admin user $name * //depot/...\"\n+\t} | p4 protect -i\n }\n \n p4_check_commit_author() {\n-    file=$1\n-    user=$2\n-    if p4 changes -m 1 //depot/$file | grep $user > /dev/null ; then\n-\treturn 0\n-    else\n-\techo \"file $file not modified by user $user\" 1>&2\n-\treturn 1\n-    fi\n+\tfile=$1 user=$2 &&\n+\tp4 changes -m 1 //depot/$file | grep -q $user\n }\n \n make_change_by_user() {\n@@ -174,15 +151,17 @@ test_expect_success 'preserve users' '\n \tp4_grant_admin alice &&\n \t\"$GITP4\" clone --dest=\"$git\" //depot &&\n \ttest_when_finished cleanup_git &&\n-\tcd \"$git\" &&\n-\techo \"username: a change by alice\" >> file1 &&\n-\techo \"username: a change by bob\" >> file2 &&\n-\tgit commit --author \"Alice <alice@localhost>\" -m \"a change by alice\" file1 &&\n-\tgit commit --author \"Bob <bob@localhost>\" -m \"a change by bob\" file2 &&\n-\tgit config git-p4.skipSubmitEditCheck true &&\n-\tP4EDITOR=touch P4USER=alice P4PASSWD=secret \"$GITP4\" commit --preserve-user &&\n-\tp4_check_commit_author file1 alice &&\n-\tp4_check_commit_author file2 bob\n+\t(\n+\t\tcd \"$git\" &&\n+\t\techo \"username: a change by alice\" >>file1 &&\n+\t\techo \"username: a change by bob\" >>file2 &&\n+\t\tgit commit --author \"Alice <alice@localhost>\" -m \"a change by alice\" file1 &&\n+\t\tgit commit --author \"Bob <bob@localhost>\" -m \"a change by bob\" file2 &&\n+\t\tgit config git-p4.skipSubmitEditCheck true &&\n+\t\tP4EDITOR=touch P4USER=alice P4PASSWD=secret \"$GITP4\" commit --preserve-user &&\n+\t\tp4_check_commit_author file1 alice &&\n+\t\tp4_check_commit_author file2 bob\n+\t)\n '\n \n # Test username support, submitting as bob, who lacks admin rights. Should\n@@ -190,32 +169,37 @@ test_expect_success 'preserve users' '\n test_expect_success 'refuse to preserve users without perms' '\n \t\"$GITP4\" clone --dest=\"$git\" //depot &&\n \ttest_when_finished cleanup_git &&\n-\tcd \"$git\" &&\n-\tgit config git-p4.skipSubmitEditCheck true &&\n-\techo \"username-noperms: a change by alice\" >> file1 &&\n-\tgit commit --author \"Alice <alice@localhost>\" -m \"perms: a change by alice\" file1 &&\n-\t! P4EDITOR=touch P4USER=bob P4PASSWD=secret \"$GITP4\" commit --preserve-user &&\n-\t! git diff --exit-code HEAD..p4/master > /dev/null\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgit config git-p4.skipSubmitEditCheck true &&\n+\t\techo \"username-noperms: a change by alice\" >>file1 &&\n+\t\tgit commit --author \"Alice <alice@localhost>\" -m \"perms: a change by alice\" file1 &&\n+\t\tP4EDITOR=touch P4USER=bob P4PASSWD=secret test_must_fail \"$GITP4\" commit --preserve-user &&\n+\t\ttest_must_fail git diff --exit-code HEAD..p4/master\n+\t)\n '\n \n # What happens with unknown author? Without allowMissingP4Users it should fail.\n test_expect_success 'preserve user where author is unknown to p4' '\n \t\"$GITP4\" clone --dest=\"$git\" //depot &&\n \ttest_when_finished cleanup_git &&\n-\tcd \"$git\" &&\n-\tgit config git-p4.skipSubmitEditCheck true &&\n-\techo \"username-bob: a change by bob\" >> file1 &&\n-\tgit commit --author \"Bob <bob@localhost>\" -m \"preserve: a change by bob\" file1 &&\n-\techo \"username-unknown: a change by charlie\" >> file1 &&\n-\tgit commit --author \"Charlie <charlie@localhost>\" -m \"preserve: a change by charlie\" file1 &&\n-\t! P4EDITOR=touch P4USER=alice P4PASSWD=secret \"$GITP4\" commit --preserve-user &&\n-\t! git diff --exit-code HEAD..p4/master > /dev/null &&\n-\techo \"$0: repeat with allowMissingP4Users enabled\" &&\n-\tgit config git-p4.allowMissingP4Users true &&\n-\tgit config git-p4.preserveUser true &&\n-\tP4EDITOR=touch P4USER=alice P4PASSWD=secret \"$GITP4\" commit &&\n-\tgit diff --exit-code HEAD..p4/master > /dev/null &&\n-\tp4_check_commit_author file1 alice\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgit config git-p4.skipSubmitEditCheck true &&\n+\t\techo \"username-bob: a change by bob\" >>file1 &&\n+\t\tgit commit --author \"Bob <bob@localhost>\" -m \"preserve: a change by bob\" file1 &&\n+\t\techo \"username-unknown: a change by charlie\" >>file1 &&\n+\t\tgit commit --author \"Charlie <charlie@localhost>\" -m \"preserve: a change by charlie\" file1 &&\n+\t\tP4EDITOR=touch P4USER=alice P4PASSWD=secret test_must_fail \"$GITP4\" commit --preserve-user &&\n+\t\ttest_must_fail git diff --exit-code HEAD..p4/master &&\n+\n+\t\techo \"$0: repeat with allowMissingP4Users enabled\" &&\n+\t\tgit config git-p4.allowMissingP4Users true &&\n+\t\tgit config git-p4.preserveUser true &&\n+\t\tP4EDITOR=touch P4USER=alice P4PASSWD=secret \"$GITP4\" commit &&\n+\t\tgit diff --exit-code HEAD..p4/master &&\n+\t\tp4_check_commit_author file1 alice\n+\t)\n '\n \n # If we're *not* using --preserve-user, git-p4 should warn if we're submitting\n@@ -225,28 +209,30 @@ test_expect_success 'preserve user where author is unknown to p4' '\n test_expect_success 'not preserving user with mixed authorship' '\n \t\"$GITP4\" clone --dest=\"$git\" //depot &&\n \ttest_when_finished cleanup_git &&\n-\tcd \"$git\" &&\n-\tgit config git-p4.skipSubmitEditCheck true &&\n-\tp4_add_user derek Derek &&\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgit config git-p4.skipSubmitEditCheck true &&\n+\t\tp4_add_user derek Derek &&\n \n-\tmake_change_by_user usernamefile3 Derek derek@localhost &&\n-\tP4EDITOR=cat P4USER=alice P4PASSWD=secret \"$GITP4\" commit >actual &&\n-\tgrep \"git author derek@localhost does not match\" actual &&\n+\t\tmake_change_by_user usernamefile3 Derek derek@localhost &&\n+\t\tP4EDITOR=cat P4USER=alice P4PASSWD=secret \"$GITP4\" commit |\\\n+\t\tgrep \"git author derek@localhost does not match\" &&\n \n-\tmake_change_by_user usernamefile3 Charlie charlie@localhost &&\n-\tP4EDITOR=cat P4USER=alice P4PASSWD=secret \"$GITP4\" commit >actual &&\n-\tgrep \"git author charlie@localhost does not match\" actual &&\n+\t\tmake_change_by_user usernamefile3 Charlie charlie@localhost &&\n+\t\tP4EDITOR=cat P4USER=alice P4PASSWD=secret \"$GITP4\" commit |\\\n+\t\tgrep \"git author charlie@localhost does not match\" &&\n \n-\tmake_change_by_user usernamefile3 alice alice@localhost &&\n-\tP4EDITOR=cat P4USER=alice P4PASSWD=secret \"$GITP4\" commit >actual &&\n-\t! grep \"git author.*does not match\" actual &&\n+\t\tmake_change_by_user usernamefile3 alice alice@localhost &&\n+\t\tP4EDITOR=cat P4USER=alice P4PASSWD=secret \"$GITP4\" |\\\n+\t\ttest_must_fail grep \"git author.*does not match\" &&\n \n-\tgit config git-p4.skipUserNameCheck true &&\n-\tmake_change_by_user usernamefile3 Charlie charlie@localhost &&\n-\tP4EDITOR=cat P4USER=alice P4PASSWD=secret \"$GITP4\" commit >actual &&\n-\t! grep \"git author.*does not match\" actual &&\n+\t\tgit config git-p4.skipUserNameCheck true &&\n+\t\tmake_change_by_user usernamefile3 Charlie charlie@localhost &&\n+\t\tP4EDITOR=cat P4USER=alice P4PASSWD=secret \"$GITP4\" commit |\\\n+\t\ttest_must_fail grep \"git author.*does not match\" &&\n \n-\tp4_check_commit_author usernamefile3 alice\n+\t\tp4_check_commit_author usernamefile3 alice\n+\t)\n '\n \n marshal_dump() {\n@@ -263,10 +249,12 @@ test_expect_success 'initial import time from top change time' '\n \tsleep 3 &&\n \t\"$GITP4\" clone --dest=\"$git\" //depot &&\n \ttest_when_finished cleanup_git &&\n-\tcd \"$git\" &&\n-\tgittime=$(git show -s --raw --pretty=format:%at HEAD) &&\n-\techo $p4time $gittime &&\n-\ttest $p4time = $gittime\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgittime=$(git show -s --raw --pretty=format:%at HEAD) &&\n+\t\techo $p4time $gittime &&\n+\t\ttest $p4time = $gittime\n+\t)\n '\n \n # Rename a file and confirm that rename is not detected in P4.\n@@ -279,47 +267,49 @@ test_expect_success 'initial import time from top change time' '\n test_expect_success 'detect renames' '\n \t\"$GITP4\" clone --dest=\"$git\" //depot@all &&\n \ttest_when_finished cleanup_git &&\n-\tcd \"$git\" &&\n-\tgit config git-p4.skipSubmitEditCheck true &&\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgit config git-p4.skipSubmitEditCheck true &&\n \n-\tgit mv file1 file4 &&\n-\tgit commit -a -m \"Rename file1 to file4\" &&\n-\tgit diff-tree -r -M HEAD &&\n-\t\"$GITP4\" submit &&\n-\tp4 filelog //depot/file4 &&\n-\t! p4 filelog //depot/file4 | grep -q \"branch from\" &&\n+\t\tgit mv file1 file4 &&\n+\t\tgit commit -a -m \"Rename file1 to file4\" &&\n+\t\tgit diff-tree -r -M HEAD &&\n+\t\t\"$GITP4\" submit &&\n+\t\tp4 filelog //depot/file4 &&\n+\t\tp4 filelog //depot/file4 | test_must_fail grep -q \"branch from\" &&\n \n-\tgit mv file4 file5 &&\n-\tgit commit -a -m \"Rename file4 to file5\" &&\n-\tgit diff-tree -r -M HEAD &&\n-\tgit config git-p4.detectRenames true &&\n-\t\"$GITP4\" submit &&\n-\tp4 filelog //depot/file5 &&\n-\tp4 filelog //depot/file5 | grep -q \"branch from //depot/file4\" &&\n+\t\tgit mv file4 file5 &&\n+\t\tgit commit -a -m \"Rename file4 to file5\" &&\n+\t\tgit diff-tree -r -M HEAD &&\n+\t\tgit config git-p4.detectRenames true &&\n+\t\t\"$GITP4\" submit &&\n+\t\tp4 filelog //depot/file5 &&\n+\t\tp4 filelog //depot/file5 | grep -q \"branch from //depot/file4\" &&\n \n-\tgit mv file5 file6 &&\n-\techo update >>file6 &&\n-\tgit add file6 &&\n-\tgit commit -a -m \"Rename file5 to file6 with changes\" &&\n-\tgit diff-tree -r -M HEAD &&\n-\tlevel=$(git diff-tree -r -M HEAD | sed 1d | cut -f1 | cut -d\" \" -f5 | sed \"s/R0*//\") &&\n-\ttest -n \"$level\" && test \"$level\" -gt 0 && test \"$level\" -lt 98 &&\n-\tgit config git-p4.detectRenames $((level + 2)) &&\n-\t\"$GITP4\" submit &&\n-\tp4 filelog //depot/file6 &&\n-\t! p4 filelog //depot/file6 | grep -q \"branch from\" &&\n+\t\tgit mv file5 file6 &&\n+\t\techo update >>file6 &&\n+\t\tgit add file6 &&\n+\t\tgit commit -a -m \"Rename file5 to file6 with changes\" &&\n+\t\tgit diff-tree -r -M HEAD &&\n+\t\tlevel=$(git diff-tree -r -M HEAD | sed 1d | cut -f1 | cut -d\" \" -f5 | sed \"s/R0*//\") &&\n+\t\ttest -n \"$level\" && test \"$level\" -gt 0 && test \"$level\" -lt 98 &&\n+\t\tgit config git-p4.detectRenames $(($level + 2)) &&\n+\t\t\"$GITP4\" submit &&\n+\t\tp4 filelog //depot/file6 &&\n+\t\tp4 filelog //depot/file6 | test_must_fail grep -q \"branch from\" &&\n \n-\tgit mv file6 file7 &&\n-\techo update >>file7 &&\n-\tgit add file7 &&\n-\tgit commit -a -m \"Rename file6 to file7 with changes\" &&\n-\tgit diff-tree -r -M HEAD &&\n-\tlevel=$(git diff-tree -r -M HEAD | sed 1d | cut -f1 | cut -d\" \" -f5 | sed \"s/R0*//\") &&\n-\ttest -n \"$level\" && test \"$level\" -gt 2 && test \"$level\" -lt 100 &&\n-\tgit config git-p4.detectRenames $((level - 2)) &&\n-\t\"$GITP4\" submit &&\n-\tp4 filelog //depot/file7 &&\n-\tp4 filelog //depot/file7 | grep -q \"branch from //depot/file6\"\n+\t\tgit mv file6 file7 &&\n+\t\techo update >>file7 &&\n+\t\tgit add file7 &&\n+\t\tgit commit -a -m \"Rename file6 to file7 with changes\" &&\n+\t\tgit diff-tree -r -M HEAD &&\n+\t\tlevel=$(git diff-tree -r -M HEAD | sed 1d | cut -f1 | cut -d\" \" -f5 | sed \"s/R0*//\") &&\n+\t\ttest -n \"$level\" && test \"$level\" -gt 2 && test \"$level\" -lt 100 &&\n+\t\tgit config git-p4.detectRenames $(($level - 2)) &&\n+\t\t\"$GITP4\" submit &&\n+\t\tp4 filelog //depot/file7 &&\n+\t\tp4 filelog //depot/file7 | grep -q \"branch from //depot/file6\"\n+\t)\n '\n \n # Copy a file and confirm that copy is not detected in P4.\n@@ -336,141 +326,79 @@ test_expect_success 'detect renames' '\n test_expect_success 'detect copies' '\n \t\"$GITP4\" clone --dest=\"$git\" //depot@all &&\n \ttest_when_finished cleanup_git &&\n-\tcd \"$git\" &&\n-\tgit config git-p4.skipSubmitEditCheck true &&\n-\n-\tcp file2 file8 &&\n-\tgit add file8 &&\n-\tgit commit -a -m \"Copy file2 to file8\" &&\n-\tgit diff-tree -r -C HEAD &&\n-\t\"$GITP4\" submit &&\n-\tp4 filelog //depot/file8 &&\n-\t! p4 filelog //depot/file8 | grep -q \"branch from\" &&\n-\n-\tcp file2 file9 &&\n-\tgit add file9 &&\n-\tgit commit -a -m \"Copy file2 to file9\" &&\n-\tgit diff-tree -r -C HEAD &&\n-\tgit config git-p4.detectCopies true &&\n-\t\"$GITP4\" submit &&\n-\tp4 filelog //depot/file9 &&\n-\t! p4 filelog //depot/file9 | grep -q \"branch from\" &&\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgit config git-p4.skipSubmitEditCheck true &&\n \n-\techo \"file2\" >>file2 &&\n-\tcp file2 file10 &&\n-\tgit add file2 file10 &&\n-\tgit commit -a -m \"Modify and copy file2 to file10\" &&\n-\tgit diff-tree -r -C HEAD &&\n-\t\"$GITP4\" submit &&\n-\tp4 filelog //depot/file10 &&\n-\tp4 filelog //depot/file10 | grep -q \"branch from //depot/file\" &&\n+\t\tcp file2 file8 &&\n+\t\tgit add file8 &&\n+\t\tgit commit -a -m \"Copy file2 to file8\" &&\n+\t\tgit diff-tree -r -C HEAD &&\n+\t\t\"$GITP4\" submit &&\n+\t\tp4 filelog //depot/file8 &&\n+\t\tp4 filelog //depot/file8 | test_must_fail grep -q \"branch from\" &&\n \n-\tcp file2 file11 &&\n-\tgit add file11 &&\n-\tgit commit -a -m \"Copy file2 to file11\" &&\n-\tgit diff-tree -r -C --find-copies-harder HEAD &&\n-\tsrc=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) &&\n-\ttest \"$src\" = file10 &&\n-\tgit config git-p4.detectCopiesHarder true &&\n-\t\"$GITP4\" submit &&\n-\tp4 filelog //depot/file11 &&\n-\tp4 filelog //depot/file11 | grep -q \"branch from //depot/file\" &&\n+\t\tcp file2 file9 &&\n+\t\tgit add file9 &&\n+\t\tgit commit -a -m \"Copy file2 to file9\" &&\n+\t\tgit diff-tree -r -C HEAD &&\n+\t\tgit config git-p4.detectCopies true &&\n+\t\t\"$GITP4\" submit &&\n+\t\tp4 filelog //depot/file9 &&\n+\t\tp4 filelog //depot/file9 | test_must_fail grep -q \"branch from\" &&\n \n-\tcp file2 file12 &&\n-\techo \"some text\" >>file12 &&\n-\tgit add file12 &&\n-\tgit commit -a -m \"Copy file2 to file12 with changes\" &&\n-\tgit diff-tree -r -C --find-copies-harder HEAD &&\n-\tlevel=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f1 | cut -d\" \" -f5 | sed \"s/C0*//\") &&\n-\ttest -n \"$level\" && test \"$level\" -gt 0 && test \"$level\" -lt 98 &&\n-\tsrc=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) &&\n-\ttest \"$src\" = file10 &&\n-\tgit config git-p4.detectCopies $((level + 2)) &&\n-\t\"$GITP4\" submit &&\n-\tp4 filelog //depot/file12 &&\n-\t! p4 filelog //depot/file12 | grep -q \"branch from\" &&\n+\t\techo \"file2\" >>file2 &&\n+\t\tcp file2 file10 &&\n+\t\tgit add file2 file10 &&\n+\t\tgit commit -a -m \"Modify and copy file2 to file10\" &&\n+\t\tgit diff-tree -r -C HEAD &&\n+\t\t\"$GITP4\" submit &&\n+\t\tp4 filelog //depot/file10 &&\n+\t\tp4 filelog //depot/file10 | grep -q \"branch from //depot/file\" &&\n \n-\tcp file2 file13 &&\n-\techo \"different text\" >>file13 &&\n-\tgit add file13 &&\n-\tgit commit -a -m \"Copy file2 to file13 with changes\" &&\n-\tgit diff-tree -r -C --find-copies-harder HEAD &&\n-\tlevel=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f1 | cut -d\" \" -f5 | sed \"s/C0*//\") &&\n-\ttest -n \"$level\" && test \"$level\" -gt 2 && test \"$level\" -lt 100 &&\n-\tsrc=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) &&\n-\ttest \"$src\" = file10 &&\n-\tgit config git-p4.detectCopies $((level - 2)) &&\n-\t\"$GITP4\" submit &&\n-\tp4 filelog //depot/file13 &&\n-\tp4 filelog //depot/file13 | grep -q \"branch from //depot/file\"\n-'\n+\t\tcp file2 file11 &&\n+\t\tgit add file11 &&\n+\t\tgit commit -a -m \"Copy file2 to file11\" &&\n+\t\tgit diff-tree -r -C --find-copies-harder HEAD &&\n+\t\tsrc=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) &&\n+\t\ttest \"$src\" = file10 &&\n+\t\tgit config git-p4.detectCopiesHarder true &&\n+\t\t\"$GITP4\" submit &&\n+\t\tp4 filelog //depot/file11 &&\n+\t\tp4 filelog //depot/file11 | grep -q \"branch from //depot/file\" &&\n \n-# Create a simple branch structure in P4 depot to check if it is correctly\n-# cloned.\n-test_expect_success 'add simple p4 branches' '\n-\tcd \"$cli\" &&\n-\tmkdir branch1 &&\n-\tcd branch1 &&\n-\techo file1 >file1 &&\n-\techo file2 >file2 &&\n-\tp4 add file1 file2 &&\n-\tp4 submit -d \"branch1\" &&\n-\tp4 integrate //depot/branch1/... //depot/branch2/... &&\n-\tp4 submit -d \"branch2\" &&\n-\techo file3 >file3 &&\n-\tp4 add file3 &&\n-\tp4 submit -d \"add file3 in branch1\" &&\n-\tp4 open file2 &&\n-\techo update >>file2 &&\n-\tp4 submit -d \"update file2 in branch1\" &&\n-\tp4 integrate //depot/branch1/... //depot/branch3/... &&\n-\tp4 submit -d \"branch3\" &&\n-\tcd \"$TRASH_DIRECTORY\"\n-'\n+\t\tcp file2 file12 &&\n+\t\techo \"some text\" >>file12 &&\n+\t\tgit add file12 &&\n+\t\tgit commit -a -m \"Copy file2 to file12 with changes\" &&\n+\t\tgit diff-tree -r -C --find-copies-harder HEAD &&\n+\t\tlevel=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f1 | cut -d\" \" -f5 | sed \"s/C0*//\") &&\n+\t\ttest -n \"$level\" && test \"$level\" -gt 0 && test \"$level\" -lt 98 &&\n+\t\tsrc=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) &&\n+\t\ttest \"$src\" = file10 &&\n+\t\tgit config git-p4.detectCopies $(($level + 2)) &&\n+\t\t\"$GITP4\" submit &&\n+\t\tp4 filelog //depot/file12 &&\n+\t\tp4 filelog //depot/file12 | test_must_fail grep -q \"branch from\" &&\n \n-# Configure branches through git-config and clone them.\n-# All files are tested to make sure branches were cloned correctly.\n-# Finally, make an update to branch1 on P4 side to check if it is imported\n-# correctly by git-p4.\n-test_expect_success 'git-p4 clone simple branches' '\n-\ttest_when_finished cleanup_git &&\n-\ttest_create_repo \"$git\" &&\n-\tcd \"$git\" &&\n-\tgit config git-p4.branchList branch1:branch2 &&\n-\tgit config --add git-p4.branchList branch1:branch3 &&\n-\t\"$GITP4\" clone --dest=. --detect-branches //depot@all &&\n-\tgit log --all --graph --decorate --stat &&\n-\tgit reset --hard p4/depot/branch1 &&\n-\ttest -f file1 &&\n-\ttest -f file2 &&\n-\ttest -f file3 &&\n-\tgrep -q update file2 &&\n-\tgit reset --hard p4/depot/branch2 &&\n-\ttest -f file1 &&\n-\ttest -f file2 &&\n-\ttest ! -f file3 &&\n-\t! grep -q update file2 &&\n-\tgit reset --hard p4/depot/branch3 &&\n-\ttest -f file1 &&\n-\ttest -f file2 &&\n-\ttest -f file3 &&\n-\tgrep -q update file2 &&\n-\tcd \"$cli\" &&\n-\tcd branch1 &&\n-\tp4 edit file2 &&\n-\techo file2_ >>file2 &&\n-\tp4 submit -d \"update file2 in branch1\" &&\n-\tcd \"$git\" &&\n-\tgit reset --hard p4/depot/branch1 &&\n-\t\"$GITP4\" rebase &&\n-\tgrep -q file2_ file2\n+\t\tcp file2 file13 &&\n+\t\techo \"different text\" >>file13 &&\n+\t\tgit add file13 &&\n+\t\tgit commit -a -m \"Copy file2 to file13 with changes\" &&\n+\t\tgit diff-tree -r -C --find-copies-harder HEAD &&\n+\t\tlevel=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f1 | cut -d\" \" -f5 | sed \"s/C0*//\") &&\n+\t\ttest -n \"$level\" && test \"$level\" -gt 2 && test \"$level\" -lt 100 &&\n+\t\tsrc=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) &&\n+\t\ttest \"$src\" = file10 &&\n+\t\tgit config git-p4.detectCopies $(($level - 2)) &&\n+\t\t\"$GITP4\" submit &&\n+\t\tp4 filelog //depot/file13 &&\n+\t\tp4 filelog //depot/file13 | grep -q \"branch from //depot/file\"\n+\t)\n '\n \n-test_expect_success 'shutdown' '\n-\tpid=`pgrep -f p4d` &&\n-\ttest -n \"$pid\" &&\n-\ttest_debug \"ps wl `echo $pid`\" &&\n-\tkill $pid\n+test_expect_success 'kill p4d' '\n+\tkill_p4d\n '\n \n test_done\ndiff --git a/t/t9801-git-p4-branch.sh b/t/t9801-git-p4-branch.sh\nnew file mode 100755\nindex 0000000..a25f18d\n--- /dev/null\n+++ b/t/t9801-git-p4-branch.sh\n@@ -0,0 +1,233 @@\n+#!/bin/sh\n+\n+test_description='git-p4 p4 branching tests'\n+\n+. ./lib-git-p4.sh\n+\n+test_expect_success 'start p4d' '\n+\tstart_p4d\n+'\n+\n+#\n+# 1: //depot/main/f1\n+# 2: //depot/main/f2\n+# 3: integrate //depot/main/... -> //depot/branch1/...\n+# 4: //depot/main/f4\n+# 5: //depot/branch1/f5\n+# .: named branch branch2\n+# 6: integrate -b branch2\n+# 7: //depot/branch2/f7\n+# 8: //depot/main/f8\n+#\n+test_expect_success 'basic p4 branches' '\n+\t(\n+\t\tcd \"$cli\" &&\n+\t\tmkdir -p main &&\n+\n+\t\techo f1 >main/f1 &&\n+\t\tp4 add main/f1 &&\n+\t\tp4 submit -d \"main/f1\" &&\n+\n+\t\techo f2 >main/f2 &&\n+\t\tp4 add main/f2 &&\n+\t\tp4 submit -d \"main/f2\" &&\n+\n+\t\tp4 integrate //depot/main/... //depot/branch1/... &&\n+\t\tp4 submit -d \"integrate main to branch1\" &&\n+\n+\t\techo f4 >main/f4 &&\n+\t\tp4 add main/f4 &&\n+\t\tp4 submit -d \"main/f4\" &&\n+\n+\t\techo f5 >branch1/f5 &&\n+\t\tp4 add branch1/f5 &&\n+\t\tp4 submit -d \"branch1/f5\" &&\n+\n+\t\tp4 branch -i <<-EOF &&\n+\t\tBranch: branch2\n+\t\tView: //depot/main/... //depot/branch2/...\n+\t\tEOF\n+\n+\t\tp4 integrate -b branch2 &&\n+\t\tp4 submit -d \"integrate main to branch2\" &&\n+\n+\t\techo f7 >branch2/f7 &&\n+\t\tp4 add branch2/f7 &&\n+\t\tp4 submit -d \"branch2/f7\" &&\n+\n+\t\techo f8 >main/f8 &&\n+\t\tp4 add main/f8 &&\n+\t\tp4 submit -d \"main/f8\"\n+\t)\n+'\n+\n+test_expect_success 'import main, no branch detection' '\n+\ttest_when_finished cleanup_git &&\n+\t\"$GITP4\" clone --dest=\"$git\" //depot/main@all &&\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgit log --oneline --graph --decorate --all &&\n+\t\tgit rev-list master >wc &&\n+\t\ttest_line_count = 4 wc\n+\t)\n+'\n+\n+test_expect_success 'import branch1, no branch detection' '\n+\ttest_when_finished cleanup_git &&\n+\t\"$GITP4\" clone --dest=\"$git\" //depot/branch1@all &&\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgit log --oneline --graph --decorate --all &&\n+\t\tgit rev-list master >wc &&\n+\t\ttest_line_count = 2 wc\n+\t)\n+'\n+\n+test_expect_success 'import branch2, no branch detection' '\n+\ttest_when_finished cleanup_git &&\n+\t\"$GITP4\" clone --dest=\"$git\" //depot/branch2@all &&\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgit log --oneline --graph --decorate --all &&\n+\t\tgit rev-list master >wc &&\n+\t\ttest_line_count = 2 wc\n+\t)\n+'\n+\n+test_expect_success 'import depot, no branch detection' '\n+\ttest_when_finished cleanup_git &&\n+\t\"$GITP4\" clone --dest=\"$git\" //depot@all &&\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgit log --oneline --graph --decorate --all &&\n+\t\tgit rev-list master >wc &&\n+\t\ttest_line_count = 8 wc\n+\t)\n+'\n+\n+test_expect_success 'import depot, branch detection' '\n+\ttest_when_finished cleanup_git &&\n+\t\"$GITP4\" clone --dest=\"$git\" --detect-branches //depot@all &&\n+\t(\n+\t\tcd \"$git\" &&\n+\n+\t\tgit log --oneline --graph --decorate --all &&\n+\n+\t\t# 4 main commits\n+\t\tgit rev-list master >wc &&\n+\t\ttest_line_count = 4 wc &&\n+\n+\t\t# 3 main, 1 integrate, 1 on branch2\n+\t\tgit rev-list p4/depot/branch2 >wc &&\n+\t\ttest_line_count = 5 wc &&\n+\n+\t\t# no branch1, since no p4 branch created for it\n+\t\ttest_must_fail git show-ref p4/depot/branch1\n+\t)\n+'\n+\n+test_expect_success 'import depot, branch detection, branchList branch definition' '\n+\ttest_when_finished cleanup_git &&\n+\ttest_create_repo \"$git\" &&\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgit config git-p4.branchList main:branch1 &&\n+\t\t\"$GITP4\" clone --dest=. --detect-branches //depot@all &&\n+\n+\t\tgit log --oneline --graph --decorate --all &&\n+\n+\t\t# 4 main commits\n+\t\tgit rev-list master >wc &&\n+\t\ttest_line_count = 4 wc &&\n+\n+\t\t# 3 main, 1 integrate, 1 on branch2\n+\t\tgit rev-list p4/depot/branch2 >wc &&\n+\t\ttest_line_count = 5 wc &&\n+\n+\t\t# 2 main, 1 integrate, 1 on branch1\n+\t\tgit rev-list p4/depot/branch1 >wc &&\n+\t\ttest_line_count = 4 wc\n+\t)\n+'\n+\n+test_expect_success 'restart p4d' '\n+\tkill_p4d &&\n+\tstart_p4d\n+'\n+\n+#\n+# 1: //depot/branch1/file1\n+#    //depot/branch1/file2\n+# 2: integrate //depot/branch1/... -> //depot/branch2/...\n+# 3: //depot/branch1/file3\n+# 4: //depot/branch1/file2 (edit)\n+# 5: integrate //depot/branch1/... -> //depot/branch3/...\n+#\n+## Create a simple branch structure in P4 depot.\n+test_expect_success 'add simple p4 branches' '\n+\t(\n+\t\tcd \"$cli\" &&\n+\t\tmkdir branch1 &&\n+\t\tcd branch1 &&\n+\t\techo file1 >file1 &&\n+\t\techo file2 >file2 &&\n+\t\tp4 add file1 file2 &&\n+\t\tp4 submit -d \"branch1\" &&\n+\t\tp4 integrate //depot/branch1/... //depot/branch2/... &&\n+\t\tp4 submit -d \"branch2\" &&\n+\t\techo file3 >file3 &&\n+\t\tp4 add file3 &&\n+\t\tp4 submit -d \"add file3 in branch1\" &&\n+\t\tp4 open file2 &&\n+\t\techo update >>file2 &&\n+\t\tp4 submit -d \"update file2 in branch1\" &&\n+\t\tp4 integrate //depot/branch1/... //depot/branch3/... &&\n+\t\tp4 submit -d \"branch3\"\n+\t)\n+'\n+\n+# Configure branches through git-config and clone them.\n+# All files are tested to make sure branches were cloned correctly.\n+# Finally, make an update to branch1 on P4 side to check if it is imported\n+# correctly by git-p4.\n+test_expect_success 'git-p4 clone simple branches' '\n+\ttest_when_finished cleanup_git &&\n+\ttest_create_repo \"$git\" &&\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgit config git-p4.branchList branch1:branch2 &&\n+\t\tgit config --add git-p4.branchList branch1:branch3 &&\n+\t\t\"$GITP4\" clone --dest=. --detect-branches //depot@all &&\n+\t\tgit log --all --graph --decorate --stat &&\n+\t\tgit reset --hard p4/depot/branch1 &&\n+\t\ttest -f file1 &&\n+\t\ttest -f file2 &&\n+\t\ttest -f file3 &&\n+\t\tgrep -q update file2 &&\n+\t\tgit reset --hard p4/depot/branch2 &&\n+\t\ttest -f file1 &&\n+\t\ttest -f file2 &&\n+\t\ttest ! -f file3 &&\n+\t\ttest_must_fail grep -q update file2 &&\n+\t\tgit reset --hard p4/depot/branch3 &&\n+\t\ttest -f file1 &&\n+\t\ttest -f file2 &&\n+\t\ttest -f file3 &&\n+\t\tgrep -q update file2 &&\n+\t\tcd \"$cli\" &&\n+\t\tcd branch1 &&\n+\t\tp4 edit file2 &&\n+\t\techo file2_ >>file2 &&\n+\t\tp4 submit -d \"update file2 in branch3\" &&\n+\t\tcd \"$git\" &&\n+\t\tgit reset --hard p4/depot/branch1 &&\n+\t\t\"$GITP4\" rebase &&\n+\t\tgrep -q file2_ file2\n+\t)\n+'\n+\n+test_expect_success 'kill p4d' '\n+\tkill_p4d\n+'\n+\n+test_done\n-- \n1.7.7\n"},{"id":"177785","messageId":"20111016144435.GE22144@arf.padd.com","threadId":"28694","inReplyTo":"20111016144215.GC22144@arf.padd.com","subject":"[PATCH 2/6] git-p4: handle utf16 filetype properly","fromName":"Pete Wyckoff","fromEmail":"pw@padd.com","sentAt":"2011-10-16T14:44:35Z","receivedAt":"2011-10-16T14:44:35Z","isPatch":true,"sender":{"key":"pw@padd.com","avatar":null},"body":"One of the filetypes that p4 supports is utf16.  Its behavior is\nodd in this case.  The data delivered through \"p4 -G print\" is\nnot encoded in utf16, although \"p4 print -o\" will produce the\nproper utf16-encoded file.\n\nWhen dealing with this filetype, discard the data from -G, and\ninstead read the contents directly.\n\nAn alternate approach would be to try to encode the data in\npython.  That worked for true utf16 files, but for other files\nmarked as utf16, p4 delivers mangled text in no recognizable encoding.\n\nAdd a test case to check utf16 handling, and +k and +ko handling.\n\nReported-by: Chris Li <git@chrisli.org>\nAcked-by: Luke Diamand <luke@diamand.org>\nSigned-off-by: Pete Wyckoff <pw@padd.com>\n---\n contrib/fast-import/git-p4 |   11 +++++\n t/t9802-git-p4-filetype.sh |  108 ++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 119 insertions(+), 0 deletions(-)\n create mode 100755 t/t9802-git-p4-filetype.sh\n\ndiff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4\nindex 2f7b270..e69caf3 100755\n--- a/contrib/fast-import/git-p4\n+++ b/contrib/fast-import/git-p4\n@@ -1238,6 +1238,15 @@ class P4Sync(Command, P4UserMap):\n             data = ''.join(contents)\n             contents = [data[:-1]]\n \n+        if file['type'].startswith(\"utf16\"):\n+            # p4 delivers different text in the python output to -G\n+            # than it does when using \"print -o\", or normal p4 client\n+            # operations.  utf16 is converted to ascii or utf8, perhaps.\n+            # But ascii text saved as -t utf16 is completely mangled.\n+            # Invoke print -o to get the real contents.\n+            text = p4_read_pipe('print -q -o - \"%s\"' % file['depotFile'])\n+            contents = [ text ]\n+\n         if self.isWindows and file[\"type\"].endswith(\"text\"):\n             mangled = []\n             for data in contents:\n@@ -1245,6 +1254,8 @@ class P4Sync(Command, P4UserMap):\n                 mangled.append(data)\n             contents = mangled\n \n+        # Note that we do not try to de-mangle keywords on utf16 files,\n+        # even though in theory somebody may want that.\n         if file['type'] in ('text+ko', 'unicode+ko', 'binary+ko'):\n             contents = map(lambda text: re.sub(r'(?i)\\$(Id|Header):[^$]*\\$',r'$\\1$', text), contents)\n         elif file['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'):\ndiff --git a/t/t9802-git-p4-filetype.sh b/t/t9802-git-p4-filetype.sh\nnew file mode 100755\nindex 0000000..dff0e02\n--- /dev/null\n+++ b/t/t9802-git-p4-filetype.sh\n@@ -0,0 +1,108 @@\n+#!/bin/sh\n+\n+test_description='git-p4 p4 filetype tests'\n+\n+. ./lib-git-p4.sh\n+\n+test_expect_success 'start p4d' '\n+\tstart_p4d\n+'\n+\n+test_expect_success 'utf-16 file create' '\n+\t(\n+\t\tcd \"$cli\" &&\n+\n+\t\t# p4 saves this verbatim\n+\t\tprintf \"three\\nline\\ntext\" >f-ascii &&\n+\t\tp4 add -t text f-ascii &&\n+\n+\t\t# p4 adds \\377\\376 header\n+\t\tcp f-ascii f-ascii-as-utf16 &&\n+\t\tp4 add -t utf16 f-ascii-as-utf16 &&\n+\n+\t\t# p4 saves this exactly as iconv produced it\n+\t\tprintf \"three\\nline\\ntext\" | iconv -f ascii -t utf-16 >f-utf16 &&\n+\t\tp4 add -t utf16 f-utf16 &&\n+\n+\t\t# this also is unchanged\n+\t\tcp f-utf16 f-utf16-as-text &&\n+\t\tp4 add -t text f-utf16-as-text &&\n+\n+\t\tp4 submit -d \"f files\" &&\n+\n+\t\t# force update of client files\n+\t\tp4 sync -f\n+\t)\n+'\n+\n+test_expect_success 'utf-16 file test' '\n+\ttest_when_finished cleanup_git &&\n+\t\"$GITP4\" clone --dest=\"$git\" //depot@all &&\n+\t(\n+\t\tcd \"$git\" &&\n+\n+\t\ttest_cmp \"$cli/f-ascii\" f-ascii &&\n+\t\ttest_cmp \"$cli/f-ascii-as-utf16\" f-ascii-as-utf16 &&\n+\t\ttest_cmp \"$cli/f-utf16\" f-utf16 &&\n+\t\ttest_cmp \"$cli/f-utf16-as-text\" f-utf16-as-text\n+\t)\n+'\n+\n+test_expect_success 'keyword file create' '\n+\t(\n+\t\tcd \"$cli\" &&\n+\n+\t\tprintf \"id\\n\\$Id\\$\\n\\$Author\\$\\ntext\" >k-text-k &&\n+\t\tp4 add -t text+k k-text-k &&\n+\n+\t\tcp k-text-k k-text-ko &&\n+\t\tp4 add -t text+ko k-text-ko &&\n+\n+\t\tcat k-text-k | iconv -f ascii -t utf-16 >k-utf16-k &&\n+\t\tp4 add -t utf16+k k-utf16-k &&\n+\n+\t\tcp k-utf16-k k-utf16-ko &&\n+\t\tp4 add -t utf16+ko k-utf16-ko &&\n+\n+\t\tp4 submit -d \"k files\" &&\n+\t\tp4 sync -f\n+\t)\n+'\n+\n+build_smush() {\n+\tcat >k_smush.py <<-\\EOF &&\n+\timport re, sys\n+\tsys.stdout.write(re.sub(r'(?i)\\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\\$', r'$\\1$', sys.stdin.read()))\n+\tEOF\n+\tcat >ko_smush.py <<-\\EOF\n+\timport re, sys\n+\tsys.stdout.write(re.sub(r'(?i)\\$(Id|Header):[^$]*\\$', r'$\\1$', sys.stdin.read()))\n+\tEOF\n+}\n+\n+test_expect_success 'keyword file test' '\n+\tbuild_smush &&\n+\ttest_when_finished rm -f k_smush.py ko_smush.py &&\n+\ttest_when_finished cleanup_git &&\n+\t\"$GITP4\" clone --dest=\"$git\" //depot@all &&\n+\t(\n+\t\tcd \"$git\" &&\n+\n+\t\t# text, ensure unexpanded\n+\t\tpython \"$TRASH_DIRECTORY/k_smush.py\" <\"$cli/k-text-k\" >cli-k-text-k-smush &&\n+\t\ttest_cmp cli-k-text-k-smush k-text-k &&\n+\t\tpython \"$TRASH_DIRECTORY/ko_smush.py\" <\"$cli/k-text-ko\" >cli-k-text-ko-smush &&\n+\t\ttest_cmp cli-k-text-ko-smush k-text-ko &&\n+\n+\t\t# utf16, even though p4 expands keywords, git-p4 does not\n+\t\t# try to undo that\n+\t\ttest_cmp \"$cli/k-utf16-k\" k-utf16-k &&\n+\t\ttest_cmp \"$cli/k-utf16-ko\" k-utf16-ko\n+\t)\n+'\n+\n+test_expect_success 'kill p4d' '\n+\tkill_p4d\n+'\n+\n+test_done\n-- \n1.7.7\n"},{"id":"177786","messageId":"20111016144501.GF22144@arf.padd.com","threadId":"28694","inReplyTo":"20111016144215.GC22144@arf.padd.com","subject":"[PATCH 3/6] git-p4: recognize all p4 filetypes","fromName":"Pete Wyckoff","fromEmail":"pw@padd.com","sentAt":"2011-10-16T14:45:01Z","receivedAt":"2011-10-16T14:45:01Z","isPatch":true,"sender":{"key":"pw@padd.com","avatar":null},"body":"The previous code was approximate in the filetypes it recognized.\nPut in the canonical list and be more careful about matching\nelements of the file type.\n\nThis might change behavior in some cases, hopefully for the\nbetter.  Windows newline mangling will now happen on all\ntext files.  Previously some like \"text+ko\" were oddly exempt.\n\nFiles with multiple combinations of modifiers, like \"text+klx\",\nare now recognized for keyword expansion.  I expect these to be\nseen only rarely.\n\nAcked-by: Luke Diamand <luke@diamand.org>\nSigned-off-by: Pete Wyckoff <pw@padd.com>\n---\n contrib/fast-import/git-p4 |   71 ++++++++++++++++++++++++++++++++------------\n 1 files changed, 52 insertions(+), 19 deletions(-)\n\ndiff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4\nindex e69caf3..0490ca5 100755\n--- a/contrib/fast-import/git-p4\n+++ b/contrib/fast-import/git-p4\n@@ -118,13 +118,41 @@ def p4_system(cmd):\n     real_cmd = p4_build_cmd(cmd)\n     return system(real_cmd)\n \n-def isP4Exec(kind):\n-    \"\"\"Determine if a Perforce 'kind' should have execute permission\n+#\n+# Canonicalize the p4 type and return a tuple of the\n+# base type, plus any modifiers.  See \"p4 help filetypes\"\n+# for a list and explanation.\n+#\n+def split_p4_type(p4type):\n+\n+    p4_filetypes_historical = {\n+        \"ctempobj\": \"binary+Sw\",\n+        \"ctext\": \"text+C\",\n+        \"cxtext\": \"text+Cx\",\n+        \"ktext\": \"text+k\",\n+        \"kxtext\": \"text+kx\",\n+        \"ltext\": \"text+F\",\n+        \"tempobj\": \"binary+FSw\",\n+        \"ubinary\": \"binary+F\",\n+        \"uresource\": \"resource+F\",\n+        \"uxbinary\": \"binary+Fx\",\n+        \"xbinary\": \"binary+x\",\n+        \"xltext\": \"text+Fx\",\n+        \"xtempobj\": \"binary+Swx\",\n+        \"xtext\": \"text+x\",\n+        \"xunicode\": \"unicode+x\",\n+        \"xutf16\": \"utf16+x\",\n+    }\n+    if p4type in p4_filetypes_historical:\n+        p4type = p4_filetypes_historical[p4type]\n+    mods = \"\"\n+    s = p4type.split(\"+\")\n+    base = s[0]\n+    mods = \"\"\n+    if len(s) > 1:\n+        mods = s[1]\n+    return (base, mods)\n \n-    'p4 help filetypes' gives a list of the types.  If it starts with 'x',\n-    or x follows one of a few letters.  Otherwise, if there is an 'x' after\n-    a plus sign, it is also executable\"\"\"\n-    return (re.search(r\"(^[cku]?x)|\\+.*x\", kind) != None)\n \n def setP4ExecBit(file, mode):\n     # Reopens an already open file and changes the execute bit to match\n@@ -1229,16 +1257,18 @@ class P4Sync(Command, P4UserMap):\n         if verbose:\n             sys.stderr.write(\"%s\\n\" % relPath)\n \n-        mode = \"644\"\n-        if isP4Exec(file[\"type\"]):\n-            mode = \"755\"\n-        elif file[\"type\"] == \"symlink\":\n-            mode = \"120000\"\n-            # p4 print on a symlink contains \"target\\n\", so strip it off\n+        (type_base, type_mods) = split_p4_type(file[\"type\"])\n+\n+        git_mode = \"100644\"\n+        if \"x\" in type_mods:\n+            git_mode = \"100755\"\n+        if type_base == \"symlink\":\n+            git_mode = \"120000\"\n+            # p4 print on a symlink contains \"target\\n\"; remove the newline\n             data = ''.join(contents)\n             contents = [data[:-1]]\n \n-        if file['type'].startswith(\"utf16\"):\n+        if type_base == \"utf16\":\n             # p4 delivers different text in the python output to -G\n             # than it does when using \"print -o\", or normal p4 client\n             # operations.  utf16 is converted to ascii or utf8, perhaps.\n@@ -1247,7 +1277,9 @@ class P4Sync(Command, P4UserMap):\n             text = p4_read_pipe('print -q -o - \"%s\"' % file['depotFile'])\n             contents = [ text ]\n \n-        if self.isWindows and file[\"type\"].endswith(\"text\"):\n+        # Perhaps windows wants unicode, utf16 newlines translated too;\n+        # but this is not doing it.\n+        if self.isWindows and type_base == \"text\":\n             mangled = []\n             for data in contents:\n                 data = data.replace(\"\\r\\n\", \"\\n\")\n@@ -1256,12 +1288,13 @@ class P4Sync(Command, P4UserMap):\n \n         # Note that we do not try to de-mangle keywords on utf16 files,\n         # even though in theory somebody may want that.\n-        if file['type'] in ('text+ko', 'unicode+ko', 'binary+ko'):\n-            contents = map(lambda text: re.sub(r'(?i)\\$(Id|Header):[^$]*\\$',r'$\\1$', text), contents)\n-        elif file['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'):\n-            contents = map(lambda text: re.sub(r'\\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$\\n]*\\$',r'$\\1$', text), contents)\n+        if type_base in (\"text\", \"unicode\", \"binary\"):\n+            if \"ko\" in type_mods:\n+                contents = map(lambda text: re.sub(r'(?i)\\$(Id|Header):[^$]*\\$', r'$\\1$', text), contents)\n+            elif \"k\" in type_mods:\n+                contents = map(lambda text: re.sub(r'\\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$\\n]*\\$', r'$\\1$', text), contents)\n \n-        self.gitStream.write(\"M %s inline %s\\n\" % (mode, relPath))\n+        self.gitStream.write(\"M %s inline %s\\n\" % (git_mode, relPath))\n \n         # total length...\n         length = 0\n-- \n1.7.7\n"},{"id":"177787","messageId":"20111016144549.GG22144@arf.padd.com","threadId":"28694","inReplyTo":"20111016144215.GC22144@arf.padd.com","subject":"[PATCH 4/6] git-p4: stop ignoring apple filetype","fromName":"Pete Wyckoff","fromEmail":"pw@padd.com","sentAt":"2011-10-16T14:45:49Z","receivedAt":"2011-10-16T14:45:49Z","isPatch":true,"sender":{"key":"pw@padd.com","avatar":null},"body":"Currently \"apple\" filetype is ignored explicitly, and the file is\nnot even included in the git repository.  This seems wrong.\nRemove this, letting it be treated like a \"binary\" filetype.\n\nAcked-by: Luke Diamand <luke@diamand.org>\nSigned-off-by: Pete Wyckoff <pw@padd.com>\n---\n contrib/fast-import/git-p4 |    5 -----\n 1 files changed, 0 insertions(+), 5 deletions(-)\n\ndiff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4\nindex 0490ca5..6b91595 100755\n--- a/contrib/fast-import/git-p4\n+++ b/contrib/fast-import/git-p4\n@@ -1247,11 +1247,6 @@ class P4Sync(Command, P4UserMap):\n     # - helper for streamP4Files\n \n     def streamOneP4File(self, file, contents):\n-        if file[\"type\"] == \"apple\":\n-            print \"\\nfile %s is a strange apple file that forks. Ignoring\" % \\\n-                file['depotFile']\n-            return\n-\n         relPath = self.stripRepoPath(file['depotFile'], self.branchPrefixes)\n         relPath = self.wildcard_decode(relPath)\n         if verbose:\n-- \n1.7.7\n"},{"id":"177788","messageId":"20111016144652.GH22144@arf.padd.com","threadId":"28694","inReplyTo":"20111016144215.GC22144@arf.padd.com","subject":"[PATCH 5/6] git-p4: keyword flattening fixes","fromName":"Pete Wyckoff","fromEmail":"pw@padd.com","sentAt":"2011-10-16T14:46:52Z","receivedAt":"2011-10-16T14:46:52Z","isPatch":true,"sender":{"key":"pw@padd.com","avatar":null},"body":"Join the text before looking for keywords.  There is nothing to\nprevent the p4 output marshaller from splitting in the middle of a\nkeyword, although it has never been known to happen.\n\nAlso remove the (?i) regexp modifier; perforce keywords are\ndocumented as case-sensitive.\n\nRemove the \"\\n\" end-character match.  I don't know why that is\nin there, and every keyword in a fairly large production p4 repository\nalways ends with a $.\n\nAcked-by: Luke Diamand <luke@diamand.org>\nSigned-off-by: Pete Wyckoff <pw@padd.com>\n---\n contrib/fast-import/git-p4 |    8 ++++++--\n 1 files changed, 6 insertions(+), 2 deletions(-)\n\ndiff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4\nindex 6b91595..55b1667 100755\n--- a/contrib/fast-import/git-p4\n+++ b/contrib/fast-import/git-p4\n@@ -1285,9 +1285,13 @@ class P4Sync(Command, P4UserMap):\n         # even though in theory somebody may want that.\n         if type_base in (\"text\", \"unicode\", \"binary\"):\n             if \"ko\" in type_mods:\n-                contents = map(lambda text: re.sub(r'(?i)\\$(Id|Header):[^$]*\\$', r'$\\1$', text), contents)\n+                text = ''.join(contents)\n+                text = re.sub(r'\\$(Id|Header):[^$]*\\$', r'$\\1$', text)\n+                contents = [ text ]\n             elif \"k\" in type_mods:\n-                contents = map(lambda text: re.sub(r'\\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$\\n]*\\$', r'$\\1$', text), contents)\n+                text = ''.join(contents)\n+                text = re.sub(r'\\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\\$', r'$\\1$', text)\n+                contents = [ text ]\n \n         self.gitStream.write(\"M %s inline %s\\n\" % (git_mode, relPath))\n \n-- \n1.7.7\n"},{"id":"177789","messageId":"20111016144752.GI22144@arf.padd.com","threadId":"28694","inReplyTo":"20111016144215.GC22144@arf.padd.com","subject":"[PATCH 6/6] git-p4: handle files with shell metacharacters","fromName":"Pete Wyckoff","fromEmail":"pw@padd.com","sentAt":"2011-10-16T14:47:52Z","receivedAt":"2011-10-16T14:47:52Z","isPatch":true,"sender":{"key":"pw@padd.com","avatar":null},"body":"From: Luke Diamand <luke@diamand.org>\n\ngit-p4 used to simply pass strings into system() and popen(), and\nrelied on the shell doing the necessary expansion. This though meant\nthat shell metacharacters in file names would be corrupted - for\nexample files with $ or space in them.\n\nSwitch to using subprocess.Popen() and friends, and pass in explicit\narrays in the places where it matters. This then avoids needing shell\nexpansion.\n\nAdd trivial helper functions for some common perforce operations. Add\ntest case.\n\n[pw: test cleanup]\n\nSigned-off-by: Luke Diamand <luke@diamand.org>\nSigned-off-by: Pete Wyckoff <pw@padd.com>\n---\n contrib/fast-import/git-p4     |  200 ++++++++++++++++++++++++---------------\n t/t9803-git-shell-metachars.sh |   64 +++++++++++++\n 2 files changed, 187 insertions(+), 77 deletions(-)\n create mode 100755 t/t9803-git-shell-metachars.sh\n\ndiff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4\nindex 55b1667..f885d70 100755\n--- a/contrib/fast-import/git-p4\n+++ b/contrib/fast-import/git-p4\n@@ -22,36 +22,39 @@ def p4_build_cmd(cmd):\n     location. It means that hooking into the environment, or other configuration\n     can be done more easily.\n     \"\"\"\n-    real_cmd = \"%s \" % \"p4\"\n+    real_cmd = [\"p4\"]\n \n     user = gitConfig(\"git-p4.user\")\n     if len(user) > 0:\n-        real_cmd += \"-u %s \" % user\n+        real_cmd += [\"-u\",user]\n \n     password = gitConfig(\"git-p4.password\")\n     if len(password) > 0:\n-        real_cmd += \"-P %s \" % password\n+        real_cmd += [\"-P\", password]\n \n     port = gitConfig(\"git-p4.port\")\n     if len(port) > 0:\n-        real_cmd += \"-p %s \" % port\n+        real_cmd += [\"-p\", port]\n \n     host = gitConfig(\"git-p4.host\")\n     if len(host) > 0:\n-        real_cmd += \"-h %s \" % host\n+        real_cmd += [\"-h\", host]\n \n     client = gitConfig(\"git-p4.client\")\n     if len(client) > 0:\n-        real_cmd += \"-c %s \" % client\n+        real_cmd += [\"-c\", client]\n \n-    real_cmd += \"%s\" % (cmd)\n-    if verbose:\n-        print real_cmd\n+\n+    if isinstance(cmd,basestring):\n+        real_cmd = ' '.join(real_cmd) + ' ' + cmd\n+    else:\n+        real_cmd += cmd\n     return real_cmd\n \n def chdir(dir):\n-    if os.name == 'nt':\n-        os.environ['PWD']=dir\n+    # P4 uses the PWD environment variable rather than getcwd(). Since we're\n+    # not using the shell, we have to set it ourselves.\n+    os.environ['PWD']=dir\n     os.chdir(dir)\n \n def die(msg):\n@@ -61,29 +64,34 @@ def die(msg):\n         sys.stderr.write(msg + \"\\n\")\n         sys.exit(1)\n \n-def write_pipe(c, str):\n+def write_pipe(c, stdin):\n     if verbose:\n-        sys.stderr.write('Writing pipe: %s\\n' % c)\n+        sys.stderr.write('Writing pipe: %s\\n' % str(c))\n \n-    pipe = os.popen(c, 'w')\n-    val = pipe.write(str)\n-    if pipe.close():\n-        die('Command failed: %s' % c)\n+    expand = isinstance(c,basestring)\n+    p = subprocess.Popen(c, stdin=subprocess.PIPE, shell=expand)\n+    pipe = p.stdin\n+    val = pipe.write(stdin)\n+    pipe.close()\n+    if p.wait():\n+        die('Command failed: %s' % str(c))\n \n     return val\n \n-def p4_write_pipe(c, str):\n+def p4_write_pipe(c, stdin):\n     real_cmd = p4_build_cmd(c)\n-    return write_pipe(real_cmd, str)\n+    return write_pipe(real_cmd, stdin)\n \n def read_pipe(c, ignore_error=False):\n     if verbose:\n-        sys.stderr.write('Reading pipe: %s\\n' % c)\n+        sys.stderr.write('Reading pipe: %s\\n' % str(c))\n \n-    pipe = os.popen(c, 'rb')\n+    expand = isinstance(c,basestring)\n+    p = subprocess.Popen(c, stdout=subprocess.PIPE, shell=expand)\n+    pipe = p.stdout\n     val = pipe.read()\n-    if pipe.close() and not ignore_error:\n-        die('Command failed: %s' % c)\n+    if p.wait() and not ignore_error:\n+        die('Command failed: %s' % str(c))\n \n     return val\n \n@@ -93,12 +101,14 @@ def p4_read_pipe(c, ignore_error=False):\n \n def read_pipe_lines(c):\n     if verbose:\n-        sys.stderr.write('Reading pipe: %s\\n' % c)\n-    ## todo: check return status\n-    pipe = os.popen(c, 'rb')\n+        sys.stderr.write('Reading pipe: %s\\n' % str(c))\n+\n+    expand = isinstance(c, basestring)\n+    p = subprocess.Popen(c, stdout=subprocess.PIPE, shell=expand)\n+    pipe = p.stdout\n     val = pipe.readlines()\n-    if pipe.close():\n-        die('Command failed: %s' % c)\n+    if pipe.close() or p.wait():\n+        die('Command failed: %s' % str(c))\n \n     return val\n \n@@ -108,15 +118,37 @@ def p4_read_pipe_lines(c):\n     return read_pipe_lines(real_cmd)\n \n def system(cmd):\n+    expand = isinstance(cmd,basestring)\n     if verbose:\n-        sys.stderr.write(\"executing %s\\n\" % cmd)\n-    if os.system(cmd) != 0:\n-        die(\"command failed: %s\" % cmd)\n+        sys.stderr.write(\"executing %s\\n\" % str(cmd))\n+    subprocess.check_call(cmd, shell=expand)\n \n def p4_system(cmd):\n     \"\"\"Specifically invoke p4 as the system command. \"\"\"\n     real_cmd = p4_build_cmd(cmd)\n-    return system(real_cmd)\n+    expand = isinstance(real_cmd, basestring)\n+    subprocess.check_call(real_cmd, shell=expand)\n+\n+def p4_integrate(src, dest):\n+    p4_system([\"integrate\", \"-Dt\", src, dest])\n+\n+def p4_sync(path):\n+    p4_system([\"sync\", path])\n+\n+def p4_add(f):\n+    p4_system([\"add\", f])\n+\n+def p4_delete(f):\n+    p4_system([\"delete\", f])\n+\n+def p4_edit(f):\n+    p4_system([\"edit\", f])\n+\n+def p4_revert(f):\n+    p4_system([\"revert\", f])\n+\n+def p4_reopen(type, file):\n+    p4_system([\"reopen\", \"-t\", type, file])\n \n #\n # Canonicalize the p4 type and return a tuple of the\n@@ -167,12 +199,12 @@ def setP4ExecBit(file, mode):\n         if p4Type[-1] == \"+\":\n             p4Type = p4Type[0:-1]\n \n-    p4_system(\"reopen -t %s %s\" % (p4Type, file))\n+    p4_reopen(p4Type, file)\n \n def getP4OpenedType(file):\n     # Returns the perforce file type for the given file.\n \n-    result = p4_read_pipe(\"opened %s\" % file)\n+    result = p4_read_pipe([\"opened\", file])\n     match = re.match(\".*\\((.+)\\)\\r?$\", result)\n     if match:\n         return match.group(1)\n@@ -228,9 +260,17 @@ def isModeExecChanged(src_mode, dst_mode):\n     return isModeExec(src_mode) != isModeExec(dst_mode)\n \n def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None):\n-    cmd = p4_build_cmd(\"-G %s\" % (cmd))\n+\n+    if isinstance(cmd,basestring):\n+        cmd = \"-G \" + cmd\n+        expand = True\n+    else:\n+        cmd = [\"-G\"] + cmd\n+        expand = False\n+\n+    cmd = p4_build_cmd(cmd)\n     if verbose:\n-        sys.stderr.write(\"Opening pipe: %s\\n\" % cmd)\n+        sys.stderr.write(\"Opening pipe: %s\\n\" % str(cmd))\n \n     # Use a temporary file to avoid deadlocks without\n     # subprocess.communicate(), which would put another copy\n@@ -238,11 +278,16 @@ def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None):\n     stdin_file = None\n     if stdin is not None:\n         stdin_file = tempfile.TemporaryFile(prefix='p4-stdin', mode=stdin_mode)\n-        stdin_file.write(stdin)\n+        if isinstance(stdin,basestring):\n+            stdin_file.write(stdin)\n+        else:\n+            for i in stdin:\n+                stdin_file.write(i + '\\n')\n         stdin_file.flush()\n         stdin_file.seek(0)\n \n-    p4 = subprocess.Popen(cmd, shell=True,\n+    p4 = subprocess.Popen(cmd,\n+                          shell=expand,\n                           stdin=stdin_file,\n                           stdout=subprocess.PIPE)\n \n@@ -275,7 +320,7 @@ def p4Where(depotPath):\n     if not depotPath.endswith(\"/\"):\n         depotPath += \"/\"\n     depotPath = depotPath + \"...\"\n-    outputList = p4CmdList(\"where %s\" % depotPath)\n+    outputList = p4CmdList([\"where\", depotPath])\n     output = None\n     for entry in outputList:\n         if \"depotFile\" in entry:\n@@ -477,8 +522,10 @@ def originP4BranchesExist():\n \n def p4ChangesForPaths(depotPaths, changeRange):\n     assert depotPaths\n-    output = p4_read_pipe_lines(\"changes \" + ' '.join ([\"%s...%s\" % (p, changeRange)\n-                                                        for p in depotPaths]))\n+    cmd = ['changes']\n+    for p in depotPaths:\n+        cmd += [\"%s...%s\" % (p, changeRange)]\n+    output = p4_read_pipe_lines(cmd)\n \n     changes = {}\n     for line in output:\n@@ -561,7 +608,7 @@ class P4Debug(Command):\n \n     def run(self, args):\n         j = 0\n-        for output in p4CmdList(\" \".join(args)):\n+        for output in p4CmdList(args):\n             print 'Element: %d' % j\n             j += 1\n             print output\n@@ -715,7 +762,7 @@ class P4Submit(Command, P4UserMap):\n                 break\n         if not client:\n             die(\"could not get client spec\")\n-        results = p4CmdList(\"changes -c %s -m 1\" % client)\n+        results = p4CmdList([\"changes\", \"-c\", client, \"-m\", \"1\"])\n         for r in results:\n             if r.has_key('change'):\n                 return r['change']\n@@ -778,7 +825,7 @@ class P4Submit(Command, P4UserMap):\n         # remove lines in the Files section that show changes to files outside the depot path we're committing into\n         template = \"\"\n         inFilesSection = False\n-        for line in p4_read_pipe_lines(\"change -o\"):\n+        for line in p4_read_pipe_lines(['change', '-o']):\n             if line.endswith(\"\\r\\n\"):\n                 line = line[:-2] + \"\\n\"\n             if inFilesSection:\n@@ -835,7 +882,7 @@ class P4Submit(Command, P4UserMap):\n             modifier = diff['status']\n             path = diff['src']\n             if modifier == \"M\":\n-                p4_system(\"edit \\\"%s\\\"\" % path)\n+                p4_edit(path)\n                 if isModeExecChanged(diff['src_mode'], diff['dst_mode']):\n                     filesToChangeExecBit[path] = diff['dst_mode']\n                 editedFiles.add(path)\n@@ -850,21 +897,21 @@ class P4Submit(Command, P4UserMap):\n                     filesToAdd.remove(path)\n             elif modifier == \"C\":\n                 src, dest = diff['src'], diff['dst']\n-                p4_system(\"integrate -Dt \\\"%s\\\" \\\"%s\\\"\" % (src, dest))\n+                p4_integrate(src, dest)\n                 if diff['src_sha1'] != diff['dst_sha1']:\n-                    p4_system(\"edit \\\"%s\\\"\" % (dest))\n+                    p4_edit(dest)\n                 if isModeExecChanged(diff['src_mode'], diff['dst_mode']):\n-                    p4_system(\"edit \\\"%s\\\"\" % (dest))\n+                    p4_edit(dest)\n                     filesToChangeExecBit[dest] = diff['dst_mode']\n                 os.unlink(dest)\n                 editedFiles.add(dest)\n             elif modifier == \"R\":\n                 src, dest = diff['src'], diff['dst']\n-                p4_system(\"integrate -Dt \\\"%s\\\" \\\"%s\\\"\" % (src, dest))\n+                p4_integrate(src, dest)\n                 if diff['src_sha1'] != diff['dst_sha1']:\n-                    p4_system(\"edit \\\"%s\\\"\" % (dest))\n+                    p4_edit(dest)\n                 if isModeExecChanged(diff['src_mode'], diff['dst_mode']):\n-                    p4_system(\"edit \\\"%s\\\"\" % (dest))\n+                    p4_edit(dest)\n                     filesToChangeExecBit[dest] = diff['dst_mode']\n                 os.unlink(dest)\n                 editedFiles.add(dest)\n@@ -887,9 +934,9 @@ class P4Submit(Command, P4UserMap):\n             if response == \"s\":\n                 print \"Skipping! Good luck with the next patches...\"\n                 for f in editedFiles:\n-                    p4_system(\"revert \\\"%s\\\"\" % f);\n+                    p4_revert(f)\n                 for f in filesToAdd:\n-                    system(\"rm %s\" %f)\n+                    os.remove(f)\n                 return\n             elif response == \"a\":\n                 os.system(applyPatchCmd)\n@@ -910,10 +957,10 @@ class P4Submit(Command, P4UserMap):\n         system(applyPatchCmd)\n \n         for f in filesToAdd:\n-            p4_system(\"add \\\"%s\\\"\" % f)\n+            p4_add(f)\n         for f in filesToDelete:\n-            p4_system(\"revert \\\"%s\\\"\" % f)\n-            p4_system(\"delete \\\"%s\\\"\" % f)\n+            p4_revert(f)\n+            p4_delete(f)\n \n         # Set/clear executable bits\n         for f in filesToChangeExecBit.keys():\n@@ -935,7 +982,7 @@ class P4Submit(Command, P4UserMap):\n                 del(os.environ[\"P4DIFF\"])\n             diff = \"\"\n             for editedFile in editedFiles:\n-                diff += p4_read_pipe(\"diff -du %r\" % editedFile)\n+                diff += p4_read_pipe(['diff', '-du', editedFile])\n \n             newdiff = \"\"\n             for newFile in filesToAdd:\n@@ -987,7 +1034,7 @@ class P4Submit(Command, P4UserMap):\n                 submitTemplate = message[:message.index(separatorLine)]\n                 if self.isWindows:\n                     submitTemplate = submitTemplate.replace(\"\\r\\n\", \"\\n\")\n-                p4_write_pipe(\"submit -i\", submitTemplate)\n+                p4_write_pipe(['submit', '-i'], submitTemplate)\n \n                 if self.preserveUser:\n                     if p4User:\n@@ -998,10 +1045,10 @@ class P4Submit(Command, P4UserMap):\n \n             else:\n                 for f in editedFiles:\n-                    p4_system(\"revert \\\"%s\\\"\" % f);\n+                    p4_revert(f)\n                 for f in filesToAdd:\n-                    p4_system(\"revert \\\"%s\\\"\" % f);\n-                    system(\"rm %s\" %f)\n+                    p4_revert(f)\n+                    os.remove(f)\n \n             os.remove(fileName)\n         else:\n@@ -1054,8 +1101,7 @@ class P4Submit(Command, P4UserMap):\n \n         chdir(self.clientPath)\n         print \"Synchronizing p4 checkout...\"\n-        p4_system(\"sync ...\")\n-\n+        p4_sync(\"...\")\n         self.check()\n \n         commits = []\n@@ -1269,7 +1315,7 @@ class P4Sync(Command, P4UserMap):\n             # operations.  utf16 is converted to ascii or utf8, perhaps.\n             # But ascii text saved as -t utf16 is completely mangled.\n             # Invoke print -o to get the real contents.\n-            text = p4_read_pipe('print -q -o - \"%s\"' % file['depotFile'])\n+            text = p4_read_pipe(['print', '-q', '-o', '-', file['depotFile']])\n             contents = [ text ]\n \n         # Perhaps windows wants unicode, utf16 newlines translated too;\n@@ -1365,10 +1411,11 @@ class P4Sync(Command, P4UserMap):\n             def streamP4FilesCbSelf(entry):\n                 self.streamP4FilesCb(entry)\n \n-            p4CmdList(\"-x - print\",\n-                '\\n'.join(['%s#%s' % (f['path'], f['rev'])\n-                                                  for f in filesToRead]),\n-                cb=streamP4FilesCbSelf)\n+            fileArgs = ['%s#%s' % (f['path'], f['rev']) for f in filesToRead]\n+\n+            p4CmdList([\"-x\", \"-\", \"print\"],\n+                      stdin=fileArgs,\n+                      cb=streamP4FilesCbSelf)\n \n             # do the last chunk\n             if self.stream_file.has_key('depotFile'):\n@@ -1429,8 +1476,8 @@ class P4Sync(Command, P4UserMap):\n             if self.verbose:\n                 print \"Change %s is labelled %s\" % (change, labelDetails)\n \n-            files = p4CmdList(\"files \" + ' '.join ([\"%s...@%s\" % (p, change)\n-                                                    for p in branchPrefixes]))\n+            files = p4CmdList([\"files\"] + [\"%s...@%s\" % (p, change)\n+                                                    for p in branchPrefixes])\n \n             if len(files) == len(labelRevisions):\n \n@@ -1478,9 +1525,9 @@ class P4Sync(Command, P4UserMap):\n             newestChange = 0\n             if self.verbose:\n                 print \"Querying files for label %s\" % label\n-            for file in p4CmdList(\"files \"\n-                                  +  ' '.join ([\"%s...@%s\" % (p, label)\n-                                                for p in self.depotPaths])):\n+            for file in p4CmdList([\"files\"] +\n+                                      [\"%s...@%s\" % (p, label)\n+                                          for p in self.depotPaths]):\n                 revisions[file[\"depotFile\"]] = file[\"rev\"]\n                 change = int(file[\"change\"])\n                 if change > newestChange:\n@@ -1735,10 +1782,9 @@ class P4Sync(Command, P4UserMap):\n         newestRevision = 0\n \n         fileCnt = 0\n-        for info in p4CmdList(\"files \"\n-                              +  ' '.join([\"%s...%s\"\n-                                           % (p, revision)\n-                                           for p in self.depotPaths])):\n+        fileArgs = [\"%s...%s\" % (p,revision) for p in self.depotPaths]\n+\n+        for info in p4CmdList([\"files\"] + fileArgs):\n \n             if 'code' in info and info['code'] == 'error':\n                 sys.stderr.write(\"p4 returned an error: %s\\n\"\ndiff --git a/t/t9803-git-shell-metachars.sh b/t/t9803-git-shell-metachars.sh\nnew file mode 100755\nindex 0000000..db04375\n--- /dev/null\n+++ b/t/t9803-git-shell-metachars.sh\n@@ -0,0 +1,64 @@\n+#!/bin/sh\n+\n+test_description='git-p4 transparency to shell metachars in filenames'\n+\n+. ./lib-git-p4.sh\n+\n+test_expect_success 'start p4d' '\n+\tstart_p4d\n+'\n+\n+test_expect_success 'init depot' '\n+\t(\n+\t\tcd \"$cli\" &&\n+\t\techo file1 >file1 &&\n+\t\tp4 add file1 &&\n+\t\tp4 submit -d \"file1\"\n+\t)\n+'\n+\n+test_expect_success 'shell metachars in filenames' '\n+\t\"$GITP4\" clone --dest=\"$git\" //depot &&\n+\ttest_when_finished cleanup_git &&\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgit config git-p4.skipSubmitEditCheck true &&\n+\t\techo f1 >foo\\$bar &&\n+\t\tgit add foo\\$bar &&\n+\t\techo f2 >\"file with spaces\" &&\n+\t\tgit add \"file with spaces\" &&\n+\t\tgit commit -m \"add files\" &&\n+\t\tP4EDITOR=touch \"$GITP4\" submit\n+\t) &&\n+\t(\n+\t\tcd \"$cli\" &&\n+\t\tp4 sync ... &&\n+\t\ttest -e \"file with spaces\" &&\n+\t\ttest -e \"foo\\$bar\"\n+\t)\n+'\n+\n+test_expect_success 'deleting with shell metachars' '\n+\t\"$GITP4\" clone --dest=\"$git\" //depot &&\n+\ttest_when_finished cleanup_git &&\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgit config git-p4.skipSubmitEditCheck true &&\n+\t\tgit rm foo\\$bar &&\n+\t\tgit rm file\\ with\\ spaces &&\n+\t\tgit commit -m \"remove files\" &&\n+\t\tP4EDITOR=touch \"$GITP4\" submit\n+\t) &&\n+\t(\n+\t\tcd \"$cli\" &&\n+\t\tp4 sync ... &&\n+\t\ttest ! -e \"file with spaces\" &&\n+\t\ttest ! -e foo\\$bar\n+\t)\n+'\n+\n+test_expect_success 'kill p4d' '\n+\tkill_p4d\n+'\n+\n+test_done\n-- \n1.7.7\n"},{"id":"177790","messageId":"201110161659.22261.stefano.lattarini@gmail.com","threadId":"28694","inReplyTo":"20111016144435.GE22144@arf.padd.com","subject":"Re: [PATCH 2/6] git-p4: handle utf16 filetype properly","fromName":"Stefano Lattarini","fromEmail":"stefano.lattarini@gmail.com","sentAt":"2011-10-16T14:59:21Z","receivedAt":"2011-10-16T14:59:21Z","isPatch":true,"sender":{"key":"stefano.lattarini@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1429199?v=4"},"body":"Hi Pete, and thanks for taking my previous remarks into account.  I have\none more nit/question though ...\n\nOn Sunday 16 October 2011, Pete Wyckoff wrote:\n> diff --git a/t/t9802-git-p4-filetype.sh b/t/t9802-git-p4-filetype.sh\n> new file mode 100755\n> index 0000000..dff0e02\n> --- /dev/null\n> +++ b/t/t9802-git-p4-filetype.sh\n> @@ -0,0 +1,108 @@\n> +#!/bin/sh\n> +\n> + [SNIP]\n> +\n> +\t\tprintf \"three\\nline\\ntext\" >f-ascii &&\n>\nWith this command, the `f-ascii' file won't be newline-terminated.  Is\nthis intended, or the result of an oversight?  The same goes for further\nsimilar usages in the rest f the patch.\n\nRegards,\n  Stefano\n"},{"id":"177792","messageId":"20111016152247.GA22954@arf.padd.com","threadId":"28694","inReplyTo":"201110161659.22261.stefano.lattarini@gmail.com","subject":"Re: [PATCH 2/6] git-p4: handle utf16 filetype properly","fromName":"Pete Wyckoff","fromEmail":"pw@padd.com","sentAt":"2011-10-16T15:22:47Z","receivedAt":"2011-10-16T15:22:47Z","isPatch":true,"sender":{"key":"pw@padd.com","avatar":null},"body":"stefano.lattarini@gmail.com wrote on Sun, 16 Oct 2011 16:59 +0200:\n> Hi Pete, and thanks for taking my previous remarks into account.  I have\n> one more nit/question though ...\n> \n> On Sunday 16 October 2011, Pete Wyckoff wrote:\n> > diff --git a/t/t9802-git-p4-filetype.sh b/t/t9802-git-p4-filetype.sh\n> > new file mode 100755\n> > index 0000000..dff0e02\n> > --- /dev/null\n> > +++ b/t/t9802-git-p4-filetype.sh\n> > @@ -0,0 +1,108 @@\n> > +#!/bin/sh\n> > +\n> > + [SNIP]\n> > +\n> > +\t\tprintf \"three\\nline\\ntext\" >f-ascii &&\n> >\n> With this command, the `f-ascii' file won't be newline-terminated.  Is\n> this intended, or the result of an oversight?  The same goes for further\n> similar usages in the rest f the patch.\n\nThat is harmless.  I think an earlier version used to count the\nlines, but now it is just random text.  Ditto the other places.\nA trailing \\n might be prettier, though, for some future debugging\nof the tests.  I tested that both with and without \\n pass.\n\n\t\t-- Pete\n"},{"id":"177798","messageId":"7vaa90reqm.fsf@alter.siamese.dyndns.org","threadId":"28694","inReplyTo":"20111016144435.GE22144@arf.padd.com","subject":"Re: [PATCH 2/6] git-p4: handle utf16 filetype properly","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-10-16T18:08:33Z","receivedAt":"2011-10-16T18:08:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Pete Wyckoff <pw@padd.com> writes:\n\n> +build_smush() {\n> +\tcat >k_smush.py <<-\\EOF &&\n> +\timport re, sys\n> +\tsys.stdout.write(re.sub(r'(?i)\\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\\$', r'$\\1$', sys.stdin.read()))\n> +\tEOF\n> +\tcat >ko_smush.py <<-\\EOF\n> +\timport re, sys\n> +\tsys.stdout.write(re.sub(r'(?i)\\$(Id|Header):[^$]*\\$', r'$\\1$', sys.stdin.read()))\n> +\tEOF\n> +}\n> +\n> +test_expect_success 'keyword file test' '\n> +\tbuild_smush &&\n> +\ttest_when_finished rm -f k_smush.py ko_smush.py &&\n> +\ttest_when_finished cleanup_git &&\n> +\t\"$GITP4\" clone --dest=\"$git\" //depot@all &&\n> +\t(\n> +\t\tcd \"$git\" &&\n> +\n> +\t\t# text, ensure unexpanded\n> +\t\tpython \"$TRASH_DIRECTORY/k_smush.py\" <\"$cli/k-text-k\" >cli-k-text-k-smush &&\n> +\t\ttest_cmp cli-k-text-k-smush k-text-k &&\n> +\t\tpython \"$TRASH_DIRECTORY/ko_smush.py\" <\"$cli/k-text-ko\" >cli-k-text-ko-smush &&\n> +\t\ttest_cmp cli-k-text-ko-smush k-text-ko &&\n> +\n> +\t\t# utf16, even though p4 expands keywords, git-p4 does not\n> +\t\t# try to undo that\n> +\t\ttest_cmp \"$cli/k-utf16-k\" k-utf16-k &&\n> +\t\ttest_cmp \"$cli/k-utf16-ko\" k-utf16-ko\n> +\t)\n> +'\n\nShouldn't this pay attention to PYTHON_PATH in any way?\n"},{"id":"177808","messageId":"20111016224719.GA23368@arf.padd.com","threadId":"28694","inReplyTo":"7vaa90reqm.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 2/6] git-p4: handle utf16 filetype properly","fromName":"Pete Wyckoff","fromEmail":"pw@padd.com","sentAt":"2011-10-16T22:47:19Z","receivedAt":"2011-10-16T22:47:19Z","isPatch":true,"sender":{"key":"pw@padd.com","avatar":null},"body":"gitster@pobox.com wrote on Sun, 16 Oct 2011 11:08 -0700:\n> Pete Wyckoff <pw@padd.com> writes:\n> > +\t\tpython \"$TRASH_DIRECTORY/k_smush.py\" <\"$cli/k-text-k\" >cli-k-text-k-smush &&\n> \n> Shouldn't this pay attention to PYTHON_PATH in any way?\n\nYes.  I did not notice that the configuration variable existed.\nI'll respond to 1/6 to fix that one too.  Below is the new 2/6,\nplease.  Includes the extra \\n that Stefano noticed too.\n\n\t\t-- Pete\n\n--------------------8<--------------------------\nFrom f177a75182576251ae4c895861d1e0f1f92794fc Mon Sep 17 00:00:00 2001\nFrom: Pete Wyckoff <pw@padd.com>\nDate: Sat, 17 Sep 2011 19:16:14 -0400\nSubject: [PATCH 2/6] git-p4: handle utf16 filetype properly\n\nOne of the filetypes that p4 supports is utf16.  Its behavior is\nodd in this case.  The data delivered through \"p4 -G print\" is\nnot encoded in utf16, although \"p4 print -o\" will produce the\nproper utf16-encoded file.\n\nWhen dealing with this filetype, discard the data from -G, and\ninstead read the contents directly.\n\nAn alternate approach would be to try to encode the data in\npython.  That worked for true utf16 files, but for other files\nmarked as utf16, p4 delivers mangled text in no recognizable encoding.\n\nAdd a test case to check utf16 handling, and +k and +ko handling.\n\nReported-by: Chris Li <git@chrisli.org>\nAcked-by: Luke Diamand <luke@diamand.org>\nSigned-off-by: Pete Wyckoff <pw@padd.com>\n---\n contrib/fast-import/git-p4 |   11 +++++\n t/t9802-git-p4-filetype.sh |  108 ++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 119 insertions(+), 0 deletions(-)\n create mode 100755 t/t9802-git-p4-filetype.sh\n\ndiff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4\nindex 2f7b270..e69caf3 100755\n--- a/contrib/fast-import/git-p4\n+++ b/contrib/fast-import/git-p4\n@@ -1238,6 +1238,15 @@ class P4Sync(Command, P4UserMap):\n             data = ''.join(contents)\n             contents = [data[:-1]]\n \n+        if file['type'].startswith(\"utf16\"):\n+            # p4 delivers different text in the python output to -G\n+            # than it does when using \"print -o\", or normal p4 client\n+            # operations.  utf16 is converted to ascii or utf8, perhaps.\n+            # But ascii text saved as -t utf16 is completely mangled.\n+            # Invoke print -o to get the real contents.\n+            text = p4_read_pipe('print -q -o - \"%s\"' % file['depotFile'])\n+            contents = [ text ]\n+\n         if self.isWindows and file[\"type\"].endswith(\"text\"):\n             mangled = []\n             for data in contents:\n@@ -1245,6 +1254,8 @@ class P4Sync(Command, P4UserMap):\n                 mangled.append(data)\n             contents = mangled\n \n+        # Note that we do not try to de-mangle keywords on utf16 files,\n+        # even though in theory somebody may want that.\n         if file['type'] in ('text+ko', 'unicode+ko', 'binary+ko'):\n             contents = map(lambda text: re.sub(r'(?i)\\$(Id|Header):[^$]*\\$',r'$\\1$', text), contents)\n         elif file['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'):\ndiff --git a/t/t9802-git-p4-filetype.sh b/t/t9802-git-p4-filetype.sh\nnew file mode 100755\nindex 0000000..3b358ef\n--- /dev/null\n+++ b/t/t9802-git-p4-filetype.sh\n@@ -0,0 +1,108 @@\n+#!/bin/sh\n+\n+test_description='git-p4 p4 filetype tests'\n+\n+. ./lib-git-p4.sh\n+\n+test_expect_success 'start p4d' '\n+\tstart_p4d\n+'\n+\n+test_expect_success 'utf-16 file create' '\n+\t(\n+\t\tcd \"$cli\" &&\n+\n+\t\t# p4 saves this verbatim\n+\t\tprintf \"three\\nline\\ntext\\n\" >f-ascii &&\n+\t\tp4 add -t text f-ascii &&\n+\n+\t\t# p4 adds \\377\\376 header\n+\t\tcp f-ascii f-ascii-as-utf16 &&\n+\t\tp4 add -t utf16 f-ascii-as-utf16 &&\n+\n+\t\t# p4 saves this exactly as iconv produced it\n+\t\tprintf \"three\\nline\\ntext\\n\" | iconv -f ascii -t utf-16 >f-utf16 &&\n+\t\tp4 add -t utf16 f-utf16 &&\n+\n+\t\t# this also is unchanged\n+\t\tcp f-utf16 f-utf16-as-text &&\n+\t\tp4 add -t text f-utf16-as-text &&\n+\n+\t\tp4 submit -d \"f files\" &&\n+\n+\t\t# force update of client files\n+\t\tp4 sync -f\n+\t)\n+'\n+\n+test_expect_success 'utf-16 file test' '\n+\ttest_when_finished cleanup_git &&\n+\t\"$GITP4\" clone --dest=\"$git\" //depot@all &&\n+\t(\n+\t\tcd \"$git\" &&\n+\n+\t\ttest_cmp \"$cli/f-ascii\" f-ascii &&\n+\t\ttest_cmp \"$cli/f-ascii-as-utf16\" f-ascii-as-utf16 &&\n+\t\ttest_cmp \"$cli/f-utf16\" f-utf16 &&\n+\t\ttest_cmp \"$cli/f-utf16-as-text\" f-utf16-as-text\n+\t)\n+'\n+\n+test_expect_success 'keyword file create' '\n+\t(\n+\t\tcd \"$cli\" &&\n+\n+\t\tprintf \"id\\n\\$Id\\$\\n\\$Author\\$\\ntext\\n\" >k-text-k &&\n+\t\tp4 add -t text+k k-text-k &&\n+\n+\t\tcp k-text-k k-text-ko &&\n+\t\tp4 add -t text+ko k-text-ko &&\n+\n+\t\tcat k-text-k | iconv -f ascii -t utf-16 >k-utf16-k &&\n+\t\tp4 add -t utf16+k k-utf16-k &&\n+\n+\t\tcp k-utf16-k k-utf16-ko &&\n+\t\tp4 add -t utf16+ko k-utf16-ko &&\n+\n+\t\tp4 submit -d \"k files\" &&\n+\t\tp4 sync -f\n+\t)\n+'\n+\n+build_smush() {\n+\tcat >k_smush.py <<-\\EOF &&\n+\timport re, sys\n+\tsys.stdout.write(re.sub(r'(?i)\\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\\$', r'$\\1$', sys.stdin.read()))\n+\tEOF\n+\tcat >ko_smush.py <<-\\EOF\n+\timport re, sys\n+\tsys.stdout.write(re.sub(r'(?i)\\$(Id|Header):[^$]*\\$', r'$\\1$', sys.stdin.read()))\n+\tEOF\n+}\n+\n+test_expect_success 'keyword file test' '\n+\tbuild_smush &&\n+\ttest_when_finished rm -f k_smush.py ko_smush.py &&\n+\ttest_when_finished cleanup_git &&\n+\t\"$GITP4\" clone --dest=\"$git\" //depot@all &&\n+\t(\n+\t\tcd \"$git\" &&\n+\n+\t\t# text, ensure unexpanded\n+\t\t\"$PYTHON_PATH\" \"$TRASH_DIRECTORY/k_smush.py\" <\"$cli/k-text-k\" >cli-k-text-k-smush &&\n+\t\ttest_cmp cli-k-text-k-smush k-text-k &&\n+\t\t\"$PYTHON_PATH\" \"$TRASH_DIRECTORY/ko_smush.py\" <\"$cli/k-text-ko\" >cli-k-text-ko-smush &&\n+\t\ttest_cmp cli-k-text-ko-smush k-text-ko &&\n+\n+\t\t# utf16, even though p4 expands keywords, git-p4 does not\n+\t\t# try to undo that\n+\t\ttest_cmp \"$cli/k-utf16-k\" k-utf16-k &&\n+\t\ttest_cmp \"$cli/k-utf16-ko\" k-utf16-ko\n+\t)\n+'\n+\n+test_expect_success 'kill p4d' '\n+\tkill_p4d\n+'\n+\n+test_done\n-- \n1.7.7\n"},{"id":"177809","messageId":"20111016224833.GB23368@arf.padd.com","threadId":"28694","inReplyTo":"20111016144319.GD22144@arf.padd.com","subject":"Re: [PATCH 1/6] git-p4 tests: refactor and cleanup","fromName":"Pete Wyckoff","fromEmail":"pw@padd.com","sentAt":"2011-10-16T22:48:33Z","receivedAt":"2011-10-16T22:48:33Z","isPatch":true,"sender":{"key":"pw@padd.com","avatar":null},"body":"pw@padd.com wrote on Sun, 16 Oct 2011 10:43 -0400:\n> Introduce a library for functions that are common to\n> multiple git-p4 test files.\n\nUpdate, replacing bare \"python\" invocation with \"$PYTHON_PATH\".\n\n\n-------------------------8<-----------------------------\n\n>From 3a26dbf40d9aa9f8432d994d5697ad10fce8ed91 Mon Sep 17 00:00:00 2001\nFrom: Pete Wyckoff <pw@padd.com>\nDate: Mon, 22 Aug 2011 22:20:33 -0400\nSubject: [PATCH 1/6] git-p4 tests: refactor and cleanup\n\nIntroduce a library for functions that are common to\nmultiple git-p4 test files.\n\nBe a bit more clever about starting and stopping p4d.\nSpecify a unique port number for each test, so that\ntests can run in parallel.  Start p4d not in daemon mode,\nand save the pid, to be able to kill it cleanly later.\nNever kill p4d at startup; always shutdown cleanly.\n\nHandle directory changes better.  Always chdir inside\na subshell, and remove any post-test directory changes.\n\nClean up whitespace, and use test_cmp and test_must_fail\nmore consistently.\n\nSeparate the tests related to detecting p4 branches\ninto their own file, and add a few more.\n\nAcked-by: Luke Diamand <luke@diamand.org>\nSigned-off-by: Pete Wyckoff <pw@padd.com>\n---\n t/lib-git-p4.sh          |   74 +++++++\n t/t9800-git-p4.sh        |  546 ++++++++++++++++++++--------------------------\n t/t9801-git-p4-branch.sh |  233 ++++++++++++++++++++\n 3 files changed, 544 insertions(+), 309 deletions(-)\n create mode 100644 t/lib-git-p4.sh\n create mode 100755 t/t9801-git-p4-branch.sh\n\ndiff --git a/t/lib-git-p4.sh b/t/lib-git-p4.sh\nnew file mode 100644\nindex 0000000..a870f9a\n--- /dev/null\n+++ b/t/lib-git-p4.sh\n@@ -0,0 +1,74 @@\n+#\n+# Library code for git-p4 tests\n+#\n+\n+. ./test-lib.sh\n+\n+if ! test_have_prereq PYTHON; then\n+\tskip_all='skipping git-p4 tests; python not available'\n+\ttest_done\n+fi\n+( p4 -h && p4d -h ) >/dev/null 2>&1 || {\n+\tskip_all='skipping git-p4 tests; no p4 or p4d'\n+\ttest_done\n+}\n+\n+GITP4=\"$GIT_BUILD_DIR/contrib/fast-import/git-p4\"\n+\n+# Try to pick a unique port: guess a large number, then hope\n+# no more than one of each test is running.\n+#\n+# This does not handle the case where somebody else is running the\n+# same tests and has chosen the same ports.\n+testid=${this_test#t}\n+git_p4_test_start=9800\n+P4DPORT=$((10669 + ($testid - $git_p4_test_start)))\n+\n+export P4PORT=localhost:$P4DPORT\n+export P4CLIENT=client\n+\n+db=\"$TRASH_DIRECTORY/db\"\n+cli=\"$TRASH_DIRECTORY/cli\"\n+git=\"$TRASH_DIRECTORY/git\"\n+pidfile=\"$TRASH_DIRECTORY/p4d.pid\"\n+\n+start_p4d() {\n+\tmkdir -p \"$db\" \"$cli\" \"$git\" &&\n+\t(\n+\t\tp4d -q -r \"$db\" -p $P4DPORT &\n+\t\techo $! >\"$pidfile\"\n+\t) &&\n+\tfor i in 1 2 3 4 5 ; do\n+\t\tp4 info >/dev/null 2>&1 && break || true &&\n+\t\techo waiting for p4d to start &&\n+\t\tsleep 1\n+\tdone &&\n+\t# complain if it never started\n+\tp4 info >/dev/null &&\n+\t(\n+\t\tcd \"$cli\" &&\n+\t\tp4 client -i <<-EOF\n+\t\tClient: client\n+\t\tDescription: client\n+\t\tRoot: $cli\n+\t\tView: //depot/... //client/...\n+\t\tEOF\n+\t)\n+}\n+\n+kill_p4d() {\n+\tpid=$(cat \"$pidfile\")\n+\t# it had better exist for the first kill\n+\tkill $pid &&\n+\tfor i in 1 2 3 4 5 ; do\n+\t\tkill $pid >/dev/null 2>&1 || break\n+\t\tsleep 1\n+\tdone &&\n+\t# complain if it would not die\n+\ttest_must_fail kill $pid >/dev/null 2>&1 &&\n+\trm -rf \"$db\" \"$cli\" \"$pidfile\"\n+}\n+\n+cleanup_git() {\n+\trm -rf \"$git\"\n+}\ndiff --git a/t/t9800-git-p4.sh b/t/t9800-git-p4.sh\nindex 01ba041..272de3f 100755\n--- a/t/t9800-git-p4.sh\n+++ b/t/t9800-git-p4.sh\n@@ -2,76 +2,51 @@\n \n test_description='git-p4 tests'\n \n-. ./test-lib.sh\n+. ./lib-git-p4.sh\n \n-( p4 -h && p4d -h ) >/dev/null 2>&1 || {\n-\tskip_all='skipping git-p4 tests; no p4 or p4d'\n-\ttest_done\n-}\n-\n-GITP4=$GIT_BUILD_DIR/contrib/fast-import/git-p4\n-P4DPORT=10669\n-\n-export P4PORT=localhost:$P4DPORT\n-\n-db=\"$TRASH_DIRECTORY/db\"\n-cli=\"$TRASH_DIRECTORY/cli\"\n-git=\"$TRASH_DIRECTORY/git\"\n-\n-test_debug 'echo p4d -q -d -r \"$db\" -p $P4DPORT'\n-test_expect_success setup '\n-\tmkdir -p \"$db\" &&\n-\tp4d -q -d -r \"$db\" -p $P4DPORT &&\n-\tmkdir -p \"$cli\" &&\n-\tmkdir -p \"$git\" &&\n-\texport P4PORT=localhost:$P4DPORT\n+test_expect_success 'start p4d' '\n+\tstart_p4d\n '\n \n test_expect_success 'add p4 files' '\n-\tcd \"$cli\" &&\n-\tp4 client -i <<-EOF &&\n-\tClient: client\n-\tDescription: client\n-\tRoot: $cli\n-\tView: //depot/... //client/...\n-\tEOF\n-\texport P4CLIENT=client &&\n-\techo file1 >file1 &&\n-\tp4 add file1 &&\n-\tp4 submit -d \"file1\" &&\n-\techo file2 >file2 &&\n-\tp4 add file2 &&\n-\tp4 submit -d \"file2\" &&\n-\tcd \"$TRASH_DIRECTORY\"\n+\t(\n+\t\tcd \"$cli\" &&\n+\t\techo file1 >file1 &&\n+\t\tp4 add file1 &&\n+\t\tp4 submit -d \"file1\" &&\n+\t\techo file2 >file2 &&\n+\t\tp4 add file2 &&\n+\t\tp4 submit -d \"file2\"\n+\t)\n '\n \n-cleanup_git() {\n-\tcd \"$TRASH_DIRECTORY\" &&\n-\trm -rf \"$git\" &&\n-\tmkdir \"$git\"\n-}\n-\n test_expect_success 'basic git-p4 clone' '\n \t\"$GITP4\" clone --dest=\"$git\" //depot &&\n \ttest_when_finished cleanup_git &&\n-\tcd \"$git\" &&\n-\tgit log --oneline >lines &&\n-\ttest_line_count = 1 lines\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgit log --oneline >lines &&\n+\t\ttest_line_count = 1 lines\n+\t)\n '\n \n test_expect_success 'git-p4 clone @all' '\n \t\"$GITP4\" clone --dest=\"$git\" //depot@all &&\n \ttest_when_finished cleanup_git &&\n-\tcd \"$git\" &&\n-\tgit log --oneline >lines &&\n-\ttest_line_count = 2 lines\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgit log --oneline >lines &&\n+\t\ttest_line_count = 2 lines\n+\t)\n '\n \n test_expect_success 'git-p4 sync uninitialized repo' '\n \ttest_create_repo \"$git\" &&\n \ttest_when_finished cleanup_git &&\n-\tcd \"$git\" &&\n-\ttest_must_fail \"$GITP4\" sync\n+\t(\n+\t\tcd \"$git\" &&\n+\t\ttest_must_fail \"$GITP4\" sync\n+\t)\n '\n \n #\n@@ -81,17 +56,19 @@ test_expect_success 'git-p4 sync uninitialized repo' '\n test_expect_success 'git-p4 sync new branch' '\n \ttest_create_repo \"$git\" &&\n \ttest_when_finished cleanup_git &&\n-\tcd \"$git\" &&\n-\ttest_commit head &&\n-\t\"$GITP4\" sync --branch=refs/remotes/p4/depot //depot@all &&\n-\tgit log --oneline p4/depot >lines &&\n-\ttest_line_count = 2 lines\n+\t(\n+\t\tcd \"$git\" &&\n+\t\ttest_commit head &&\n+\t\t\"$GITP4\" sync --branch=refs/remotes/p4/depot //depot@all &&\n+\t\tgit log --oneline p4/depot >lines &&\n+\t\ttest_line_count = 2 lines\n+\t)\n '\n \n test_expect_success 'exit when p4 fails to produce marshaled output' '\n \tbadp4dir=\"$TRASH_DIRECTORY/badp4dir\" &&\n-\tmkdir -p \"$badp4dir\" &&\n-\ttest_when_finished \"rm -rf $badp4dir\" &&\n+\tmkdir \"$badp4dir\" &&\n+\ttest_when_finished \"rm \\\"$badp4dir/p4\\\" && rmdir \\\"$badp4dir\\\"\" &&\n \tcat >\"$badp4dir\"/p4 <<-EOF &&\n \t#!$SHELL_PATH\n \texit 1\n@@ -103,61 +80,61 @@ test_expect_success 'exit when p4 fails to produce marshaled output' '\n '\n \n test_expect_success 'add p4 files with wildcards in the names' '\n-\tcd \"$cli\" &&\n-\techo file-wild-hash >file-wild#hash &&\n-\techo file-wild-star >file-wild\\*star &&\n-\techo file-wild-at >file-wild@at &&\n-\techo file-wild-percent >file-wild%percent &&\n-\tp4 add -f file-wild* &&\n-\tp4 submit -d \"file wildcards\"\n+\t(\n+\t\tcd \"$cli\" &&\n+\t\techo file-wild-hash >file-wild#hash &&\n+\t\techo file-wild-star >file-wild\\*star &&\n+\t\techo file-wild-at >file-wild@at &&\n+\t\techo file-wild-percent >file-wild%percent &&\n+\t\tp4 add -f file-wild* &&\n+\t\tp4 submit -d \"file wildcards\"\n+\t)\n '\n \n test_expect_success 'wildcard files git-p4 clone' '\n \t\"$GITP4\" clone --dest=\"$git\" //depot &&\n \ttest_when_finished cleanup_git &&\n-\tcd \"$git\" &&\n-\ttest -f file-wild#hash &&\n-\ttest -f file-wild\\*star &&\n-\ttest -f file-wild@at &&\n-\ttest -f file-wild%percent\n+\t(\n+\t\tcd \"$git\" &&\n+\t\ttest -f file-wild#hash &&\n+\t\ttest -f file-wild\\*star &&\n+\t\ttest -f file-wild@at &&\n+\t\ttest -f file-wild%percent\n+\t)\n '\n \n test_expect_success 'clone bare' '\n \t\"$GITP4\" clone --dest=\"$git\" --bare //depot &&\n \ttest_when_finished cleanup_git &&\n-\tcd \"$git\" &&\n-\ttest ! -d .git &&\n-\tbare=`git config --get core.bare` &&\n-\ttest \"$bare\" = true\n+\t(\n+\t\tcd \"$git\" &&\n+\t\ttest ! -d .git &&\n+\t\tbare=`git config --get core.bare` &&\n+\t\ttest \"$bare\" = true\n+\t)\n '\n \n p4_add_user() {\n-    name=$1\n-    fullname=$2\n-    p4 user -f -i <<EOF &&\n-User: $name\n-Email: $name@localhost\n-FullName: $fullname\n-EOF\n-    p4 passwd -P secret $name\n+\tname=$1 fullname=$2 &&\n+\tp4 user -f -i <<-EOF &&\n+\tUser: $name\n+\tEmail: $name@localhost\n+\tFullName: $fullname\n+\tEOF\n+\tp4 passwd -P secret $name\n }\n \n p4_grant_admin() {\n-    name=$1\n-    p4 protect -o |\\\n-\tawk \"{print}END{print \\\"    admin user $name * //depot/...\\\"}\" |\\\n-\tp4 protect -i\n+\tname=$1 &&\n+\t{\n+\t\tp4 protect -o &&\n+\t\techo \"    admin user $name * //depot/...\"\n+\t} | p4 protect -i\n }\n \n p4_check_commit_author() {\n-    file=$1\n-    user=$2\n-    if p4 changes -m 1 //depot/$file | grep $user > /dev/null ; then\n-\treturn 0\n-    else\n-\techo \"file $file not modified by user $user\" 1>&2\n-\treturn 1\n-    fi\n+\tfile=$1 user=$2 &&\n+\tp4 changes -m 1 //depot/$file | grep -q $user\n }\n \n make_change_by_user() {\n@@ -174,15 +151,17 @@ test_expect_success 'preserve users' '\n \tp4_grant_admin alice &&\n \t\"$GITP4\" clone --dest=\"$git\" //depot &&\n \ttest_when_finished cleanup_git &&\n-\tcd \"$git\" &&\n-\techo \"username: a change by alice\" >> file1 &&\n-\techo \"username: a change by bob\" >> file2 &&\n-\tgit commit --author \"Alice <alice@localhost>\" -m \"a change by alice\" file1 &&\n-\tgit commit --author \"Bob <bob@localhost>\" -m \"a change by bob\" file2 &&\n-\tgit config git-p4.skipSubmitEditCheck true &&\n-\tP4EDITOR=touch P4USER=alice P4PASSWD=secret \"$GITP4\" commit --preserve-user &&\n-\tp4_check_commit_author file1 alice &&\n-\tp4_check_commit_author file2 bob\n+\t(\n+\t\tcd \"$git\" &&\n+\t\techo \"username: a change by alice\" >>file1 &&\n+\t\techo \"username: a change by bob\" >>file2 &&\n+\t\tgit commit --author \"Alice <alice@localhost>\" -m \"a change by alice\" file1 &&\n+\t\tgit commit --author \"Bob <bob@localhost>\" -m \"a change by bob\" file2 &&\n+\t\tgit config git-p4.skipSubmitEditCheck true &&\n+\t\tP4EDITOR=touch P4USER=alice P4PASSWD=secret \"$GITP4\" commit --preserve-user &&\n+\t\tp4_check_commit_author file1 alice &&\n+\t\tp4_check_commit_author file2 bob\n+\t)\n '\n \n # Test username support, submitting as bob, who lacks admin rights. Should\n@@ -190,32 +169,37 @@ test_expect_success 'preserve users' '\n test_expect_success 'refuse to preserve users without perms' '\n \t\"$GITP4\" clone --dest=\"$git\" //depot &&\n \ttest_when_finished cleanup_git &&\n-\tcd \"$git\" &&\n-\tgit config git-p4.skipSubmitEditCheck true &&\n-\techo \"username-noperms: a change by alice\" >> file1 &&\n-\tgit commit --author \"Alice <alice@localhost>\" -m \"perms: a change by alice\" file1 &&\n-\t! P4EDITOR=touch P4USER=bob P4PASSWD=secret \"$GITP4\" commit --preserve-user &&\n-\t! git diff --exit-code HEAD..p4/master > /dev/null\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgit config git-p4.skipSubmitEditCheck true &&\n+\t\techo \"username-noperms: a change by alice\" >>file1 &&\n+\t\tgit commit --author \"Alice <alice@localhost>\" -m \"perms: a change by alice\" file1 &&\n+\t\tP4EDITOR=touch P4USER=bob P4PASSWD=secret test_must_fail \"$GITP4\" commit --preserve-user &&\n+\t\ttest_must_fail git diff --exit-code HEAD..p4/master\n+\t)\n '\n \n # What happens with unknown author? Without allowMissingP4Users it should fail.\n test_expect_success 'preserve user where author is unknown to p4' '\n \t\"$GITP4\" clone --dest=\"$git\" //depot &&\n \ttest_when_finished cleanup_git &&\n-\tcd \"$git\" &&\n-\tgit config git-p4.skipSubmitEditCheck true &&\n-\techo \"username-bob: a change by bob\" >> file1 &&\n-\tgit commit --author \"Bob <bob@localhost>\" -m \"preserve: a change by bob\" file1 &&\n-\techo \"username-unknown: a change by charlie\" >> file1 &&\n-\tgit commit --author \"Charlie <charlie@localhost>\" -m \"preserve: a change by charlie\" file1 &&\n-\t! P4EDITOR=touch P4USER=alice P4PASSWD=secret \"$GITP4\" commit --preserve-user &&\n-\t! git diff --exit-code HEAD..p4/master > /dev/null &&\n-\techo \"$0: repeat with allowMissingP4Users enabled\" &&\n-\tgit config git-p4.allowMissingP4Users true &&\n-\tgit config git-p4.preserveUser true &&\n-\tP4EDITOR=touch P4USER=alice P4PASSWD=secret \"$GITP4\" commit &&\n-\tgit diff --exit-code HEAD..p4/master > /dev/null &&\n-\tp4_check_commit_author file1 alice\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgit config git-p4.skipSubmitEditCheck true &&\n+\t\techo \"username-bob: a change by bob\" >>file1 &&\n+\t\tgit commit --author \"Bob <bob@localhost>\" -m \"preserve: a change by bob\" file1 &&\n+\t\techo \"username-unknown: a change by charlie\" >>file1 &&\n+\t\tgit commit --author \"Charlie <charlie@localhost>\" -m \"preserve: a change by charlie\" file1 &&\n+\t\tP4EDITOR=touch P4USER=alice P4PASSWD=secret test_must_fail \"$GITP4\" commit --preserve-user &&\n+\t\ttest_must_fail git diff --exit-code HEAD..p4/master &&\n+\n+\t\techo \"$0: repeat with allowMissingP4Users enabled\" &&\n+\t\tgit config git-p4.allowMissingP4Users true &&\n+\t\tgit config git-p4.preserveUser true &&\n+\t\tP4EDITOR=touch P4USER=alice P4PASSWD=secret \"$GITP4\" commit &&\n+\t\tgit diff --exit-code HEAD..p4/master &&\n+\t\tp4_check_commit_author file1 alice\n+\t)\n '\n \n # If we're *not* using --preserve-user, git-p4 should warn if we're submitting\n@@ -225,33 +209,35 @@ test_expect_success 'preserve user where author is unknown to p4' '\n test_expect_success 'not preserving user with mixed authorship' '\n \t\"$GITP4\" clone --dest=\"$git\" //depot &&\n \ttest_when_finished cleanup_git &&\n-\tcd \"$git\" &&\n-\tgit config git-p4.skipSubmitEditCheck true &&\n-\tp4_add_user derek Derek &&\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgit config git-p4.skipSubmitEditCheck true &&\n+\t\tp4_add_user derek Derek &&\n \n-\tmake_change_by_user usernamefile3 Derek derek@localhost &&\n-\tP4EDITOR=cat P4USER=alice P4PASSWD=secret \"$GITP4\" commit >actual &&\n-\tgrep \"git author derek@localhost does not match\" actual &&\n+\t\tmake_change_by_user usernamefile3 Derek derek@localhost &&\n+\t\tP4EDITOR=cat P4USER=alice P4PASSWD=secret \"$GITP4\" commit |\\\n+\t\tgrep \"git author derek@localhost does not match\" &&\n \n-\tmake_change_by_user usernamefile3 Charlie charlie@localhost &&\n-\tP4EDITOR=cat P4USER=alice P4PASSWD=secret \"$GITP4\" commit >actual &&\n-\tgrep \"git author charlie@localhost does not match\" actual &&\n+\t\tmake_change_by_user usernamefile3 Charlie charlie@localhost &&\n+\t\tP4EDITOR=cat P4USER=alice P4PASSWD=secret \"$GITP4\" commit |\\\n+\t\tgrep \"git author charlie@localhost does not match\" &&\n \n-\tmake_change_by_user usernamefile3 alice alice@localhost &&\n-\tP4EDITOR=cat P4USER=alice P4PASSWD=secret \"$GITP4\" commit >actual &&\n-\t! grep \"git author.*does not match\" actual &&\n+\t\tmake_change_by_user usernamefile3 alice alice@localhost &&\n+\t\tP4EDITOR=cat P4USER=alice P4PASSWD=secret \"$GITP4\" |\\\n+\t\ttest_must_fail grep \"git author.*does not match\" &&\n \n-\tgit config git-p4.skipUserNameCheck true &&\n-\tmake_change_by_user usernamefile3 Charlie charlie@localhost &&\n-\tP4EDITOR=cat P4USER=alice P4PASSWD=secret \"$GITP4\" commit >actual &&\n-\t! grep \"git author.*does not match\" actual &&\n+\t\tgit config git-p4.skipUserNameCheck true &&\n+\t\tmake_change_by_user usernamefile3 Charlie charlie@localhost &&\n+\t\tP4EDITOR=cat P4USER=alice P4PASSWD=secret \"$GITP4\" commit |\\\n+\t\ttest_must_fail grep \"git author.*does not match\" &&\n \n-\tp4_check_commit_author usernamefile3 alice\n+\t\tp4_check_commit_author usernamefile3 alice\n+\t)\n '\n \n marshal_dump() {\n \twhat=$1\n-\tpython -c 'import marshal, sys; d = marshal.load(sys.stdin); print d[\"'$what'\"]'\n+\t\"$PYTHON_PATH\" -c 'import marshal, sys; d = marshal.load(sys.stdin); print d[\"'$what'\"]'\n }\n \n # Sleep a bit so that the top-most p4 change did not happen \"now\".  Then\n@@ -263,10 +249,12 @@ test_expect_success 'initial import time from top change time' '\n \tsleep 3 &&\n \t\"$GITP4\" clone --dest=\"$git\" //depot &&\n \ttest_when_finished cleanup_git &&\n-\tcd \"$git\" &&\n-\tgittime=$(git show -s --raw --pretty=format:%at HEAD) &&\n-\techo $p4time $gittime &&\n-\ttest $p4time = $gittime\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgittime=$(git show -s --raw --pretty=format:%at HEAD) &&\n+\t\techo $p4time $gittime &&\n+\t\ttest $p4time = $gittime\n+\t)\n '\n \n # Rename a file and confirm that rename is not detected in P4.\n@@ -279,47 +267,49 @@ test_expect_success 'initial import time from top change time' '\n test_expect_success 'detect renames' '\n \t\"$GITP4\" clone --dest=\"$git\" //depot@all &&\n \ttest_when_finished cleanup_git &&\n-\tcd \"$git\" &&\n-\tgit config git-p4.skipSubmitEditCheck true &&\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgit config git-p4.skipSubmitEditCheck true &&\n \n-\tgit mv file1 file4 &&\n-\tgit commit -a -m \"Rename file1 to file4\" &&\n-\tgit diff-tree -r -M HEAD &&\n-\t\"$GITP4\" submit &&\n-\tp4 filelog //depot/file4 &&\n-\t! p4 filelog //depot/file4 | grep -q \"branch from\" &&\n+\t\tgit mv file1 file4 &&\n+\t\tgit commit -a -m \"Rename file1 to file4\" &&\n+\t\tgit diff-tree -r -M HEAD &&\n+\t\t\"$GITP4\" submit &&\n+\t\tp4 filelog //depot/file4 &&\n+\t\tp4 filelog //depot/file4 | test_must_fail grep -q \"branch from\" &&\n \n-\tgit mv file4 file5 &&\n-\tgit commit -a -m \"Rename file4 to file5\" &&\n-\tgit diff-tree -r -M HEAD &&\n-\tgit config git-p4.detectRenames true &&\n-\t\"$GITP4\" submit &&\n-\tp4 filelog //depot/file5 &&\n-\tp4 filelog //depot/file5 | grep -q \"branch from //depot/file4\" &&\n+\t\tgit mv file4 file5 &&\n+\t\tgit commit -a -m \"Rename file4 to file5\" &&\n+\t\tgit diff-tree -r -M HEAD &&\n+\t\tgit config git-p4.detectRenames true &&\n+\t\t\"$GITP4\" submit &&\n+\t\tp4 filelog //depot/file5 &&\n+\t\tp4 filelog //depot/file5 | grep -q \"branch from //depot/file4\" &&\n \n-\tgit mv file5 file6 &&\n-\techo update >>file6 &&\n-\tgit add file6 &&\n-\tgit commit -a -m \"Rename file5 to file6 with changes\" &&\n-\tgit diff-tree -r -M HEAD &&\n-\tlevel=$(git diff-tree -r -M HEAD | sed 1d | cut -f1 | cut -d\" \" -f5 | sed \"s/R0*//\") &&\n-\ttest -n \"$level\" && test \"$level\" -gt 0 && test \"$level\" -lt 98 &&\n-\tgit config git-p4.detectRenames $((level + 2)) &&\n-\t\"$GITP4\" submit &&\n-\tp4 filelog //depot/file6 &&\n-\t! p4 filelog //depot/file6 | grep -q \"branch from\" &&\n+\t\tgit mv file5 file6 &&\n+\t\techo update >>file6 &&\n+\t\tgit add file6 &&\n+\t\tgit commit -a -m \"Rename file5 to file6 with changes\" &&\n+\t\tgit diff-tree -r -M HEAD &&\n+\t\tlevel=$(git diff-tree -r -M HEAD | sed 1d | cut -f1 | cut -d\" \" -f5 | sed \"s/R0*//\") &&\n+\t\ttest -n \"$level\" && test \"$level\" -gt 0 && test \"$level\" -lt 98 &&\n+\t\tgit config git-p4.detectRenames $(($level + 2)) &&\n+\t\t\"$GITP4\" submit &&\n+\t\tp4 filelog //depot/file6 &&\n+\t\tp4 filelog //depot/file6 | test_must_fail grep -q \"branch from\" &&\n \n-\tgit mv file6 file7 &&\n-\techo update >>file7 &&\n-\tgit add file7 &&\n-\tgit commit -a -m \"Rename file6 to file7 with changes\" &&\n-\tgit diff-tree -r -M HEAD &&\n-\tlevel=$(git diff-tree -r -M HEAD | sed 1d | cut -f1 | cut -d\" \" -f5 | sed \"s/R0*//\") &&\n-\ttest -n \"$level\" && test \"$level\" -gt 2 && test \"$level\" -lt 100 &&\n-\tgit config git-p4.detectRenames $((level - 2)) &&\n-\t\"$GITP4\" submit &&\n-\tp4 filelog //depot/file7 &&\n-\tp4 filelog //depot/file7 | grep -q \"branch from //depot/file6\"\n+\t\tgit mv file6 file7 &&\n+\t\techo update >>file7 &&\n+\t\tgit add file7 &&\n+\t\tgit commit -a -m \"Rename file6 to file7 with changes\" &&\n+\t\tgit diff-tree -r -M HEAD &&\n+\t\tlevel=$(git diff-tree -r -M HEAD | sed 1d | cut -f1 | cut -d\" \" -f5 | sed \"s/R0*//\") &&\n+\t\ttest -n \"$level\" && test \"$level\" -gt 2 && test \"$level\" -lt 100 &&\n+\t\tgit config git-p4.detectRenames $(($level - 2)) &&\n+\t\t\"$GITP4\" submit &&\n+\t\tp4 filelog //depot/file7 &&\n+\t\tp4 filelog //depot/file7 | grep -q \"branch from //depot/file6\"\n+\t)\n '\n \n # Copy a file and confirm that copy is not detected in P4.\n@@ -336,141 +326,79 @@ test_expect_success 'detect renames' '\n test_expect_success 'detect copies' '\n \t\"$GITP4\" clone --dest=\"$git\" //depot@all &&\n \ttest_when_finished cleanup_git &&\n-\tcd \"$git\" &&\n-\tgit config git-p4.skipSubmitEditCheck true &&\n-\n-\tcp file2 file8 &&\n-\tgit add file8 &&\n-\tgit commit -a -m \"Copy file2 to file8\" &&\n-\tgit diff-tree -r -C HEAD &&\n-\t\"$GITP4\" submit &&\n-\tp4 filelog //depot/file8 &&\n-\t! p4 filelog //depot/file8 | grep -q \"branch from\" &&\n-\n-\tcp file2 file9 &&\n-\tgit add file9 &&\n-\tgit commit -a -m \"Copy file2 to file9\" &&\n-\tgit diff-tree -r -C HEAD &&\n-\tgit config git-p4.detectCopies true &&\n-\t\"$GITP4\" submit &&\n-\tp4 filelog //depot/file9 &&\n-\t! p4 filelog //depot/file9 | grep -q \"branch from\" &&\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgit config git-p4.skipSubmitEditCheck true &&\n \n-\techo \"file2\" >>file2 &&\n-\tcp file2 file10 &&\n-\tgit add file2 file10 &&\n-\tgit commit -a -m \"Modify and copy file2 to file10\" &&\n-\tgit diff-tree -r -C HEAD &&\n-\t\"$GITP4\" submit &&\n-\tp4 filelog //depot/file10 &&\n-\tp4 filelog //depot/file10 | grep -q \"branch from //depot/file\" &&\n+\t\tcp file2 file8 &&\n+\t\tgit add file8 &&\n+\t\tgit commit -a -m \"Copy file2 to file8\" &&\n+\t\tgit diff-tree -r -C HEAD &&\n+\t\t\"$GITP4\" submit &&\n+\t\tp4 filelog //depot/file8 &&\n+\t\tp4 filelog //depot/file8 | test_must_fail grep -q \"branch from\" &&\n \n-\tcp file2 file11 &&\n-\tgit add file11 &&\n-\tgit commit -a -m \"Copy file2 to file11\" &&\n-\tgit diff-tree -r -C --find-copies-harder HEAD &&\n-\tsrc=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) &&\n-\ttest \"$src\" = file10 &&\n-\tgit config git-p4.detectCopiesHarder true &&\n-\t\"$GITP4\" submit &&\n-\tp4 filelog //depot/file11 &&\n-\tp4 filelog //depot/file11 | grep -q \"branch from //depot/file\" &&\n+\t\tcp file2 file9 &&\n+\t\tgit add file9 &&\n+\t\tgit commit -a -m \"Copy file2 to file9\" &&\n+\t\tgit diff-tree -r -C HEAD &&\n+\t\tgit config git-p4.detectCopies true &&\n+\t\t\"$GITP4\" submit &&\n+\t\tp4 filelog //depot/file9 &&\n+\t\tp4 filelog //depot/file9 | test_must_fail grep -q \"branch from\" &&\n \n-\tcp file2 file12 &&\n-\techo \"some text\" >>file12 &&\n-\tgit add file12 &&\n-\tgit commit -a -m \"Copy file2 to file12 with changes\" &&\n-\tgit diff-tree -r -C --find-copies-harder HEAD &&\n-\tlevel=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f1 | cut -d\" \" -f5 | sed \"s/C0*//\") &&\n-\ttest -n \"$level\" && test \"$level\" -gt 0 && test \"$level\" -lt 98 &&\n-\tsrc=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) &&\n-\ttest \"$src\" = file10 &&\n-\tgit config git-p4.detectCopies $((level + 2)) &&\n-\t\"$GITP4\" submit &&\n-\tp4 filelog //depot/file12 &&\n-\t! p4 filelog //depot/file12 | grep -q \"branch from\" &&\n+\t\techo \"file2\" >>file2 &&\n+\t\tcp file2 file10 &&\n+\t\tgit add file2 file10 &&\n+\t\tgit commit -a -m \"Modify and copy file2 to file10\" &&\n+\t\tgit diff-tree -r -C HEAD &&\n+\t\t\"$GITP4\" submit &&\n+\t\tp4 filelog //depot/file10 &&\n+\t\tp4 filelog //depot/file10 | grep -q \"branch from //depot/file\" &&\n \n-\tcp file2 file13 &&\n-\techo \"different text\" >>file13 &&\n-\tgit add file13 &&\n-\tgit commit -a -m \"Copy file2 to file13 with changes\" &&\n-\tgit diff-tree -r -C --find-copies-harder HEAD &&\n-\tlevel=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f1 | cut -d\" \" -f5 | sed \"s/C0*//\") &&\n-\ttest -n \"$level\" && test \"$level\" -gt 2 && test \"$level\" -lt 100 &&\n-\tsrc=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) &&\n-\ttest \"$src\" = file10 &&\n-\tgit config git-p4.detectCopies $((level - 2)) &&\n-\t\"$GITP4\" submit &&\n-\tp4 filelog //depot/file13 &&\n-\tp4 filelog //depot/file13 | grep -q \"branch from //depot/file\"\n-'\n+\t\tcp file2 file11 &&\n+\t\tgit add file11 &&\n+\t\tgit commit -a -m \"Copy file2 to file11\" &&\n+\t\tgit diff-tree -r -C --find-copies-harder HEAD &&\n+\t\tsrc=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) &&\n+\t\ttest \"$src\" = file10 &&\n+\t\tgit config git-p4.detectCopiesHarder true &&\n+\t\t\"$GITP4\" submit &&\n+\t\tp4 filelog //depot/file11 &&\n+\t\tp4 filelog //depot/file11 | grep -q \"branch from //depot/file\" &&\n \n-# Create a simple branch structure in P4 depot to check if it is correctly\n-# cloned.\n-test_expect_success 'add simple p4 branches' '\n-\tcd \"$cli\" &&\n-\tmkdir branch1 &&\n-\tcd branch1 &&\n-\techo file1 >file1 &&\n-\techo file2 >file2 &&\n-\tp4 add file1 file2 &&\n-\tp4 submit -d \"branch1\" &&\n-\tp4 integrate //depot/branch1/... //depot/branch2/... &&\n-\tp4 submit -d \"branch2\" &&\n-\techo file3 >file3 &&\n-\tp4 add file3 &&\n-\tp4 submit -d \"add file3 in branch1\" &&\n-\tp4 open file2 &&\n-\techo update >>file2 &&\n-\tp4 submit -d \"update file2 in branch1\" &&\n-\tp4 integrate //depot/branch1/... //depot/branch3/... &&\n-\tp4 submit -d \"branch3\" &&\n-\tcd \"$TRASH_DIRECTORY\"\n-'\n+\t\tcp file2 file12 &&\n+\t\techo \"some text\" >>file12 &&\n+\t\tgit add file12 &&\n+\t\tgit commit -a -m \"Copy file2 to file12 with changes\" &&\n+\t\tgit diff-tree -r -C --find-copies-harder HEAD &&\n+\t\tlevel=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f1 | cut -d\" \" -f5 | sed \"s/C0*//\") &&\n+\t\ttest -n \"$level\" && test \"$level\" -gt 0 && test \"$level\" -lt 98 &&\n+\t\tsrc=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) &&\n+\t\ttest \"$src\" = file10 &&\n+\t\tgit config git-p4.detectCopies $(($level + 2)) &&\n+\t\t\"$GITP4\" submit &&\n+\t\tp4 filelog //depot/file12 &&\n+\t\tp4 filelog //depot/file12 | test_must_fail grep -q \"branch from\" &&\n \n-# Configure branches through git-config and clone them.\n-# All files are tested to make sure branches were cloned correctly.\n-# Finally, make an update to branch1 on P4 side to check if it is imported\n-# correctly by git-p4.\n-test_expect_success 'git-p4 clone simple branches' '\n-\ttest_when_finished cleanup_git &&\n-\ttest_create_repo \"$git\" &&\n-\tcd \"$git\" &&\n-\tgit config git-p4.branchList branch1:branch2 &&\n-\tgit config --add git-p4.branchList branch1:branch3 &&\n-\t\"$GITP4\" clone --dest=. --detect-branches //depot@all &&\n-\tgit log --all --graph --decorate --stat &&\n-\tgit reset --hard p4/depot/branch1 &&\n-\ttest -f file1 &&\n-\ttest -f file2 &&\n-\ttest -f file3 &&\n-\tgrep -q update file2 &&\n-\tgit reset --hard p4/depot/branch2 &&\n-\ttest -f file1 &&\n-\ttest -f file2 &&\n-\ttest ! -f file3 &&\n-\t! grep -q update file2 &&\n-\tgit reset --hard p4/depot/branch3 &&\n-\ttest -f file1 &&\n-\ttest -f file2 &&\n-\ttest -f file3 &&\n-\tgrep -q update file2 &&\n-\tcd \"$cli\" &&\n-\tcd branch1 &&\n-\tp4 edit file2 &&\n-\techo file2_ >>file2 &&\n-\tp4 submit -d \"update file2 in branch1\" &&\n-\tcd \"$git\" &&\n-\tgit reset --hard p4/depot/branch1 &&\n-\t\"$GITP4\" rebase &&\n-\tgrep -q file2_ file2\n+\t\tcp file2 file13 &&\n+\t\techo \"different text\" >>file13 &&\n+\t\tgit add file13 &&\n+\t\tgit commit -a -m \"Copy file2 to file13 with changes\" &&\n+\t\tgit diff-tree -r -C --find-copies-harder HEAD &&\n+\t\tlevel=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f1 | cut -d\" \" -f5 | sed \"s/C0*//\") &&\n+\t\ttest -n \"$level\" && test \"$level\" -gt 2 && test \"$level\" -lt 100 &&\n+\t\tsrc=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) &&\n+\t\ttest \"$src\" = file10 &&\n+\t\tgit config git-p4.detectCopies $(($level - 2)) &&\n+\t\t\"$GITP4\" submit &&\n+\t\tp4 filelog //depot/file13 &&\n+\t\tp4 filelog //depot/file13 | grep -q \"branch from //depot/file\"\n+\t)\n '\n \n-test_expect_success 'shutdown' '\n-\tpid=`pgrep -f p4d` &&\n-\ttest -n \"$pid\" &&\n-\ttest_debug \"ps wl `echo $pid`\" &&\n-\tkill $pid\n+test_expect_success 'kill p4d' '\n+\tkill_p4d\n '\n \n test_done\ndiff --git a/t/t9801-git-p4-branch.sh b/t/t9801-git-p4-branch.sh\nnew file mode 100755\nindex 0000000..a25f18d\n--- /dev/null\n+++ b/t/t9801-git-p4-branch.sh\n@@ -0,0 +1,233 @@\n+#!/bin/sh\n+\n+test_description='git-p4 p4 branching tests'\n+\n+. ./lib-git-p4.sh\n+\n+test_expect_success 'start p4d' '\n+\tstart_p4d\n+'\n+\n+#\n+# 1: //depot/main/f1\n+# 2: //depot/main/f2\n+# 3: integrate //depot/main/... -> //depot/branch1/...\n+# 4: //depot/main/f4\n+# 5: //depot/branch1/f5\n+# .: named branch branch2\n+# 6: integrate -b branch2\n+# 7: //depot/branch2/f7\n+# 8: //depot/main/f8\n+#\n+test_expect_success 'basic p4 branches' '\n+\t(\n+\t\tcd \"$cli\" &&\n+\t\tmkdir -p main &&\n+\n+\t\techo f1 >main/f1 &&\n+\t\tp4 add main/f1 &&\n+\t\tp4 submit -d \"main/f1\" &&\n+\n+\t\techo f2 >main/f2 &&\n+\t\tp4 add main/f2 &&\n+\t\tp4 submit -d \"main/f2\" &&\n+\n+\t\tp4 integrate //depot/main/... //depot/branch1/... &&\n+\t\tp4 submit -d \"integrate main to branch1\" &&\n+\n+\t\techo f4 >main/f4 &&\n+\t\tp4 add main/f4 &&\n+\t\tp4 submit -d \"main/f4\" &&\n+\n+\t\techo f5 >branch1/f5 &&\n+\t\tp4 add branch1/f5 &&\n+\t\tp4 submit -d \"branch1/f5\" &&\n+\n+\t\tp4 branch -i <<-EOF &&\n+\t\tBranch: branch2\n+\t\tView: //depot/main/... //depot/branch2/...\n+\t\tEOF\n+\n+\t\tp4 integrate -b branch2 &&\n+\t\tp4 submit -d \"integrate main to branch2\" &&\n+\n+\t\techo f7 >branch2/f7 &&\n+\t\tp4 add branch2/f7 &&\n+\t\tp4 submit -d \"branch2/f7\" &&\n+\n+\t\techo f8 >main/f8 &&\n+\t\tp4 add main/f8 &&\n+\t\tp4 submit -d \"main/f8\"\n+\t)\n+'\n+\n+test_expect_success 'import main, no branch detection' '\n+\ttest_when_finished cleanup_git &&\n+\t\"$GITP4\" clone --dest=\"$git\" //depot/main@all &&\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgit log --oneline --graph --decorate --all &&\n+\t\tgit rev-list master >wc &&\n+\t\ttest_line_count = 4 wc\n+\t)\n+'\n+\n+test_expect_success 'import branch1, no branch detection' '\n+\ttest_when_finished cleanup_git &&\n+\t\"$GITP4\" clone --dest=\"$git\" //depot/branch1@all &&\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgit log --oneline --graph --decorate --all &&\n+\t\tgit rev-list master >wc &&\n+\t\ttest_line_count = 2 wc\n+\t)\n+'\n+\n+test_expect_success 'import branch2, no branch detection' '\n+\ttest_when_finished cleanup_git &&\n+\t\"$GITP4\" clone --dest=\"$git\" //depot/branch2@all &&\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgit log --oneline --graph --decorate --all &&\n+\t\tgit rev-list master >wc &&\n+\t\ttest_line_count = 2 wc\n+\t)\n+'\n+\n+test_expect_success 'import depot, no branch detection' '\n+\ttest_when_finished cleanup_git &&\n+\t\"$GITP4\" clone --dest=\"$git\" //depot@all &&\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgit log --oneline --graph --decorate --all &&\n+\t\tgit rev-list master >wc &&\n+\t\ttest_line_count = 8 wc\n+\t)\n+'\n+\n+test_expect_success 'import depot, branch detection' '\n+\ttest_when_finished cleanup_git &&\n+\t\"$GITP4\" clone --dest=\"$git\" --detect-branches //depot@all &&\n+\t(\n+\t\tcd \"$git\" &&\n+\n+\t\tgit log --oneline --graph --decorate --all &&\n+\n+\t\t# 4 main commits\n+\t\tgit rev-list master >wc &&\n+\t\ttest_line_count = 4 wc &&\n+\n+\t\t# 3 main, 1 integrate, 1 on branch2\n+\t\tgit rev-list p4/depot/branch2 >wc &&\n+\t\ttest_line_count = 5 wc &&\n+\n+\t\t# no branch1, since no p4 branch created for it\n+\t\ttest_must_fail git show-ref p4/depot/branch1\n+\t)\n+'\n+\n+test_expect_success 'import depot, branch detection, branchList branch definition' '\n+\ttest_when_finished cleanup_git &&\n+\ttest_create_repo \"$git\" &&\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgit config git-p4.branchList main:branch1 &&\n+\t\t\"$GITP4\" clone --dest=. --detect-branches //depot@all &&\n+\n+\t\tgit log --oneline --graph --decorate --all &&\n+\n+\t\t# 4 main commits\n+\t\tgit rev-list master >wc &&\n+\t\ttest_line_count = 4 wc &&\n+\n+\t\t# 3 main, 1 integrate, 1 on branch2\n+\t\tgit rev-list p4/depot/branch2 >wc &&\n+\t\ttest_line_count = 5 wc &&\n+\n+\t\t# 2 main, 1 integrate, 1 on branch1\n+\t\tgit rev-list p4/depot/branch1 >wc &&\n+\t\ttest_line_count = 4 wc\n+\t)\n+'\n+\n+test_expect_success 'restart p4d' '\n+\tkill_p4d &&\n+\tstart_p4d\n+'\n+\n+#\n+# 1: //depot/branch1/file1\n+#    //depot/branch1/file2\n+# 2: integrate //depot/branch1/... -> //depot/branch2/...\n+# 3: //depot/branch1/file3\n+# 4: //depot/branch1/file2 (edit)\n+# 5: integrate //depot/branch1/... -> //depot/branch3/...\n+#\n+## Create a simple branch structure in P4 depot.\n+test_expect_success 'add simple p4 branches' '\n+\t(\n+\t\tcd \"$cli\" &&\n+\t\tmkdir branch1 &&\n+\t\tcd branch1 &&\n+\t\techo file1 >file1 &&\n+\t\techo file2 >file2 &&\n+\t\tp4 add file1 file2 &&\n+\t\tp4 submit -d \"branch1\" &&\n+\t\tp4 integrate //depot/branch1/... //depot/branch2/... &&\n+\t\tp4 submit -d \"branch2\" &&\n+\t\techo file3 >file3 &&\n+\t\tp4 add file3 &&\n+\t\tp4 submit -d \"add file3 in branch1\" &&\n+\t\tp4 open file2 &&\n+\t\techo update >>file2 &&\n+\t\tp4 submit -d \"update file2 in branch1\" &&\n+\t\tp4 integrate //depot/branch1/... //depot/branch3/... &&\n+\t\tp4 submit -d \"branch3\"\n+\t)\n+'\n+\n+# Configure branches through git-config and clone them.\n+# All files are tested to make sure branches were cloned correctly.\n+# Finally, make an update to branch1 on P4 side to check if it is imported\n+# correctly by git-p4.\n+test_expect_success 'git-p4 clone simple branches' '\n+\ttest_when_finished cleanup_git &&\n+\ttest_create_repo \"$git\" &&\n+\t(\n+\t\tcd \"$git\" &&\n+\t\tgit config git-p4.branchList branch1:branch2 &&\n+\t\tgit config --add git-p4.branchList branch1:branch3 &&\n+\t\t\"$GITP4\" clone --dest=. --detect-branches //depot@all &&\n+\t\tgit log --all --graph --decorate --stat &&\n+\t\tgit reset --hard p4/depot/branch1 &&\n+\t\ttest -f file1 &&\n+\t\ttest -f file2 &&\n+\t\ttest -f file3 &&\n+\t\tgrep -q update file2 &&\n+\t\tgit reset --hard p4/depot/branch2 &&\n+\t\ttest -f file1 &&\n+\t\ttest -f file2 &&\n+\t\ttest ! -f file3 &&\n+\t\ttest_must_fail grep -q update file2 &&\n+\t\tgit reset --hard p4/depot/branch3 &&\n+\t\ttest -f file1 &&\n+\t\ttest -f file2 &&\n+\t\ttest -f file3 &&\n+\t\tgrep -q update file2 &&\n+\t\tcd \"$cli\" &&\n+\t\tcd branch1 &&\n+\t\tp4 edit file2 &&\n+\t\techo file2_ >>file2 &&\n+\t\tp4 submit -d \"update file2 in branch3\" &&\n+\t\tcd \"$git\" &&\n+\t\tgit reset --hard p4/depot/branch1 &&\n+\t\t\"$GITP4\" rebase &&\n+\t\tgrep -q file2_ file2\n+\t)\n+'\n+\n+test_expect_success 'kill p4d' '\n+\tkill_p4d\n+'\n+\n+test_done\n-- \n1.7.7\n"}]}