verbose = False
+
+def p4_build_cmd(cmd):
+ """Build a suitable p4 command line.
+
+ This consolidates building and returning a p4 command line into one
+ location. It means that hooking into the environment, or other configuration
+ can be done more easily.
+ """
+ real_cmd = "%s " % "p4"
+
+ user = gitConfig("git-p4.user")
+ if len(user) > 0:
+ real_cmd += "-u %s " % user
+
+ password = gitConfig("git-p4.password")
+ if len(password) > 0:
+ real_cmd += "-P %s " % password
+
+ port = gitConfig("git-p4.port")
+ if len(port) > 0:
+ real_cmd += "-p %s " % port
+
+ host = gitConfig("git-p4.host")
+ if len(host) > 0:
+ real_cmd += "-h %s " % host
+
+ client = gitConfig("git-p4.client")
+ if len(client) > 0:
+ real_cmd += "-c %s " % client
+
+ real_cmd += "%s" % (cmd)
+ if verbose:
+ print real_cmd
+ return real_cmd
+
+def chdir(dir):
+ if os.name == 'nt':
+ os.environ['PWD']=dir
+ os.chdir(dir)
+
def die(msg):
if verbose:
raise Exception(msg)
return val
+def p4_write_pipe(c, str):
+ real_cmd = p4_build_cmd(c)
+ return write_pipe(c, str)
+
def read_pipe(c, ignore_error=False):
if verbose:
sys.stderr.write('Reading pipe: %s\n' % c)
return val
+def p4_read_pipe(c, ignore_error=False):
+ real_cmd = p4_build_cmd(c)
+ return read_pipe(real_cmd, ignore_error)
def read_pipe_lines(c):
if verbose:
return val
+def p4_read_pipe_lines(c):
+ """Specifically invoke p4 on the command supplied. """
+ real_cmd = p4_build_cmd(c)
+ return read_pipe_lines(real_cmd)
+
def system(cmd):
if verbose:
sys.stderr.write("executing %s\n" % cmd)
if os.system(cmd) != 0:
die("command failed: %s" % cmd)
+def p4_system(cmd):
+ """Specifically invoke p4 as the system command. """
+ real_cmd = p4_build_cmd(cmd)
+ return system(real_cmd)
+
def isP4Exec(kind):
"""Determine if a Perforce 'kind' should have execute permission
if p4Type[-1] == "+":
p4Type = p4Type[0:-1]
- system("p4 reopen -t %s %s" % (p4Type, file))
+ p4_system("reopen -t %s %s" % (p4Type, file))
def getP4OpenedType(file):
# Returns the perforce file type for the given file.
- result = read_pipe("p4 opened %s" % file)
- match = re.match(".*\((.+)\)$", result)
+ result = p4_read_pipe("opened %s" % file)
+ match = re.match(".*\((.+)\)\r?$", result)
if match:
return match.group(1)
else:
- die("Could not determine file type for %s" % file)
+ die("Could not determine file type for %s (result: '%s')" % (file, result))
def diffTreePattern():
# This is a simple generator for the diff tree regex pattern. This could be
return isModeExec(src_mode) != isModeExec(dst_mode)
def p4CmdList(cmd, stdin=None, stdin_mode='w+b'):
- cmd = "p4 -G %s" % cmd
+ cmd = p4_build_cmd("-G %s" % (cmd))
if verbose:
sys.stderr.write("Opening pipe: %s\n" % cmd)
def p4ChangesForPaths(depotPaths, changeRange):
assert depotPaths
- output = read_pipe_lines("p4 changes " + ' '.join (["%s...%s" % (p, changeRange)
+ output = p4_read_pipe_lines("changes " + ' '.join (["%s...%s" % (p, changeRange)
for p in depotPaths]))
changes = []
# remove lines in the Files section that show changes to files outside the depot path we're committing into
template = ""
inFilesSection = False
- for line in read_pipe_lines("p4 change -o"):
+ for line in p4_read_pipe_lines("change -o"):
+ if line.endswith("\r\n"):
+ line = line[:-2] + "\n"
if inFilesSection:
if line.startswith("\t"):
# path starts and ends with a tab
modifier = diff['status']
path = diff['src']
if modifier == "M":
- system("p4 edit \"%s\"" % path)
+ p4_system("edit \"%s\"" % path)
if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
filesToChangeExecBit[path] = diff['dst_mode']
editedFiles.add(path)
filesToAdd.remove(path)
elif modifier == "R":
src, dest = diff['src'], diff['dst']
- system("p4 integrate -Dt \"%s\" \"%s\"" % (src, dest))
- system("p4 edit \"%s\"" % (dest))
+ p4_system("integrate -Dt \"%s\" \"%s\"" % (src, dest))
+ p4_system("edit \"%s\"" % (dest))
if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
filesToChangeExecBit[dest] = diff['dst_mode']
os.unlink(dest)
if response == "s":
print "Skipping! Good luck with the next patches..."
for f in editedFiles:
- system("p4 revert \"%s\"" % f);
+ p4_system("revert \"%s\"" % f);
for f in filesToAdd:
system("rm %s" %f)
return
system(applyPatchCmd)
for f in filesToAdd:
- system("p4 add \"%s\"" % f)
+ p4_system("add \"%s\"" % f)
for f in filesToDelete:
- system("p4 revert \"%s\"" % f)
- system("p4 delete \"%s\"" % f)
+ p4_system("revert \"%s\"" % f)
+ p4_system("delete \"%s\"" % f)
# Set/clear executable bits
for f in filesToChangeExecBit.keys():
setP4ExecBit(f, mode)
logMessage = extractLogMessageFromGitCommit(id)
- if self.isWindows:
- logMessage = logMessage.replace("\n", "\r\n")
logMessage = logMessage.strip()
template = self.prepareSubmitTemplate()
if self.interactive:
submitTemplate = self.prepareLogMessage(template, logMessage)
- diff = read_pipe("p4 diff -du ...")
+ if os.environ.has_key("P4DIFF"):
+ del(os.environ["P4DIFF"])
+ diff = p4_read_pipe("diff -du ...")
+ newdiff = ""
for newFile in filesToAdd:
- diff += "==== new file ====\n"
- diff += "--- /dev/null\n"
- diff += "+++ %s\n" % newFile
+ newdiff += "==== new file ====\n"
+ newdiff += "--- /dev/null\n"
+ newdiff += "+++ %s\n" % newFile
f = open(newFile, "r")
for line in f.readlines():
- diff += "+" + line
+ newdiff += "+" + line
f.close()
- separatorLine = "######## everything below this line is just the diff #######"
- if platform.system() == "Windows":
- separatorLine += "\r"
- separatorLine += "\n"
+ separatorLine = "######## everything below this line is just the diff #######\n"
[handle, fileName] = tempfile.mkstemp()
tmpFile = os.fdopen(handle, "w+")
- tmpFile.write(submitTemplate + separatorLine + diff)
+ if self.isWindows:
+ submitTemplate = submitTemplate.replace("\n", "\r\n")
+ separatorLine = separatorLine.replace("\n", "\r\n")
+ newdiff = newdiff.replace("\n", "\r\n")
+ tmpFile.write(submitTemplate + separatorLine + diff + newdiff)
tmpFile.close()
defaultEditor = "vi"
if platform.system() == "Windows":
defaultEditor = "notepad"
- editor = os.environ.get("EDITOR", defaultEditor);
+ if os.environ.has_key("P4EDITOR"):
+ editor = os.environ.get("P4EDITOR")
+ else:
+ editor = os.environ.get("EDITOR", defaultEditor);
system(editor + " " + fileName)
tmpFile = open(fileName, "rb")
message = tmpFile.read()
if self.isWindows:
submitTemplate = submitTemplate.replace("\r\n", "\n")
- write_pipe("p4 submit -i", submitTemplate)
+ p4_write_pipe("submit -i", submitTemplate)
else:
fileName = "submit.txt"
file = open(fileName, "w+")
else:
return False
+ allowSubmit = gitConfig("git-p4.allowSubmit")
+ if len(allowSubmit) > 0 and not self.master in allowSubmit.split(","):
+ die("%s is not in git-p4.allowSubmit" % self.master)
+
[upstream, settings] = findUpstreamBranchPoint()
self.depotPath = settings['depot-paths'][0]
if len(self.origin) == 0:
print "Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath)
self.oldWorkingDirectory = os.getcwd()
- os.chdir(self.clientPath)
+ chdir(self.clientPath)
print "Syncronizing p4 checkout..."
- system("p4 sync ...")
+ p4_system("sync ...")
self.check()
if len(commits) == 0:
print "All changes applied!"
- os.chdir(self.oldWorkingDirectory)
+ chdir(self.oldWorkingDirectory)
sync = P4Sync()
sync.run([])
help="Import into refs/heads/ , not refs/remotes"),
optparse.make_option("--max-changes", dest="maxChanges"),
optparse.make_option("--keep-path", dest="keepRepoPath", action='store_true',
- help="Keep entire BRANCH/DIR/SUBDIR prefix during import")
+ help="Keep entire BRANCH/DIR/SUBDIR prefix during import"),
+ optparse.make_option("--use-client-spec", dest="useClientSpec", action='store_true',
+ help="Only sync files that are included in the Perforce Client Spec")
]
self.description = """Imports from Perforce into a git repository.\n
example:
self.depotPaths = None
self.p4BranchesInGit = []
self.cloneExclude = []
+ self.useClientSpec = False
+ self.clientSpecDirs = []
if gitConfig("git-p4.syncFromOrigin") == "false":
self.syncWithOrigin = False
## Should move this out, doesn't use SELF.
def readP4Files(self, files):
- files = [f for f in files
- if f['action'] != 'delete']
+ filesForCommit = []
+ filesToRead = []
- if not files:
- return
+ for f in files:
+ includeFile = True
+ for val in self.clientSpecDirs:
+ if f['path'].startswith(val[0]):
+ if val[1] <= 0:
+ includeFile = False
+ break
+
+ if includeFile:
+ filesForCommit.append(f)
+ if f['action'] != 'delete':
+ filesToRead.append(f)
- 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']));
+ filedata = []
+ if len(filesToRead) > 0:
+ filedata = p4CmdList('-x - print',
+ stdin='\n'.join(['%s#%s' % (f['path'], f['rev'])
+ for f in filesToRead]),
+ stdin_mode='w+')
+
+ if "p4ExitCode" in filedata[0]:
+ die("Problems executing p4. Error: [%d]."
+ % (filedata[0]['p4ExitCode']));
j = 0;
contents = {}
while j < len(filedata):
stat = filedata[j]
j += 1
- text = ''
+ text = [];
while j < len(filedata) and filedata[j]['code'] in ('text', 'unicode', 'binary'):
- tmp = filedata[j]['data']
- if stat['type'] in ('text+ko', 'unicode+ko', 'binary+ko'):
- tmp = re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', tmp)
- elif stat['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'):
- tmp = re.sub(r'(?i)\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$',r'$\1$', tmp)
- text += tmp
+ text.append(filedata[j]['data'])
j += 1
-
+ text = ''.join(text)
if not stat.has_key('depotFile'):
sys.stderr.write("p4 print fails with: %s\n" % repr(stat))
continue
+ if stat['type'] in ('text+ko', 'unicode+ko', 'binary+ko'):
+ text = re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text)
+ elif stat['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'):
+ text = re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$',r'$\1$', text)
+
contents[stat['depotFile']] = text
- for f in files:
- assert not f.has_key('data')
- f['data'] = contents[f['path']]
+ for f in filesForCommit:
+ path = f['path']
+ if contents.has_key(path):
+ f['data'] = contents[path]
+
+ return filesForCommit
def commit(self, details, files, branch, branchPrefixes, parent = ""):
epoch = details["time"]
new_files.append (f)
else:
sys.stderr.write("Ignoring file outside of prefix: %s\n" % path)
- files = new_files
- self.readP4Files(files)
-
-
-
+ files = self.readP4Files(new_files)
self.gitStream.write("commit %s\n" % branch)
# gitStream.write("mark :%s\n" % details["change"])
print self.gitError.read()
+ def getClientSpec(self):
+ specList = p4CmdList( "client -o" )
+ temp = {}
+ for entry in specList:
+ for k,v in entry.iteritems():
+ if k.startswith("View"):
+ if v.startswith('"'):
+ start = 1
+ else:
+ start = 0
+ index = v.find("...")
+ v = v[start:index]
+ if v.startswith("-"):
+ v = v[1:]
+ temp[v] = -len(v)
+ else:
+ temp[v] = len(v)
+ self.clientSpecDirs = temp.items()
+ self.clientSpecDirs.sort( lambda x, y: abs( y[1] ) - abs( x[1] ) )
+
def run(self, args):
self.depotPaths = []
self.changeRange = ""
if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes and gitBranchExists(self.branch):
system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch))
+ if self.useClientSpec or gitConfig("git-p4.useclientspec") == "true":
+ self.getClientSpec()
+
# TODO: should always look at previous commits,
# merge with previous imports, if possible.
if args == []:
print "Importing from %s into %s" % (', '.join(depotPaths), self.cloneDestination)
if not os.path.exists(self.cloneDestination):
os.makedirs(self.cloneDestination)
- os.chdir(self.cloneDestination)
+ chdir(self.cloneDestination)
system("git init")
self.gitdir = os.getcwd() + "/.git"
if not P4Sync.run(self, depotPaths):
if os.path.exists(cmd.gitdir):
cdup = read_pipe("git rev-parse --show-cdup").strip()
if len(cdup) > 0:
- os.chdir(cdup);
+ chdir(cdup);
if not isValidGitDir(cmd.gitdir):
if isValidGitDir(cmd.gitdir + "/.git"):