if os.system(cmd) != 0:
die("command failed: %s" % cmd)
-def p4CmdList(cmd):
+def p4CmdList(cmd, stdin=None, stdin_mode='w+b'):
cmd = "p4 -G %s" % cmd
if verbose:
sys.stderr.write("Opening pipe: %s\n" % cmd)
- pipe = os.popen(cmd, "rb")
+
+ # Use a temporary file to avoid deadlocks without
+ # subprocess.communicate(), which would put another copy
+ # of stdout into memory.
+ stdin_file = None
+ if stdin is not None:
+ stdin_file = tempfile.TemporaryFile(prefix='p4-stdin', mode=stdin_mode)
+ stdin_file.write(stdin)
+ stdin_file.flush()
+ stdin_file.seek(0)
+
+ p4 = subprocess.Popen(cmd, shell=True,
+ stdin=stdin_file,
+ stdout=subprocess.PIPE)
result = []
try:
while True:
- entry = marshal.load(pipe)
+ entry = marshal.load(p4.stdout)
result.append(entry)
except EOFError:
pass
- exitCode = pipe.close()
- if exitCode != None:
+ exitCode = p4.wait()
+ if exitCode != 0:
entry = {}
entry["p4ExitCode"] = exitCode
result.append(entry)
def gitConfig(key):
return read_pipe("git config %s" % key, ignore_error=True).strip()
+def p4BranchesInGit(branchesAreInRemotes = True):
+ branches = {}
+
+ cmdline = "git rev-parse --symbolic "
+ if branchesAreInRemotes:
+ cmdline += " --remotes"
+ else:
+ 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":
+ continue
+ branch = line
+
+ # strip off p4
+ branch = re.sub ("^p4/", "", line)
+
+ branches[branch] = parseRevision(line)
+ return branches
+
+def findUpstreamBranchPoint(head = "HEAD"):
+ branches = p4BranchesInGit()
+ # map from depot-path to branch name
+ branchByDepotPath = {}
+ for branch in branches.keys():
+ tip = branches[branch]
+ log = extractLogMessageFromGitCommit(tip)
+ settings = extractSettingsGitLog(log)
+ if settings.has_key("depot-paths"):
+ paths = ",".join(settings["depot-paths"])
+ branchByDepotPath[paths] = "remotes/p4/" + branch
+
+ settings = None
+ parent = 0
+ while parent < 65535:
+ commit = head + "~%s" % parent
+ log = extractLogMessageFromGitCommit(commit)
+ settings = extractSettingsGitLog(log)
+ if settings.has_key("depot-paths"):
+ paths = ",".join(settings["depot-paths"])
+ if branchByDepotPath.has_key(paths):
+ return [branchByDepotPath[paths], settings]
+
+ parent = parent + 1
+
+ return ["", settings]
+
class Command:
def __init__(self):
self.usage = "usage: %prog [options]"
system(applyPatchCmd)
for f in filesToAdd:
- system("p4 add %s" % f)
+ system("p4 add \"%s\"" % f)
for f in filesToDelete:
- system("p4 revert %s" % f)
- system("p4 delete %s" % f)
+ system("p4 revert \"%s\"" % f)
+ system("p4 delete \"%s\"" % f)
logMessage = ""
if not self.directSubmit:
else:
return False
- depotPath = ""
- parent = 0
- while parent < 65535:
- commit = "HEAD~%s" % parent
- log = extractLogMessageFromGitCommit(commit)
- settings = extractSettingsGitLog(log)
- if not settings.has_key("depot-paths"):
- parent = parent + 1
- continue
-
- depotPath = settings['depot-paths'][0]
-
- if len(self.origin) == 0:
- names = read_pipe_lines("git name-rev '--refs=refs/remotes/p4/*' '%s'" % commit)
- if len(names) > 0:
- # strip away the beginning of 'HEAD~42 refs/remotes/p4/foo'
- self.origin = names[0].strip()[len(commit) + 1:]
-
- break
+ [upstream, settings] = findUpstreamBranchPoint()
+ depotPath = settings['depot-paths'][0]
+ if len(self.origin) == 0:
+ self.origin = upstream
if self.verbose:
print "Origin branch is " + self.origin
self.isWindows = (platform.system() == "Windows")
self.keepRepoPath = False
self.depotPaths = None
+ self.p4BranchesInGit = []
if gitConfig("git-p4.syncFromOrigin") == "false":
self.syncWithOrigin = False
if branch not in branches:
branches[branch] = []
branches[branch].append(file)
+ break
return branches
if not files:
return
- filedata = p4CmdList('print %s' % ' '.join(['"%s#%s"' % (f['path'],
- f['rev'])
- for f in files]))
+ filedata = p4CmdList('-x - print',
+ stdin='\n'.join(['%s#%s' % (f['path'], f['rev'])
+ for f in files]),
+ stdin_mode='w+')
+ if "p4ExitCode" in filedata[0]:
+ die("Problems executing p4. Error: [%d]."
+ % (filedata[0]['p4ExitCode']));
j = 0;
contents = {}
self.gitStream.write("data <<EOT\n")
self.gitStream.write(details["desc"])
- self.gitStream.write("\n[git-p4: depot-paths = \"%s\": change = %s: "
- "options = %s]\n"
- % (','.join (branchPrefixes), details["change"],
- details['options']
- ))
- self.gitStream.write("EOT\n\n")
+ self.gitStream.write("\n[git-p4: depot-paths = \"%s\": change = %s"
+ % (','.join (branchPrefixes), details["change"]))
+ if len(details['options']) > 0:
+ self.gitStream.write(": options = %s" % details['options'])
+ self.gitStream.write("]\nEOT\n\n")
if len(parent) > 0:
if self.verbose:
return p
def getBranchMapping(self):
+ lostAndFoundBranches = set()
+
for info in p4CmdList("branches"):
details = p4Cmd("branch -o %s" % info["branch"])
viewIdx = 0
if source.startswith(self.depotPaths[0]) and destination.startswith(self.depotPaths[0]):
source = source[len(self.depotPaths[0]):-4]
destination = destination[len(self.depotPaths[0]):-4]
- if destination not in self.knownBranches:
- self.knownBranches[destination] = source
+
+ if destination in self.knownBranches:
+ if not self.silent:
+ print "p4 branch %s defines a mapping from %s to %s" % (info["branch"], source, destination)
+ print "but there exists another mapping from %s to %s already!" % (self.knownBranches[destination], destination)
+ continue
+
+ self.knownBranches[destination] = source
+
+ lostAndFoundBranches.discard(destination)
+
if source not in self.knownBranches:
- self.knownBranches[source] = source
+ lostAndFoundBranches.add(source)
+
+
+ for branch in lostAndFoundBranches:
+ self.knownBranches[branch] = branch
def listExistingP4GitBranches(self):
self.p4BranchesInGit = []
self.gitError = importProcess.stderr
if self.revision:
- print "Doing initial import of %s from revision %s" % (' '.join(self.depotPaths), self.revision)
+ print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), self.revision, self.branch)
details = { "user" : "git perforce import user", "time" : int(time.time()) }
details["desc"] = ("Initial import of %s from the state at revision %s"
print "No changes to import!"
return True
+ if not self.silent and not self.detectBranches:
+ print "Import destination: %s" % self.branch
+
self.updatedBranches = set()
cnt = 1
def run(self, args):
sync = P4Sync()
sync.run([])
- print "Rebasing the current branch"
+
+ [upstream, settings] = findUpstreamBranchPoint()
+ if len(upstream) == 0:
+ die("Cannot find upstream branchpoint for rebase")
+
+ # the branchpoint may be p4/foo~3, so strip off the parent
+ upstream = re.sub("~[0-9]+$", "", upstream)
+
+ print "Rebasing the current branch onto %s" % upstream
oldHead = read_pipe("git rev-parse HEAD").strip()
- system("git rebase p4")
+ system("git rebase %s" % upstream)
system("git diff-tree --stat --summary -M %s HEAD" % oldHead)
return True
self.cloneDestination = self.defaultDestination(args)
print "Importing from %s into %s" % (', '.join(depotPaths), self.cloneDestination)
- os.makedirs(self.cloneDestination)
+ if not os.path.exists(self.cloneDestination):
+ os.makedirs(self.cloneDestination)
os.chdir(self.cloneDestination)
system("git init")
self.gitdir = os.getcwd() + "/.git"
return True
+class P4Branches(Command):
+ def __init__(self):
+ Command.__init__(self)
+ self.options = [ ]
+ self.description = ("Shows the git branches that hold imports and their "
+ + "corresponding perforce depot paths")
+ self.verbose = False
+
+ def run(self, args):
+ cmdline = "git rev-parse --symbolic "
+ cmdline += " --remotes"
+
+ for line in read_pipe_lines(cmdline):
+ line = line.strip()
+
+ if not line.startswith('p4/') or line == "p4/HEAD":
+ continue
+ branch = line
+
+ log = extractLogMessageFromGitCommit("refs/remotes/%s" % branch)
+ settings = extractSettingsGitLog(log)
+
+ print "%s <= %s (%s)" % (branch, ",".join(settings["depot-paths"]), settings["change"])
+ return True
+
class HelpFormatter(optparse.IndentedHelpFormatter):
def __init__(self):
optparse.IndentedHelpFormatter.__init__(self)
"sync" : P4Sync,
"rebase" : P4Rebase,
"clone" : P4Clone,
- "rollback" : P4RollBack
+ "rollback" : P4RollBack,
+ "branches" : P4Branches
}