index bcea4cf3deac1c5eeb21c5468b574a043611cb27..e8a5c1fa316f2ac5be4fa32b145d233db31252a5 100755 (executable)
# 2007 Trolltech ASA
# License: MIT <http://www.opensource.org/licenses/mit-license.php>
#
# 2007 Trolltech ASA
# License: MIT <http://www.opensource.org/licenses/mit-license.php>
#
+# TODO: * Consider making --with-origin the default, assuming that the git
+# protocol is always more efficient. (needs manual testing first :)
+#
import optparse, sys, os, marshal, popen2, subprocess, shelve
import tempfile, getopt, sha, os.path, time, platform
import optparse, sys, os, marshal, popen2, subprocess, shelve
import tempfile, getopt, sha, os.path, time, platform
result.append(entry)
except EOFError:
pass
result.append(entry)
except EOFError:
pass
- pipe.close()
+ exitCode = pipe.close()
+ if exitCode != None:
+ entry = {}
+ entry["p4ExitCode"] = exitCode
+ result.append(entry)
return result
return result
if not depotPath.endswith("/"):
depotPath += "/"
output = p4Cmd("where %s..." % depotPath)
if not depotPath.endswith("/"):
depotPath += "/"
output = p4Cmd("where %s..." % depotPath)
+ if output["code"] == "error":
+ return ""
clientPath = ""
if "path" in output:
clientPath = output.get("path")
clientPath = ""
if "path" in output:
clientPath = output.get("path")
print output
return True
print output
return True
+class P4RollBack(Command):
+ def __init__(self):
+ Command.__init__(self)
+ self.options = [
+ optparse.make_option("--verbose", dest="verbose", action="store_true"),
+ optparse.make_option("--local", dest="rollbackLocalBranches", action="store_true")
+ ]
+ self.description = "A tool to debug the multi-branch import. Don't use :)"
+ self.verbose = False
+ self.rollbackLocalBranches = False
+
+ def run(self, args):
+ if len(args) != 1:
+ return False
+ maxChange = int(args[0])
+
+ if "p4ExitCode" in p4Cmd("changes -m 1"):
+ die("Problems executing p4");
+
+ if self.rollbackLocalBranches:
+ refPrefix = "refs/heads/"
+ lines = mypopen("git rev-parse --symbolic --branches").readlines()
+ else:
+ refPrefix = "refs/remotes/"
+ lines = mypopen("git rev-parse --symbolic --remotes").readlines()
+
+ for line in lines:
+ if self.rollbackLocalBranches or (line.startswith("p4/") and line != "p4/HEAD\n"):
+ ref = refPrefix + line[:-1]
+ log = extractLogMessageFromGitCommit(ref)
+ depotPath, change = extractDepotPathAndChangeFromGitLog(log)
+ changed = False
+
+ if len(p4Cmd("changes -m 1 %s...@%s" % (depotPath, maxChange))) == 0:
+ print "Branch %s did not exist at change %s, deleting." % (ref, maxChange)
+ system("git update-ref -d %s `git rev-parse %s`" % (ref, ref))
+ continue
+
+ while len(change) > 0 and int(change) > maxChange:
+ changed = True
+ if self.verbose:
+ print "%s is at %s ; rewinding towards %s" % (ref, change, maxChange)
+ system("git update-ref %s \"%s^\"" % (ref, ref))
+ log = extractLogMessageFromGitCommit(ref)
+ depotPath, change = extractDepotPathAndChangeFromGitLog(log)
+
+ if changed:
+ print "%s rewound to %s" % (ref, change)
+
+ return True
+
class P4Submit(Command):
def __init__(self):
Command.__init__(self)
class P4Submit(Command):
def __init__(self):
Command.__init__(self)
print submitTemplate
raw_input("Press return to continue...")
else:
print submitTemplate
raw_input("Press return to continue...")
else:
- pipe = os.popen("p4 submit -i", "wb")
- pipe.write(submitTemplate)
- pipe.close()
+ if self.directSubmit:
+ print "Submitting to git first"
+ os.chdir(self.oldWorkingDirectory)
+ pipe = os.popen("git commit -a -F -", "wb")
+ pipe.write(submitTemplate)
+ pipe.close()
+ os.chdir(self.clientPath)
+
+ pipe = os.popen("p4 submit -i", "wb")
+ pipe.write(submitTemplate)
+ pipe.close()
elif response == "s":
for f in editedFiles:
system("p4 revert \"%s\"" % f);
elif response == "s":
for f in editedFiles:
system("p4 revert \"%s\"" % f);
if len(args) == 0:
self.master = currentGitBranch()
if len(args) == 0:
self.master = currentGitBranch()
- if len(self.master) == 0 or not os.path.exists("%s/refs/heads/%s" % (gitdir, self.master)):
+ if len(self.master) == 0 or not gitBranchExists("refs/heads/%s" % self.master):
die("Detecting current git branch failed!")
elif len(args) == 1:
self.master = args[0]
die("Detecting current git branch failed!")
elif len(args) == 1:
self.master = args[0]
sys.exit(128)
print "Perforce checkout for depot path %s located at %s" % (depotPath, self.clientPath)
sys.exit(128)
print "Perforce checkout for depot path %s located at %s" % (depotPath, self.clientPath)
- oldWorkingDirectory = os.getcwd()
+ self.oldWorkingDirectory = os.getcwd()
if self.directSubmit:
self.diffStatus = mypopen("git diff -r --name-status HEAD").readlines()
if self.directSubmit:
self.diffStatus = mypopen("git diff -r --name-status HEAD").readlines()
+ if len(self.diffStatus) == 0:
+ print "No changes in working directory to submit."
+ return True
patch = mypopen("git diff -p --binary --diff-filter=ACMRTUXB HEAD").read()
self.diffFile = gitdir + "/p4-git-diff"
f = open(self.diffFile, "wb")
patch = mypopen("git diff -p --binary --diff-filter=ACMRTUXB HEAD").read()
self.diffFile = gitdir + "/p4-git-diff"
f = open(self.diffFile, "wb")
print "No changes found to apply between %s and current HEAD" % self.origin
else:
print "All changes applied!"
print "No changes found to apply between %s and current HEAD" % self.origin
else:
print "All changes applied!"
- response = ""
- os.chdir(oldWorkingDirectory)
-
- if self.directSubmit:
- response = raw_input("Do you want to DISCARD your git WORKING DIRECTORY CHANGES and sync from Perforce now using git-p4 rebase? [y]es/[n]o ")
- if response == "y" or response == "yes":
- system("git reset --hard")
-
- if len(response) == 0:
- response = raw_input("Do you want to sync from Perforce now using git-p4 rebase? [y]es/[n]o ")
+ os.chdir(self.oldWorkingDirectory)
+ response = raw_input("Do you want to sync from Perforce now using git-p4 rebase? [y]es/[n]o ")
if response == "y" or response == "yes":
rebase = P4Rebase()
rebase.run([])
if response == "y" or response == "yes":
rebase = P4Rebase()
rebase.run([])
optparse.make_option("--silent", dest="silent", action="store_true"),
optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"),
optparse.make_option("--with-origin", dest="syncWithOrigin", action="store_true"),
optparse.make_option("--silent", dest="silent", action="store_true"),
optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"),
optparse.make_option("--with-origin", dest="syncWithOrigin", action="store_true"),
- optparse.make_option("--verbose", dest="verbose", action="store_true")
+ optparse.make_option("--verbose", dest="verbose", action="store_true"),
+ optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false"),
+ optparse.make_option("--max-changes", dest="maxChanges")
]
self.description = """Imports from Perforce into a git repository.\n
example:
]
self.description = """Imports from Perforce into a git repository.\n
example:
self.changesFile = ""
self.syncWithOrigin = False
self.verbose = False
self.changesFile = ""
self.syncWithOrigin = False
self.verbose = False
+ self.importIntoRemotes = True
+ self.maxChanges = ""
+ self.isWindows = (platform.system() == "Windows")
def p4File(self, depotPath):
return os.popen("p4 print -q \"%s\"" % depotPath, "rb").read()
def p4File(self, depotPath):
return os.popen("p4 print -q \"%s\"" % depotPath, "rb").read()
relPath = path[len(self.depotPath):]
for branch in self.knownBranches.keys():
relPath = path[len(self.depotPath):]
for branch in self.knownBranches.keys():
- if relPath.startswith(branch):
+ if relPath.startswith(branch + "/"): # add a trailing slash so that a commit into qt/4.2foo doesn't end up in qt/4.2
if branch not in branches:
branches[branch] = []
branches[branch].append(file)
if branch not in branches:
branches[branch] = []
branches[branch].append(file)
data = self.p4File(depotPath)
data = self.p4File(depotPath)
+ if self.isWindows and file["type"].endswith("text"):
+ data = data.replace("\r\n", "\n")
+
self.gitStream.write("M %s inline %s\n" % (mode, relPath))
self.gitStream.write("data %s\n" % len(data))
self.gitStream.write(data)
self.gitStream.write("M %s inline %s\n" % (mode, relPath))
self.gitStream.write("data %s\n" % len(data))
self.gitStream.write(data)
print "Tag %s does not match with change %s: file count is different." % (labelDetails["label"], change)
def getUserMapFromPerforceServer(self):
print "Tag %s does not match with change %s: file count is different." % (labelDetails["label"], change)
def getUserMapFromPerforceServer(self):
+ if self.userMapFromPerforceServer:
+ return
self.users = {}
for output in p4CmdList("users"):
self.users = {}
for output in p4CmdList("users"):
for user in self.users.keys():
cache.write("%s\t%s\n" % (user, self.users[user]))
cache.close();
for user in self.users.keys():
cache.write("%s\t%s\n" % (user, self.users[user]))
cache.close();
+ self.userMapFromPerforceServer = True
def loadUserMapFromCache(self):
self.users = {}
def loadUserMapFromCache(self):
self.users = {}
+ self.userMapFromPerforceServer = False
try:
cache = open(gitdir + "/p4-usercache.txt", "rb")
lines = cache.readlines()
try:
cache = open(gitdir + "/p4-usercache.txt", "rb")
lines = cache.readlines()
def listExistingP4GitBranches(self):
self.p4BranchesInGit = []
def listExistingP4GitBranches(self):
self.p4BranchesInGit = []
- for line in mypopen("git rev-parse --symbolic --remotes").readlines():
- if line.startswith("p4/") and line != "p4/HEAD\n":
+ cmdline = "git rev-parse --symbolic "
+ if self.importIntoRemotes:
+ cmdline += " --remotes"
+ else:
+ cmdline += " --branches"
+
+ for line in mypopen(cmdline).readlines():
+ if self.importIntoRemotes and ((not line.startswith("p4/")) or line == "p4/HEAD\n"):
+ continue
+ if self.importIntoRemotes:
+ # strip off p4
branch = line[3:-1]
branch = line[3:-1]
- self.p4BranchesInGit.append(branch)
- self.initialParents["refs/remotes/p4/" + branch] = parseRevision(line[:-1])
+ else:
+ branch = line[:-1]
+ self.p4BranchesInGit.append(branch)
+ self.initialParents[self.refPrefix + branch] = parseRevision(line[:-1])
+
+ def createOrUpdateBranchesFromOrigin(self):
+ if not self.silent:
+ print "Creating/updating branch(es) in %s based on origin branch(es)" % self.refPrefix
+
+ for line in mypopen("git rev-parse --symbolic --remotes"):
+ if (not line.startswith("origin/")) or line.endswith("HEAD\n"):
+ continue
+
+ headName = line[len("origin/"):-1]
+ remoteHead = self.refPrefix + headName
+ originHead = "origin/" + headName
+
+ [originPreviousDepotPath, originP4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(originHead))
+ if len(originPreviousDepotPath) == 0 or len(originP4Change) == 0:
+ continue
+
+ update = False
+ if not gitBranchExists(remoteHead):
+ if self.verbose:
+ print "creating %s" % remoteHead
+ update = True
+ else:
+ [p4PreviousDepotPath, p4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(remoteHead))
+ if len(p4Change) > 0:
+ if originPreviousDepotPath == p4PreviousDepotPath:
+ originP4Change = int(originP4Change)
+ p4Change = int(p4Change)
+ if originP4Change > p4Change:
+ print "%s (%s) is newer than %s (%s). Updating p4 branch from origin." % (originHead, originP4Change, remoteHead, p4Change)
+ update = True
+ else:
+ print "Ignoring: %s was imported from %s while %s was imported from %s" % (originHead, originPreviousDepotPath, remoteHead, p4PreviousDepotPath)
+
+ if update:
+ system("git update-ref %s %s" % (remoteHead, originHead))
def run(self, args):
self.depotPath = ""
def run(self, args):
self.depotPath = ""
self.knownBranches = {}
self.initialParents = {}
self.knownBranches = {}
self.initialParents = {}
- self.listExistingP4GitBranches()
-
- if self.syncWithOrigin and gitBranchExists("origin") and gitBranchExists("refs/remotes/p4/master") and not self.detectBranches:
- ### needs to be ported to multi branch import
+ if self.importIntoRemotes:
+ self.refPrefix = "refs/remotes/p4/"
+ else:
+ self.refPrefix = "refs/heads/"
+ if self.syncWithOrigin:
print "Syncing with origin first as requested by calling git fetch origin"
system("git fetch origin")
print "Syncing with origin first as requested by calling git fetch origin"
system("git fetch origin")
- [originPreviousDepotPath, originP4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("origin"))
- [p4PreviousDepotPath, p4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("p4"))
- if len(originPreviousDepotPath) > 0 and len(originP4Change) > 0 and len(p4Change) > 0:
- if originPreviousDepotPath == p4PreviousDepotPath:
- originP4Change = int(originP4Change)
- p4Change = int(p4Change)
- if originP4Change > p4Change:
- print "origin (%s) is newer than p4 (%s). Updating p4 branch from origin." % (originP4Change, p4Change)
- system("git update-ref refs/remotes/p4/master origin");
- else:
- print "Cannot sync with origin. It was imported from %s while remotes/p4 was imported from %s" % (originPreviousDepotPath, p4PreviousDepotPath)
+
+ createP4HeadRef = False;
if len(self.branch) == 0:
if len(self.branch) == 0:
- self.branch = "refs/remotes/p4/master"
- if gitBranchExists("refs/heads/p4"):
+ 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");
system("git update-ref %s refs/heads/p4" % self.branch)
system("git branch -D p4");
- if not gitBranchExists("refs/remotes/p4/HEAD"):
- system("git symbolic-ref refs/remotes/p4/HEAD %s" % self.branch)
+ # create it /after/ importing, when master exists
+ if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes:
+ createP4HeadRef = True
if len(args) == 0:
if len(args) == 0:
- if not gitBranchExists(self.branch) and gitBranchExists("origin") and not self.detectBranches:
- ### needs to be ported to multi branch import
+ self.createOrUpdateBranchesFromOrigin()
+ self.listExistingP4GitBranches()
+
+ if len(self.p4BranchesInGit) > 1:
if not self.silent:
if not self.silent:
- print "Creating %s branch in git repository based on origin" % self.branch
- branch = self.branch
- if not branch.startswith("refs"):
- branch = "refs/heads/" + branch
- system("git update-ref %s origin" % branch)
+ print "Importing from/into multiple branches"
+ self.detectBranches = True
if self.verbose:
print "branches: %s" % self.p4BranchesInGit
p4Change = 0
for branch in self.p4BranchesInGit:
if self.verbose:
print "branches: %s" % self.p4BranchesInGit
p4Change = 0
for branch in self.p4BranchesInGit:
- depotPath, change = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("refs/remotes/p4/" + branch))
+ depotPath, change = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(self.refPrefix + branch))
if self.verbose:
print "path %s change %s" % (depotPath, change)
if self.verbose:
print "path %s change %s" % (depotPath, change)
self.depotPath = self.previousDepotPath
self.changeRange = "@%s,#head" % p4Change
self.initialParent = parseRevision(self.branch)
self.depotPath = self.previousDepotPath
self.changeRange = "@%s,#head" % p4Change
self.initialParent = parseRevision(self.branch)
- if not self.silent:
+ if not self.silent and not self.detectBranches:
print "Performing incremental import into %s git branch" % self.branch
if not self.branch.startswith("refs/"):
print "Performing incremental import into %s git branch" % self.branch
if not self.branch.startswith("refs/"):
changes.reverse()
changes.reverse()
+ if len(self.maxChanges) > 0:
+ changes = changes[0:min(int(self.maxChanges), len(changes))]
+
if len(changes) == 0:
if not self.silent:
if len(changes) == 0:
if not self.silent:
- print "no changes to import!"
+ print "No changes to import!"
return True
return True
+ self.updatedBranches = set()
+
cnt = 1
for change in changes:
description = p4Cmd("describe %s" % change)
if not self.silent:
cnt = 1
for change in changes:
description = p4Cmd("describe %s" % change)
if not self.silent:
- sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes)))
+ sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes)))
sys.stdout.flush()
cnt = cnt + 1
sys.stdout.flush()
cnt = cnt + 1
if self.verbose:
print "branch is %s" % branch
if self.verbose:
print "branch is %s" % branch
+ self.updatedBranches.add(branch)
+
if branch not in self.createdBranches:
self.createdBranches.add(branch)
parent = self.knownBranches[branch]
if branch not in self.createdBranches:
self.createdBranches.add(branch)
parent = self.knownBranches[branch]
elif len(parent) > 0:
parent = self.projectName + parent
elif len(parent) > 0:
parent = self.projectName + parent
- branch = "refs/remotes/p4/" + branch
+ branch = self.refPrefix + branch
if len(parent) > 0:
if len(parent) > 0:
- parent = "refs/remotes/p4/" + parent
+ parent = self.refPrefix + parent
if self.verbose:
print "looking for initial parent for %s; current parent is %s" % (branch, parent)
if self.verbose:
print "looking for initial parent for %s; current parent is %s" % (branch, parent)
print self.gitError.read()
sys.exit(1)
print self.gitError.read()
sys.exit(1)
- if not self.silent:
- print ""
+ if not self.silent:
+ print ""
+ if len(self.updatedBranches) > 0:
+ sys.stdout.write("Updated branches: ")
+ for b in self.updatedBranches:
+ sys.stdout.write("%s " % b)
+ sys.stdout.write("\n")
self.gitStream.close()
self.gitStream.close()
self.gitOutput.close()
self.gitError.close()
self.gitOutput.close()
self.gitError.close()
+ if createP4HeadRef:
+ system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch))
+
return True
class P4Rebase(Command):
return True
class P4Rebase(Command):
"submit" : P4Submit(),
"sync" : P4Sync(),
"rebase" : P4Rebase(),
"submit" : P4Submit(),
"sync" : P4Sync(),
"rebase" : P4Rebase(),
- "clone" : P4Clone()
+ "clone" : P4Clone(),
+ "rollback" : P4RollBack()
}
if len(sys.argv[1:]) == 0:
}
if len(sys.argv[1:]) == 0: