From 4f9273d27b399c9b6e0be372e607b9a8176c0699 Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Sat, 26 Jan 2013 22:11:04 -0500 Subject: git p4: temp branch name should use / even on windows Commit fed2369 (git-p4: Search for parent commit on branch creation, 2012-01-25) uses temporary branches to help find the parent of a new p4 branch. The temp branches are of the form "git-p4-tmp/%d" for some p4 change number. Mistakenly, this string was made using os.path.join() instead of just string concatenation. On windows, this turns into a backslash (\), which is not allowed in git branch names. Reported-by: Casey McGinty Signed-off-by: Pete Wyckoff Signed-off-by: Junio C Hamano --- git-p4.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'git-p4.py') diff --git a/git-p4.py b/git-p4.py index 2da564995d..fb77c56049 100755 --- a/git-p4.py +++ b/git-p4.py @@ -2687,7 +2687,7 @@ class P4Sync(Command, P4UserMap): blob = None if len(parent) > 0: - tempBranch = os.path.join(self.tempBranchLocation, "%d" % (change)) + tempBranch = "%s/%d" % (self.tempBranchLocation, change) if self.verbose: print "Creating temporary branch: " + tempBranch self.commit(description, filesForCommit, tempBranch) -- cgit v1.2.1 From f629fa597c1736a419e2bd68bbca2883c6a143cf Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Sat, 26 Jan 2013 22:11:05 -0500 Subject: git p4: remove unused imports Found by "pyflakes" checker tool. Modules shelve, getopt were unused. Module os.path is exported by os. Reformat one-per-line as is PEP008 suggested style. Signed-off-by: Pete Wyckoff Signed-off-by: Junio C Hamano --- git-p4.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'git-p4.py') diff --git a/git-p4.py b/git-p4.py index fb77c56049..47d092d2e5 100755 --- a/git-p4.py +++ b/git-p4.py @@ -7,16 +7,20 @@ # 2007 Trolltech ASA # License: MIT # - import sys if sys.hexversion < 0x02040000: # The limiter is the subprocess module sys.stderr.write("git-p4: requires Python 2.4 or later.\n") sys.exit(1) - -import optparse, os, marshal, subprocess, shelve -import tempfile, getopt, os.path, time, platform -import re, shutil +import os +import optparse +import marshal +import subprocess +import tempfile +import time +import platform +import re +import shutil verbose = False -- cgit v1.2.1 From 0f487d308d819cf6c64b866cb2f5c366a13b1639 Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Sat, 26 Jan 2013 22:11:06 -0500 Subject: git p4: generate better error message for bad depot path Depot paths must start with //. Exit with a better explanation when a bad depot path is supplied. Signed-off-by: Pete Wyckoff Signed-off-by: Junio C Hamano --- git-p4.py | 1 + 1 file changed, 1 insertion(+) (limited to 'git-p4.py') diff --git a/git-p4.py b/git-p4.py index 47d092d2e5..cbf8525594 100755 --- a/git-p4.py +++ b/git-p4.py @@ -3163,6 +3163,7 @@ class P4Clone(P4Sync): self.cloneExclude = ["/"+p for p in self.cloneExclude] for p in depotPaths: if not p.startswith("//"): + sys.stderr.write('Depot paths must start with "//": %s\n' % p) return False if not self.cloneDestination: -- cgit v1.2.1 From bb5ea62d80313f6fd37f3f3c214c78feb34036f9 Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Sat, 26 Jan 2013 22:11:12 -0500 Subject: git p4: remove unreachable windows \r\n conversion code Replacing \r\n with \n on windows was added in c1f9197 (Replace \r\n with \n when importing from p4 on Windows, 2007-05-24), to work around an oddity with "p4 print" on windows. Text files are printed with "\r\r\n" endings, regardless of whether they were created on unix or windows, and regardless of the client LineEnd setting. As of d2c6dd3 (use p4CmdList() to get file contents in Python dicts. This is more robust., 2007-05-23), git-p4 uses "p4 -G print", which generates files in a raw format. As the native line ending format if p4 is \n, there will be no \r\n in the raw text. Actually, it is possible to generate a text file so that the p4 representation includes embedded \r\n, even though this is not normal on either windows or unix. In that case the code would have mistakenly stripped them out, but now they will be left intact. More information on how p4 deals with line endings is here: http://kb.perforce.com/article/63 Signed-off-by: Pete Wyckoff Signed-off-by: Junio C Hamano --- git-p4.py | 9 --------- 1 file changed, 9 deletions(-) (limited to 'git-p4.py') diff --git a/git-p4.py b/git-p4.py index cbf8525594..445d704222 100755 --- a/git-p4.py +++ b/git-p4.py @@ -2134,15 +2134,6 @@ class P4Sync(Command, P4UserMap): print "\nIgnoring apple filetype file %s" % file['depotFile'] return - # Perhaps windows wants unicode, utf16 newlines translated too; - # but this is not doing it. - if self.isWindows and type_base == "text": - mangled = [] - for data in contents: - data = data.replace("\r\n", "\n") - mangled.append(data) - contents = mangled - # Note that we do not try to de-mangle keywords on utf16 files, # even though in theory somebody may want that. pattern = p4_keywords_regexp_for_type(type_base, type_mods) -- cgit v1.2.1 From 7f0e596276aa120059b0f2df235a8ba1cb9b2554 Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Sat, 26 Jan 2013 22:11:13 -0500 Subject: git p4: scrub crlf for utf16 files on windows Files of type utf16 are handled with "p4 print" instead of the normal "p4 -G print" interface due to how the latter does not produce correct output. See 55aa571 (git-p4: handle utf16 filetype properly, 2011-09-17) for details. On windows, though, "p4 print" can not be told which line endings to use, as there is no underlying client, and always chooses crlf, even for utf16 files. Convert the \r\n into \n when importing utf16 files. The fix for this is complex, in that the problem is a property of the NT version of p4. There are old versions of p4 that were compiled directly for cygwin that should not be subjected to text replacement. The right check here, then, is to look at the p4 version, not the OS version. Note also that on cygwin, platform.system() is "CYGWIN_NT-5.1" or similar, not "Windows". Add a function to memoize the p4 version string and use it to check for "/NT", indicating the Windows build of p4. Signed-off-by: Pete Wyckoff Signed-off-by: Junio C Hamano --- git-p4.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'git-p4.py') diff --git a/git-p4.py b/git-p4.py index 445d704222..c62b2cab82 100755 --- a/git-p4.py +++ b/git-p4.py @@ -170,6 +170,22 @@ def p4_system(cmd): expand = isinstance(real_cmd, basestring) subprocess.check_call(real_cmd, shell=expand) +_p4_version_string = None +def p4_version_string(): + """Read the version string, showing just the last line, which + hopefully is the interesting version bit. + + $ p4 -V + Perforce - The Fast Software Configuration Management System. + Copyright 1995-2011 Perforce Software. All rights reserved. + Rev. P4/NTX86/2011.1/393975 (2011/12/16). + """ + global _p4_version_string + if not _p4_version_string: + a = p4_read_pipe_lines(["-V"]) + _p4_version_string = a[-1].rstrip() + return _p4_version_string + def p4_integrate(src, dest): p4_system(["integrate", "-Dt", wildcard_encode(src), wildcard_encode(dest)]) @@ -1973,7 +1989,6 @@ class P4Sync(Command, P4UserMap): self.syncWithOrigin = True self.importIntoRemotes = True self.maxChanges = "" - self.isWindows = (platform.system() == "Windows") self.keepRepoPath = False self.depotPaths = None self.p4BranchesInGit = [] @@ -2118,7 +2133,14 @@ class P4Sync(Command, P4UserMap): # operations. utf16 is converted to ascii or utf8, perhaps. # But ascii text saved as -t utf16 is completely mangled. # Invoke print -o to get the real contents. + # + # On windows, the newlines will always be mangled by print, so put + # them back too. This is not needed to the cygwin windows version, + # just the native "NT" type. + # text = p4_read_pipe(['print', '-q', '-o', '-', file['depotFile']]) + if p4_version_string().find("/NT") >= 0: + text = text.replace("\r\n", "\n") contents = [ text ] if type_base == "apple": -- cgit v1.2.1 From d20f0f8e2804bedc2c3743b523724ba7b8d34264 Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Sat, 26 Jan 2013 22:11:19 -0500 Subject: git p4: disable read-only attribute before deleting On windows, p4 marks un-edited files as read-only. Not only are they read-only, but also they cannot be deleted. Remove the read-only attribute before deleting in both the copy and rename cases. This also happens in the RCS cleanup code, where a file is marked to be deleted, but must first be edited to remove adjust the keyword lines. Make sure it is editable before patching. Signed-off-by: Pete Wyckoff Signed-off-by: Junio C Hamano --- git-p4.py | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'git-p4.py') diff --git a/git-p4.py b/git-p4.py index c62b2cab82..a98970483b 100755 --- a/git-p4.py +++ b/git-p4.py @@ -21,6 +21,7 @@ import time import platform import re import shutil +import stat verbose = False @@ -1231,6 +1232,9 @@ class P4Submit(Command, P4UserMap): p4_edit(dest) pureRenameCopy.discard(dest) filesToChangeExecBit[dest] = diff['dst_mode'] + if self.isWindows: + # turn off read-only attribute + os.chmod(dest, stat.S_IWRITE) os.unlink(dest) editedFiles.add(dest) elif modifier == "R": @@ -1249,6 +1253,8 @@ class P4Submit(Command, P4UserMap): p4_edit(dest) # with move: already open, writable filesToChangeExecBit[dest] = diff['dst_mode'] if not self.p4HasMoveCommand: + if self.isWindows: + os.chmod(dest, stat.S_IWRITE) os.unlink(dest) filesToDelete.add(src) editedFiles.add(dest) @@ -1289,6 +1295,10 @@ class P4Submit(Command, P4UserMap): for file in kwfiles: if verbose: print "zapping %s with %s" % (line,pattern) + # File is being deleted, so not open in p4. Must + # disable the read-only bit on windows. + if self.isWindows and file not in editedFiles: + os.chmod(file, stat.S_IWRITE) self.patchRCSKeywords(file, kwfiles[file]) fixed_rcs_keywords = True -- cgit v1.2.1 From 9bf28855108d7121e7c439da07175ff4c4f33e42 Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Sat, 26 Jan 2013 22:11:20 -0500 Subject: git p4: avoid shell when mapping users The extra quoting and double-% are unneeded, just to work around the shell. Instead, avoid the shell indirection. Signed-off-by: Pete Wyckoff Signed-off-by: Junio C Hamano --- git-p4.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'git-p4.py') diff --git a/git-p4.py b/git-p4.py index a98970483b..c43d0443fb 100755 --- a/git-p4.py +++ b/git-p4.py @@ -1050,7 +1050,8 @@ class P4Submit(Command, P4UserMap): def p4UserForCommit(self,id): # Return the tuple (perforce user,git email) for a given git commit id self.getUserMapFromPerforceServer() - gitEmail = read_pipe("git log --max-count=1 --format='%%ae' %s" % id) + gitEmail = read_pipe(["git", "log", "--max-count=1", + "--format=%ae", id]) gitEmail = gitEmail.strip() if not self.emails.has_key(gitEmail): return (None,gitEmail) -- cgit v1.2.1 From c7d34884ae1d37e910ce6813c9baeb06c0912228 Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Sat, 26 Jan 2013 22:11:21 -0500 Subject: git p4: avoid shell when invoking git rev-list Invoke git rev-list directly, avoiding the shell, in P4Submit and P4Sync. The overhead of starting extra processes is significant in cygwin; this speeds things up on that platform. Signed-off-by: Pete Wyckoff Signed-off-by: Junio C Hamano --- git-p4.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'git-p4.py') diff --git a/git-p4.py b/git-p4.py index c43d0443fb..c8ae83d502 100755 --- a/git-p4.py +++ b/git-p4.py @@ -1606,7 +1606,7 @@ class P4Submit(Command, P4UserMap): self.check() commits = [] - for line in read_pipe_lines("git rev-list --no-merges %s..%s" % (self.origin, self.master)): + for line in read_pipe_lines(["git", "rev-list", "--no-merges", "%s..%s" % (self.origin, self.master)]): commits.append(line.strip()) commits.reverse() @@ -2644,7 +2644,8 @@ class P4Sync(Command, P4UserMap): def searchParent(self, parent, branch, target): parentFound = False - for blob in read_pipe_lines(["git", "rev-list", "--reverse", "--no-merges", parent]): + for blob in read_pipe_lines(["git", "rev-list", "--reverse", + "--no-merges", parent]): blob = blob.strip() if len(read_pipe(["git", "diff-tree", blob, target])) == 0: parentFound = True -- cgit v1.2.1 From 2abba3014e54920762eb81236e394af7b152f74c Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Sat, 26 Jan 2013 22:11:22 -0500 Subject: git p4: avoid shell when invoking git config --get-all Signed-off-by: Pete Wyckoff Signed-off-by: Junio C Hamano --- git-p4.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'git-p4.py') diff --git a/git-p4.py b/git-p4.py index c8ae83d502..7efa9a862e 100755 --- a/git-p4.py +++ b/git-p4.py @@ -571,7 +571,8 @@ def gitConfig(key, args = None): # set args to "--bool", for instance def gitConfigList(key): if not _gitConfig.has_key(key): - _gitConfig[key] = read_pipe("git config --get-all %s" % key, ignore_error=True).strip().split(os.linesep) + s = read_pipe(["git", "config", "--get-all", key], ignore_error=True) + _gitConfig[key] = s.strip().split(os.linesep) return _gitConfig[key] def p4BranchesInGit(branchesAreInRemotes=True): -- cgit v1.2.1 From b345d6c3b7517912f1f35f8b235f8a78892d5796 Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Sat, 26 Jan 2013 22:11:23 -0500 Subject: git p4: avoid shell when calling git config Signed-off-by: Pete Wyckoff Signed-off-by: Junio C Hamano --- git-p4.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'git-p4.py') diff --git a/git-p4.py b/git-p4.py index 7efa9a862e..ff3e8c9425 100755 --- a/git-p4.py +++ b/git-p4.py @@ -560,13 +560,16 @@ def gitBranchExists(branch): return proc.wait() == 0; _gitConfig = {} -def gitConfig(key, args = None): # set args to "--bool", for instance + +def gitConfig(key, args=None): # set args to "--bool", for instance if not _gitConfig.has_key(key): - argsFilter = "" - if args != None: - argsFilter = "%s " % args - cmd = "git config %s%s" % (argsFilter, key) - _gitConfig[key] = read_pipe(cmd, ignore_error=True).strip() + cmd = [ "git", "config" ] + if args: + assert(args == "--bool") + cmd.append(args) + cmd.append(key) + s = read_pipe(cmd, ignore_error=True) + _gitConfig[key] = s.strip() return _gitConfig[key] def gitConfigList(key): -- cgit v1.2.1 From 0d60903293d1a839541add545846c9a8b3967c5f Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Sat, 26 Jan 2013 22:11:24 -0500 Subject: git p4: introduce gitConfigBool Make the intent of "--bool" more obvious by returning a direct True or False value. Convert a couple non-bool users with obvious bool intent. Signed-off-by: Pete Wyckoff Signed-off-by: Junio C Hamano --- git-p4.py | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) (limited to 'git-p4.py') diff --git a/git-p4.py b/git-p4.py index ff3e8c9425..955a5dde24 100755 --- a/git-p4.py +++ b/git-p4.py @@ -561,17 +561,25 @@ def gitBranchExists(branch): _gitConfig = {} -def gitConfig(key, args=None): # set args to "--bool", for instance +def gitConfig(key): if not _gitConfig.has_key(key): - cmd = [ "git", "config" ] - if args: - assert(args == "--bool") - cmd.append(args) - cmd.append(key) + cmd = [ "git", "config", key ] s = read_pipe(cmd, ignore_error=True) _gitConfig[key] = s.strip() return _gitConfig[key] +def gitConfigBool(key): + """Return a bool, using git config --bool. It is True only if the + variable is set to true, and False if set to false or not present + in the config.""" + + if not _gitConfig.has_key(key): + cmd = [ "git", "config", "--bool", key ] + s = read_pipe(cmd, ignore_error=True) + v = s.strip() + _gitConfig[key] = v == "true" + return _gitConfig[key] + def gitConfigList(key): if not _gitConfig.has_key(key): s = read_pipe(["git", "config", "--get-all", key], ignore_error=True) @@ -722,8 +730,7 @@ def p4PathStartsWith(path, prefix): # # we may or may not have a problem. If you have core.ignorecase=true, # we treat DirA and dira as the same directory - ignorecase = gitConfig("core.ignorecase", "--bool") == "true" - if ignorecase: + if gitConfigBool("core.ignorecase"): return path.lower().startswith(prefix.lower()) return path.startswith(prefix) @@ -959,7 +966,7 @@ class P4Submit(Command, P4UserMap): self.usage += " [name of git branch to submit into perforce depot]" self.origin = "" self.detectRenames = False - self.preserveUser = gitConfig("git-p4.preserveUser").lower() == "true" + self.preserveUser = gitConfigBool("git-p4.preserveUser") self.dry_run = False self.prepare_p4_only = False self.conflict_behavior = None @@ -1068,7 +1075,7 @@ class P4Submit(Command, P4UserMap): (user,email) = self.p4UserForCommit(id) if not user: msg = "Cannot find p4 user for email %s in commit %s." % (email, id) - if gitConfig('git-p4.allowMissingP4Users').lower() == "true": + if gitConfigBool("git-p4.allowMissingP4Users"): print "%s" % msg else: die("Error: %s\nSet git-p4.allowMissingP4Users to true to allow this." % msg) @@ -1163,7 +1170,7 @@ class P4Submit(Command, P4UserMap): message. Return true if okay to continue with the submit.""" # if configured to skip the editing part, just submit - if gitConfig("git-p4.skipSubmitEdit") == "true": + if gitConfigBool("git-p4.skipSubmitEdit"): return True # look at the modification time, to check later if the user saved @@ -1179,7 +1186,7 @@ class P4Submit(Command, P4UserMap): # If the file was not saved, prompt to see if this patch should # be skipped. But skip this verification step if configured so. - if gitConfig("git-p4.skipSubmitEditCheck") == "true": + if gitConfigBool("git-p4.skipSubmitEditCheck"): return True # modification time updated means user saved the file @@ -1279,7 +1286,7 @@ class P4Submit(Command, P4UserMap): # Patch failed, maybe it's just RCS keyword woes. Look through # the patch to see if that's possible. - if gitConfig("git-p4.attemptRCSCleanup","--bool") == "true": + if gitConfigBool("git-p4.attemptRCSCleanup"): file = None pattern = None kwfiles = {} @@ -1574,7 +1581,7 @@ class P4Submit(Command, P4UserMap): sys.exit(128) self.useClientSpec = False - if gitConfig("git-p4.useclientspec", "--bool") == "true": + if gitConfigBool("git-p4.useclientspec"): self.useClientSpec = True if self.useClientSpec: self.clientSpecDirs = getClientSpec() @@ -1614,7 +1621,7 @@ class P4Submit(Command, P4UserMap): commits.append(line.strip()) commits.reverse() - if self.preserveUser or (gitConfig("git-p4.skipUserNameCheck") == "true"): + if self.preserveUser or gitConfigBool("git-p4.skipUserNameCheck"): self.checkAuthorship = False else: self.checkAuthorship = True @@ -1650,7 +1657,7 @@ class P4Submit(Command, P4UserMap): else: self.diffOpts += " -C%s" % detectCopies - if gitConfig("git-p4.detectCopiesHarder", "--bool") == "true": + if gitConfigBool("git-p4.detectCopiesHarder"): self.diffOpts += " --find-copies-harder" # @@ -1734,7 +1741,7 @@ class P4Submit(Command, P4UserMap): "--format=format:%h %s", c]) print "You will have to do 'git p4 sync' and rebase." - if gitConfig("git-p4.exportLabels", "--bool") == "true": + if gitConfigBool("git-p4.exportLabels"): self.exportLabels = True if self.exportLabels: @@ -2834,7 +2841,7 @@ class P4Sync(Command, P4UserMap): # will use this after clone to set the variable self.useClientSpec_from_options = True else: - if gitConfig("git-p4.useclientspec", "--bool") == "true": + if gitConfigBool("git-p4.useclientspec"): self.useClientSpec = True if self.useClientSpec: self.clientSpecDirs = getClientSpec() @@ -3074,7 +3081,7 @@ class P4Sync(Command, P4UserMap): sys.stdout.write("%s " % b) sys.stdout.write("\n") - if gitConfig("git-p4.importLabels", "--bool") == "true": + if gitConfigBool("git-p4.importLabels"): self.importLabels = True if self.importLabels: -- cgit v1.2.1