From 46738bd7e9209e9a636dfddb46e32fd606386d81 Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Mon, 14 Jan 2013 19:46:55 -0500 Subject: [PATCH 01/14] git p4: test sync/clone --branch behavior Add failing tests to document behavior when there are multiple p4 branches, as created using the --branch option. In particular: Using clone --branch populates the specified branch correctly, but dies with an error when trying to checkout master. Calling sync without a master branch dies with an error looking for master. When there are two or more branches, a sync does nothing due to branch detection code, but that is expected. Using sync --branch to try to update just a particular branch updates no branch, but appears to succeed. Signed-off-by: Pete Wyckoff Signed-off-by: Junio C Hamano --- t/t9806-git-p4-options.sh | 53 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/t/t9806-git-p4-options.sh b/t/t9806-git-p4-options.sh index fa40cc8bb5..844aae048e 100755 --- a/t/t9806-git-p4-options.sh +++ b/t/t9806-git-p4-options.sh @@ -27,14 +27,59 @@ test_expect_success 'clone no --git-dir' ' test_must_fail git p4 clone --git-dir=xx //depot ' -test_expect_success 'clone --branch' ' +test_expect_failure 'clone --branch should checkout master' ' git p4 clone --branch=refs/remotes/p4/sb --dest="$git" //depot && test_when_finished cleanup_git && ( cd "$git" && - git ls-files >files && - test_line_count = 0 files && - test_path_is_file .git/refs/remotes/p4/sb + git rev-parse refs/remotes/p4/sb >sb && + git rev-parse refs/heads/master >master && + test_cmp sb master && + git rev-parse HEAD >head && + test_cmp sb head + ) +' + +test_expect_failure 'sync when branch is not called master should work' ' + git p4 clone --branch=refs/remotes/p4/sb --dest="$git" //depot@2 && + test_when_finished cleanup_git && + ( + cd "$git" && + git p4 sync && + git show -s --format=%s refs/remotes/p4/sb >show && + grep "change 3" show + ) +' + +# engages --detect-branches code, which will do filename filtering so +# no sync to either b1 or b2 +test_expect_success 'sync when two branches but no master should noop' ' + test_when_finished cleanup_git && + ( + cd "$git" && + git init && + git p4 sync --branch=refs/remotes/p4/b1 //depot@2 && + git p4 sync --branch=refs/remotes/p4/b2 //depot@2 && + git p4 sync && + git show -s --format=%s refs/remotes/p4/b1 >show && + grep "Initial import" show && + git show -s --format=%s refs/remotes/p4/b2 >show && + grep "Initial import" show + ) +' + +test_expect_failure 'sync --branch updates specified branch' ' + test_when_finished cleanup_git && + ( + cd "$git" && + git init && + git p4 sync --branch=refs/remotes/p4/b1 //depot@2 && + git p4 sync --branch=refs/remotes/p4/b2 //depot@2 && + git p4 sync --branch=refs/remotes/p4/b2 && + git show -s --format=%s refs/remotes/p4/b1 >show && + grep "Initial import" show && + git show -s --format=%s refs/remotes/p4/b2 >show && + grep "change 3" show ) ' From 991a2de45af713ad6476c3d03aefb30ba5599bfe Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Mon, 14 Jan 2013 19:46:56 -0500 Subject: [PATCH 02/14] git p4: rearrange and simplify hasOrigin handling Signed-off-by: Pete Wyckoff Signed-off-by: Junio C Hamano --- git-p4.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/git-p4.py b/git-p4.py index 551aec9417..e9127319d4 100755 --- a/git-p4.py +++ b/git-p4.py @@ -2748,23 +2748,23 @@ def run(self, args): self.changeRange = "" self.initialParent = "" self.previousDepotPaths = [] + self.hasOrigin = False # map from branch depot path to parent branch self.knownBranches = {} self.initialParents = {} - self.hasOrigin = originP4BranchesExist() - if not self.syncWithOrigin: - self.hasOrigin = False if self.importIntoRemotes: self.refPrefix = "refs/remotes/p4/" else: self.refPrefix = "refs/heads/p4/" - if self.syncWithOrigin and self.hasOrigin: - if not self.silent: - print "Syncing with origin first by calling git fetch origin" - system("git fetch origin") + if self.syncWithOrigin: + self.hasOrigin = originP4BranchesExist() + if self.hasOrigin: + if not self.silent: + print 'Syncing with origin first, using "git fetch origin"' + system("git fetch origin") if len(self.branch) == 0: self.branch = self.refPrefix + "master" From 2c8037edeef9ae6f510335587c04081e58564758 Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Mon, 14 Jan 2013 19:46:57 -0500 Subject: [PATCH 03/14] git p4: add comments to p4BranchesInGit Signed-off-by: Pete Wyckoff Signed-off-by: Junio C Hamano --- git-p4.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/git-p4.py b/git-p4.py index e9127319d4..de88680f2c 100755 --- a/git-p4.py +++ b/git-p4.py @@ -547,27 +547,36 @@ def gitConfigList(key): _gitConfig[key] = read_pipe("git config --get-all %s" % key, ignore_error=True).strip().split(os.linesep) return _gitConfig[key] -def p4BranchesInGit(branchesAreInRemotes = True): +def p4BranchesInGit(branchesAreInRemotes=True): + """Find all the branches whose names start with "p4/", looking + in remotes or heads as specified by the argument. Return + a dictionary of { branch: revision } for each one found. + The branch names are the short names, without any + "p4/" prefix.""" + branches = {} cmdline = "git rev-parse --symbolic " if branchesAreInRemotes: - cmdline += " --remotes" + cmdline += "--remotes" else: - cmdline += " --branches" + cmdline += "--branches" for line in read_pipe_lines(cmdline): line = line.strip() - ## only import to p4/ - if not line.startswith('p4/') or line == "p4/HEAD": + # only import to p4/ + if not line.startswith('p4/'): + continue + # special symbolic ref to p4/master + if line == "p4/HEAD": continue - branch = line - # strip off p4 - branch = re.sub ("^p4/", "", line) + # strip off p4/ prefix + branch = line[len("p4/"):] branches[branch] = parseRevision(line) + return branches def findUpstreamBranchPoint(head = "HEAD"): From 3b650fc9869a51190578aed8014d906f01f510a9 Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Mon, 14 Jan 2013 19:46:58 -0500 Subject: [PATCH 04/14] git p4: inline listExistingP4GitBranches It is four lines of code used in only one place. Simplify by including it where it is used. Signed-off-by: Pete Wyckoff Signed-off-by: Junio C Hamano --- git-p4.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/git-p4.py b/git-p4.py index de88680f2c..2443f79e07 100755 --- a/git-p4.py +++ b/git-p4.py @@ -2512,13 +2512,6 @@ def getBranchMappingFromGitBranches(self): branch = branch[len(self.projectName):] self.knownBranches[branch] = branch - def listExistingP4GitBranches(self): - # branches holds mapping from name to commit - branches = p4BranchesInGit(self.importIntoRemotes) - self.p4BranchesInGit = branches.keys() - for branch in branches.keys(): - self.initialParents[self.refPrefix + branch] = branches[branch] - def updateOptionDict(self, d): option_keys = {} if self.keepRepoPath: @@ -2799,7 +2792,12 @@ def run(self, args): if args == []: if self.hasOrigin: createOrUpdateBranchesFromOrigin(self.refPrefix, self.silent) - self.listExistingP4GitBranches() + + # branches holds mapping from branch name to sha1 + branches = p4BranchesInGit(self.importIntoRemotes) + self.p4BranchesInGit = branches.keys() + for branch in branches.keys(): + self.initialParents[self.refPrefix + branch] = branches[branch] if len(self.p4BranchesInGit) > 1: if not self.silent: From 55d124376f1d5d91be4376016d92a6ca6474428a Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Mon, 14 Jan 2013 19:46:59 -0500 Subject: [PATCH 05/14] git p4: create p4/HEAD on initial clone There is code to create a symbolic reference from p4/HEAD to p4/master. This allows saying "git show p4" as a shortcut to "git show p4/master", for example. But this reference was only created on the second "git p4 sync" (or first sync after a clone). Make it work on the initial clone or sync. Signed-off-by: Pete Wyckoff Signed-off-by: Junio C Hamano --- git-p4.py | 12 ++++++++---- t/t9806-git-p4-options.sh | 23 +++++++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/git-p4.py b/git-p4.py index 2443f79e07..7a0c0409be 100755 --- a/git-p4.py +++ b/git-p4.py @@ -2772,10 +2772,7 @@ def run(self, args): self.branch = self.refPrefix + "master" if gitBranchExists("refs/heads/p4") and self.importIntoRemotes: system("git update-ref %s refs/heads/p4" % self.branch) - system("git branch -D p4"); - # create it /after/ importing, when master exists - if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes and gitBranchExists(self.branch): - system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch)) + system("git branch -D p4") # accept either the command-line option, or the configuration variable if self.useClientSpec: @@ -3007,6 +3004,13 @@ def run(self, args): read_pipe("git update-ref -d %s" % branch) os.rmdir(os.path.join(os.environ.get("GIT_DIR", ".git"), self.tempBranchLocation)) + # Create a symbolic ref p4/HEAD pointing to p4/ to allow + # a convenient shortcut refname "p4". + if self.importIntoRemotes: + head_ref = self.refPrefix + "HEAD" + if not gitBranchExists(head_ref) and gitBranchExists(self.branch): + system(["git", "symbolic-ref", head_ref, self.branch]) + return True class P4Rebase(Command): diff --git a/t/t9806-git-p4-options.sh b/t/t9806-git-p4-options.sh index 844aae048e..4900aef19e 100755 --- a/t/t9806-git-p4-options.sh +++ b/t/t9806-git-p4-options.sh @@ -83,6 +83,29 @@ test_expect_failure 'sync --branch updates specified branch' ' ) ' +# allows using the refname "p4" as a short name for p4/master +test_expect_success 'clone creates HEAD symbolic reference' ' + git p4 clone --dest="$git" //depot && + test_when_finished cleanup_git && + ( + cd "$git" && + git rev-parse --verify refs/remotes/p4/master >master && + git rev-parse --verify p4 >p4 && + test_cmp master p4 + ) +' + +test_expect_success 'clone --branch creates HEAD symbolic reference' ' + git p4 clone --branch=refs/remotes/p4/sb --dest="$git" //depot && + test_when_finished cleanup_git && + ( + cd "$git" && + git rev-parse --verify refs/remotes/p4/sb >sb && + git rev-parse --verify p4 >p4 && + test_cmp sb p4 + ) +' + test_expect_success 'clone --changesfile' ' test_when_finished "rm cf" && printf "1\n3\n" >cf && From 695d69989429e9268c0587e3606399fc9d6cd6fa Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Mon, 14 Jan 2013 19:47:00 -0500 Subject: [PATCH 06/14] git p4: verify expected refs in clone --bare test Make sure that the standard branches are created as expected. Signed-off-by: Pete Wyckoff Signed-off-by: Junio C Hamano --- t/t9800-git-p4-basic.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/t/t9800-git-p4-basic.sh b/t/t9800-git-p4-basic.sh index 8c5979647f..166e75209f 100755 --- a/t/t9800-git-p4-basic.sh +++ b/t/t9800-git-p4-basic.sh @@ -160,9 +160,12 @@ test_expect_success 'clone --bare should make a bare repository' ' test_when_finished cleanup_git && ( cd "$git" && - test ! -d .git && - bare=`git config --get core.bare` && - test "$bare" = true + test_path_is_missing .git && + git config --get --bool core.bare true && + git rev-parse --verify refs/remotes/p4/master && + git rev-parse --verify refs/remotes/p4/HEAD && + git rev-parse --verify refs/heads/master && + git rev-parse --verify HEAD ) ' From c595956db99a728ab2247b564c8283cf5c0d84e6 Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Mon, 14 Jan 2013 19:47:01 -0500 Subject: [PATCH 07/14] git p4: clone --branch should checkout master When using the --branch argument to "git p4 clone", one might specify a destination for p4 changes different from the default refs/remotes/p4/master. Both cases should create a master branch and checkout files. Signed-off-by: Pete Wyckoff Signed-off-by: Junio C Hamano --- Documentation/git-p4.txt | 3 +-- git-p4.py | 20 +++++++++----------- t/t9806-git-p4-options.sh | 2 +- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/Documentation/git-p4.txt b/Documentation/git-p4.txt index beff6229c8..2623bee5fc 100644 --- a/Documentation/git-p4.txt +++ b/Documentation/git-p4.txt @@ -174,8 +174,7 @@ subsequent 'sync' operations. --branch :: Import changes into given branch. If the branch starts with 'refs/', it will be used as is, otherwise the path 'refs/heads/' - will be prepended. The default branch is 'master'. If used - with an initial clone, no HEAD will be checked out. + will be prepended. The default branch is 'p4/master'. + This example imports a new remote "p4/proj2" into an existing git repository: diff --git a/git-p4.py b/git-p4.py index 7a0c0409be..c59ad93034 100755 --- a/git-p4.py +++ b/git-p4.py @@ -3118,17 +3118,15 @@ def run(self, args): if not P4Sync.run(self, depotPaths): return False - if self.branch != "master": - if self.importIntoRemotes: - masterbranch = "refs/remotes/p4/master" - else: - masterbranch = "refs/heads/p4/master" - if gitBranchExists(masterbranch): - system("git branch master %s" % masterbranch) - if not self.cloneBare: - system("git checkout -f") - else: - print "Could not detect main branch. No checkout/master branch created." + + # create a master branch and check out a work tree + if gitBranchExists(self.branch): + system([ "git", "branch", "master", self.branch ]) + if not self.cloneBare: + system([ "git", "checkout", "-f" ]) + else: + print 'Not checking out any branch, use ' \ + '"git checkout -q -b master "' # auto-set this variable if invoked with --use-client-spec if self.useClientSpec_from_options: diff --git a/t/t9806-git-p4-options.sh b/t/t9806-git-p4-options.sh index 4900aef19e..2ad3a3e0ca 100755 --- a/t/t9806-git-p4-options.sh +++ b/t/t9806-git-p4-options.sh @@ -27,7 +27,7 @@ test_expect_success 'clone no --git-dir' ' test_must_fail git p4 clone --git-dir=xx //depot ' -test_expect_failure 'clone --branch should checkout master' ' +test_expect_success 'clone --branch should checkout master' ' git p4 clone --branch=refs/remotes/p4/sb --dest="$git" //depot && test_when_finished cleanup_git && ( From 182edef5b473bc13c57a158e3282c0aab4da01d6 Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Mon, 14 Jan 2013 19:47:02 -0500 Subject: [PATCH 08/14] git p4 doc: fix branch detection example Make sure that the example on how to use git-p4.branchList works if typed directly. In particular, it does not make sense to set a config variable until the git repository has been initialized. Reported-by: Olivier Delalleau Signed-off-by: Pete Wyckoff Signed-off-by: Junio C Hamano --- Documentation/git-p4.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/git-p4.txt b/Documentation/git-p4.txt index 2623bee5fc..7c5230e82c 100644 --- a/Documentation/git-p4.txt +++ b/Documentation/git-p4.txt @@ -393,8 +393,10 @@ the path elements in the p4 repository. The example above relied on the presence of the p4 branch. Without p4 branches, the same result will occur with: ---- +git init depot +cd depot git config git-p4.branchList main:branch1 -git p4 clone --detect-branches //depot@all +git p4 clone --detect-branches //depot@all . ---- From 40d69ac3a4fd72b879bab9975b26e1b445b5f0cc Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Mon, 14 Jan 2013 19:47:03 -0500 Subject: [PATCH 09/14] git p4: allow short ref names to --branch For a clone or sync, --branch says where the newly imported branch should go, or which existing branch to sync up. It takes an argument, which is currently either something that starts with "refs/", or if not, "refs/heads/p4" is prepended. Putting it in heads seems like a bad default; these should go in remotes/p4/ in most situations. Make that the new default, and be more liberal in the form of the branch name. Signed-off-by: Pete Wyckoff Signed-off-by: Junio C Hamano --- Documentation/git-p4.txt | 7 +++++-- git-p4.py | 12 +++++++++++- t/t9806-git-p4-options.sh | 21 +++++++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/Documentation/git-p4.txt b/Documentation/git-p4.txt index 7c5230e82c..7bd5c290c4 100644 --- a/Documentation/git-p4.txt +++ b/Documentation/git-p4.txt @@ -173,8 +173,11 @@ subsequent 'sync' operations. --branch :: Import changes into given branch. If the branch starts with - 'refs/', it will be used as is, otherwise the path 'refs/heads/' - will be prepended. The default branch is 'p4/master'. + 'refs/', it will be used as is. Otherwise if it does not start + with 'p4/', that prefix is added. The branch is assumed to + name a remote tracking, but this can be modified using + '--import-local', or by giving a full ref name. The default + branch is 'master'. + This example imports a new remote "p4/proj2" into an existing git repository: diff --git a/git-p4.py b/git-p4.py index c59ad93034..2d0b58f0b4 100755 --- a/git-p4.py +++ b/git-p4.py @@ -2841,8 +2841,18 @@ def run(self, args): if not self.silent and not self.detectBranches: print "Performing incremental import into %s git branch" % self.branch + # accept multiple ref name abbreviations: + # refs/foo/bar/branch -> use it exactly + # p4/branch -> prepend refs/remotes/ or refs/heads/ + # branch -> prepend refs/remotes/p4/ or refs/heads/p4/ if not self.branch.startswith("refs/"): - self.branch = "refs/heads/" + self.branch + if self.importIntoRemotes: + prepend = "refs/remotes/" + else: + prepend = "refs/heads/" + if not self.branch.startswith("p4/"): + prepend += "p4/" + self.branch = prepend + self.branch if len(args) == 0 and self.depotPaths: if not self.silent: diff --git a/t/t9806-git-p4-options.sh b/t/t9806-git-p4-options.sh index 2ad3a3e0ca..c0d44337d2 100755 --- a/t/t9806-git-p4-options.sh +++ b/t/t9806-git-p4-options.sh @@ -51,6 +51,27 @@ test_expect_failure 'sync when branch is not called master should work' ' ) ' +test_expect_success 'sync --branch builds the full ref name correctly' ' + test_when_finished cleanup_git && + ( + cd "$git" && + git init && + + git p4 sync --branch=b1 //depot && + git rev-parse --verify refs/remotes/p4/b1 && + git p4 sync --branch=p4/b2 //depot && + git rev-parse --verify refs/remotes/p4/b2 && + + git p4 sync --import-local --branch=h1 //depot && + git rev-parse --verify refs/heads/p4/h1 && + git p4 sync --import-local --branch=p4/h2 //depot && + git rev-parse --verify refs/heads/p4/h2 && + + git p4 sync --branch=refs/stuff //depot && + git rev-parse --verify refs/stuff + ) +' + # engages --detect-branches code, which will do filename filtering so # no sync to either b1 or b2 test_expect_success 'sync when two branches but no master should noop' ' From 47497844442615b6e5cd5f9b9e1552e8ba84ce71 Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Mon, 14 Jan 2013 19:47:04 -0500 Subject: [PATCH 10/14] git p4: rearrange self.initialParent use This was set in a couple of places, both of which were very far away from its use. Move it a bit closer to importChanges(), and add some comments. Signed-off-by: Pete Wyckoff Signed-off-by: Junio C Hamano --- git-p4.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/git-p4.py b/git-p4.py index 2d0b58f0b4..2924496cd4 100755 --- a/git-p4.py +++ b/git-p4.py @@ -2683,6 +2683,7 @@ def importChanges(self, changes): files = self.extractFilesFromCommit(description) self.commit(description, files, self.branch, self.initialParent) + # only needed once, to connect to the previous commit self.initialParent = "" except IOError: print self.gitError.read() @@ -2748,7 +2749,6 @@ def importHeadRevision(self, revision): def run(self, args): self.depotPaths = [] self.changeRange = "" - self.initialParent = "" self.previousDepotPaths = [] self.hasOrigin = False @@ -2836,8 +2836,6 @@ def run(self, args): if p4Change > 0: self.depotPaths = sorted(self.previousDepotPaths) self.changeRange = "@%s,#head" % p4Change - if not self.detectBranches: - self.initialParent = parseRevision(self.branch) if not self.silent and not self.detectBranches: print "Performing incremental import into %s git branch" % self.branch @@ -2982,6 +2980,14 @@ def run(self, args): self.updatedBranches = set() + if not self.detectBranches: + if args: + # start a new branch + self.initialParent = "" + else: + # build on a previous revision + self.initialParent = parseRevision(self.branch) + self.importChanges(changes) if not self.silent: From 5a8e84cde3711076d3ad7260daa0a24ee40c8e07 Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Mon, 14 Jan 2013 19:47:05 -0500 Subject: [PATCH 11/14] git p4: fail gracefully on sync with no master branch If --branch was used to build a repository with no refs/remotes/p4/master, future syncs will not know which branch to sync. Notice this situation and print a helpful error message. Signed-off-by: Pete Wyckoff Signed-off-by: Junio C Hamano --- git-p4.py | 29 +++++++++++++++++++++++++++-- t/t9806-git-p4-options.sh | 9 ++++----- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/git-p4.py b/git-p4.py index 2924496cd4..7b71ceb288 100755 --- a/git-p4.py +++ b/git-p4.py @@ -579,6 +579,17 @@ def p4BranchesInGit(branchesAreInRemotes=True): return branches +def branch_exists(branch): + """Make sure that the given ref name really exists.""" + + cmd = [ "git", "rev-parse", "--symbolic", "--verify", branch ] + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, _ = p.communicate() + if p.returncode: + return False + # expect exactly one line of output: the branch name + return out.rstrip() == branch + def findUpstreamBranchPoint(head = "HEAD"): branches = p4BranchesInGit() # map from depot-path to branch name @@ -2768,6 +2779,7 @@ def run(self, args): print 'Syncing with origin first, using "git fetch origin"' system("git fetch origin") + branch_arg_given = bool(self.branch) if len(self.branch) == 0: self.branch = self.refPrefix + "master" if gitBranchExists("refs/heads/p4") and self.importIntoRemotes: @@ -2961,8 +2973,21 @@ def run(self, args): else: # catch "git p4 sync" with no new branches, in a repo that # does not have any existing p4 branches - if len(args) == 0 and not self.p4BranchesInGit: - die("No remote p4 branches. Perhaps you never did \"git p4 clone\" in here."); + if len(args) == 0: + if not self.p4BranchesInGit: + die("No remote p4 branches. Perhaps you never did \"git p4 clone\" in here.") + + # The default branch is master, unless --branch is used to + # specify something else. Make sure it exists, or complain + # nicely about how to use --branch. + if not self.detectBranches: + if not branch_exists(self.branch): + if branch_arg_given: + die("Error: branch %s does not exist." % self.branch) + else: + die("Error: no branch %s; perhaps specify one with --branch." % + self.branch) + if self.verbose: print "Getting p4 changes for %s...%s" % (', '.join(self.depotPaths), self.changeRange) diff --git a/t/t9806-git-p4-options.sh b/t/t9806-git-p4-options.sh index c0d44337d2..a51f1221ed 100755 --- a/t/t9806-git-p4-options.sh +++ b/t/t9806-git-p4-options.sh @@ -40,14 +40,13 @@ test_expect_success 'clone --branch should checkout master' ' ) ' -test_expect_failure 'sync when branch is not called master should work' ' - git p4 clone --branch=refs/remotes/p4/sb --dest="$git" //depot@2 && +test_expect_success 'sync when no master branch prints a nice error' ' test_when_finished cleanup_git && + git p4 clone --branch=refs/remotes/p4/sb --dest="$git" //depot@2 && ( cd "$git" && - git p4 sync && - git show -s --format=%s refs/remotes/p4/sb >show && - grep "change 3" show + test_must_fail git p4 sync 2>err && + grep "Error: no branch refs/remotes/p4/master" err ) ' From 8c9e8b6e756cce90797e19dc64e3b51426546cf3 Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Mon, 14 Jan 2013 19:47:06 -0500 Subject: [PATCH 12/14] git p4: fix sync --branch when no master branch It is legal to sync a branch with a different name than refs/remotes/p4/master, and to do so even when master does not exist. Signed-off-by: Pete Wyckoff Signed-off-by: Junio C Hamano --- Documentation/git-p4.txt | 5 +++++ git-p4.py | 14 +++++++++++--- t/t9806-git-p4-options.sh | 8 ++++---- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/Documentation/git-p4.txt b/Documentation/git-p4.txt index 7bd5c290c4..e79d0467c4 100644 --- a/Documentation/git-p4.txt +++ b/Documentation/git-p4.txt @@ -112,6 +112,11 @@ will be fetched and consulted first during a 'git p4 sync'. Since importing directly from p4 is considerably slower than pulling changes from a git remote, this can be useful in a multi-developer environment. +If there are multiple branches, doing 'git p4 sync' will automatically +use the "BRANCH DETECTION" algorithm to try to partition new changes +into the right branch. This can be overridden with the '--branch' +option to specify just a single branch to update. + Rebase ~~~~~~ diff --git a/git-p4.py b/git-p4.py index 7b71ceb288..9ea1905dbe 100755 --- a/git-p4.py +++ b/git-p4.py @@ -2804,14 +2804,22 @@ def run(self, args): # branches holds mapping from branch name to sha1 branches = p4BranchesInGit(self.importIntoRemotes) - self.p4BranchesInGit = branches.keys() - for branch in branches.keys(): - self.initialParents[self.refPrefix + branch] = branches[branch] + + # restrict to just this one, disabling detect-branches + if branch_arg_given: + short = self.branch.split("/")[-1] + if short in branches: + self.p4BranchesInGit = [ short ] + else: + self.p4BranchesInGit = branches.keys() if len(self.p4BranchesInGit) > 1: if not self.silent: print "Importing from/into multiple branches" self.detectBranches = True + for branch in branches.keys(): + self.initialParents[self.refPrefix + branch] = \ + branches[branch] if self.verbose: print "branches: %s" % self.p4BranchesInGit diff --git a/t/t9806-git-p4-options.sh b/t/t9806-git-p4-options.sh index a51f1221ed..3b0000fab2 100755 --- a/t/t9806-git-p4-options.sh +++ b/t/t9806-git-p4-options.sh @@ -88,14 +88,14 @@ test_expect_success 'sync when two branches but no master should noop' ' ) ' -test_expect_failure 'sync --branch updates specified branch' ' +test_expect_success 'sync --branch updates specific branch, no detection' ' test_when_finished cleanup_git && ( cd "$git" && git init && - git p4 sync --branch=refs/remotes/p4/b1 //depot@2 && - git p4 sync --branch=refs/remotes/p4/b2 //depot@2 && - git p4 sync --branch=refs/remotes/p4/b2 && + git p4 sync --branch=b1 //depot@2 && + git p4 sync --branch=b2 //depot@2 && + git p4 sync --branch=b2 && git show -s --format=%s refs/remotes/p4/b1 >show && grep "Initial import" show && git show -s --format=%s refs/remotes/p4/b2 >show && From af8c0092503f677afc9ea4070ebd9204133b83ab Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Mon, 14 Jan 2013 19:47:07 -0500 Subject: [PATCH 13/14] git p4 test: keep P4CLIENT changes inside subshells Tests assume that this is set to something valid. Make sure that the 'clone --use-client-spec' does not leak its changes out into the rest of the tests. Signed-off-by: Pete Wyckoff Signed-off-by: Junio C Hamano --- t/t9806-git-p4-options.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/t/t9806-git-p4-options.sh b/t/t9806-git-p4-options.sh index 3b0000fab2..8d914a5766 100755 --- a/t/t9806-git-p4-options.sh +++ b/t/t9806-git-p4-options.sh @@ -226,9 +226,11 @@ test_expect_success 'clone --use-client-spec' ' View: //depot/sub/... //client2/bus/... EOF ) && - P4CLIENT=client2 && test_when_finished cleanup_git && - git p4 clone --dest="$git" --use-client-spec //depot/... && + ( + P4CLIENT=client2 && + git p4 clone --dest="$git" --use-client-spec //depot/... + ) && ( cd "$git" && test_path_is_file bus/dir/f4 && @@ -241,6 +243,7 @@ test_expect_success 'clone --use-client-spec' ' cd "$git" && git init && git config git-p4.useClientSpec true && + P4CLIENT=client2 && git p4 sync //depot/... && git checkout -b master p4/master && test_path_is_file bus/dir/f4 && From 44e8d26cf3fbc0795fb1c44f54f458847313d35e Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Mon, 14 Jan 2013 19:47:08 -0500 Subject: [PATCH 14/14] git p4: fix submit when no master branch It finds its upstream and applies the commit properly, but the sync step will fail unless it is told which branch to work on. Signed-off-by: Pete Wyckoff Signed-off-by: Junio C Hamano --- Documentation/git-p4.txt | 5 +++++ git-p4.py | 6 +++++- t/t9806-git-p4-options.sh | 25 +++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/Documentation/git-p4.txt b/Documentation/git-p4.txt index e79d0467c4..f70ef9ded2 100644 --- a/Documentation/git-p4.txt +++ b/Documentation/git-p4.txt @@ -294,6 +294,11 @@ These options can be used to modify 'git p4 submit' behavior. to bypass the prompt, causing conflicting commits to be automatically skipped, or to quit trying to apply commits, without prompting. +--branch :: + After submitting, sync this named branch instead of the default + p4/master. See the "Sync options" section above for more + information. + Rebase options ~~~~~~~~~~~~~~ These options can be used to modify 'git p4 rebase' behavior. diff --git a/git-p4.py b/git-p4.py index 9ea1905dbe..253ad06cc2 100755 --- a/git-p4.py +++ b/git-p4.py @@ -921,7 +921,8 @@ def __init__(self): optparse.make_option("--dry-run", "-n", dest="dry_run", action="store_true"), optparse.make_option("--prepare-p4-only", dest="prepare_p4_only", action="store_true"), optparse.make_option("--conflict", dest="conflict_behavior", - choices=self.conflict_behavior_choices) + choices=self.conflict_behavior_choices), + optparse.make_option("--branch", dest="branch"), ] self.description = "Submit changes from git to the perforce depot." self.usage += " [name of git branch to submit into perforce depot]" @@ -934,6 +935,7 @@ def __init__(self): self.isWindows = (platform.system() == "Windows") self.exportLabels = False self.p4HasMoveCommand = p4_has_move_command() + self.branch = None def check(self): if len(p4CmdList("opened ...")) > 0: @@ -1670,6 +1672,8 @@ def run(self, args): print "All commits applied!" sync = P4Sync() + if self.branch: + sync.branch = self.branch sync.run([]) rebase = P4Rebase() diff --git a/t/t9806-git-p4-options.sh b/t/t9806-git-p4-options.sh index 8d914a5766..4f077eeca8 100755 --- a/t/t9806-git-p4-options.sh +++ b/t/t9806-git-p4-options.sh @@ -251,6 +251,31 @@ test_expect_success 'clone --use-client-spec' ' ) ' +test_expect_success 'submit works with no p4/master' ' + test_when_finished cleanup_git && + git p4 clone --branch=b1 //depot@1,2 --destination="$git" && + ( + cd "$git" && + test_commit submit-1-branch && + git config git-p4.skipSubmitEdit true && + git p4 submit --branch=b1 + ) +' + +# The sync/rebase part post-submit will engage detect-branches +# machinery which will not do anything in this particular test. +test_expect_success 'submit works with two branches' ' + test_when_finished cleanup_git && + git p4 clone --branch=b1 //depot@1,2 --destination="$git" && + ( + cd "$git" && + git p4 sync --branch=b2 //depot@1,3 && + test_commit submit-2-branches && + git config git-p4.skipSubmitEdit true && + git p4 submit + ) +' + test_expect_success 'kill p4d' ' kill_p4d '