Merge branch 'pw/git-p4-on-cygwin'
Improve "git p4" on Cygwin. * pw/git-p4-on-cygwin: (21 commits) git p4: introduce gitConfigBool git p4: avoid shell when calling git config git p4: avoid shell when invoking git config --get-all git p4: avoid shell when invoking git rev-list git p4: avoid shell when mapping users git p4: disable read-only attribute before deleting git p4 test: use test_chmod for cygwin git p4: cygwin p4 client does not mark read-only git p4 test: avoid wildcard * in windows git p4 test: use LineEnd unix in windows tests too git p4 test: newline handling git p4: scrub crlf for utf16 files on windows git p4: remove unreachable windows \r\n conversion code git p4 test: translate windows paths for cygwin git p4 test: start p4d inside its db dir git p4 test: use client_view in t9806 git p4 test: avoid loop in client_view git p4 test: use client_view to build the initial client git p4: generate better error message for bad depot path git p4: remove unused imports ...
This commit is contained in:
119
git-p4.py
119
git-p4.py
@ -7,16 +7,21 @@
|
||||
# 2007 Trolltech ASA
|
||||
# License: MIT <http://www.opensource.org/licenses/mit-license.php>
|
||||
#
|
||||
|
||||
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
|
||||
import stat
|
||||
|
||||
try:
|
||||
from subprocess import CalledProcessError
|
||||
@ -185,6 +190,22 @@ def p4_system(cmd):
|
||||
if retcode:
|
||||
raise CalledProcessError(retcode, real_cmd)
|
||||
|
||||
_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)])
|
||||
|
||||
@ -558,18 +579,30 @@ def gitBranchExists(branch):
|
||||
return proc.wait() == 0;
|
||||
|
||||
_gitConfig = {}
|
||||
def gitConfig(key, args = None): # set args to "--bool", for instance
|
||||
|
||||
def gitConfig(key):
|
||||
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", 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):
|
||||
_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):
|
||||
@ -716,8 +749,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)
|
||||
|
||||
@ -954,7 +986,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
|
||||
@ -1049,7 +1081,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)
|
||||
@ -1062,7 +1095,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)
|
||||
@ -1157,7 +1190,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
|
||||
@ -1173,7 +1206,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
|
||||
@ -1231,6 +1264,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 +1285,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)
|
||||
@ -1268,7 +1306,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 = {}
|
||||
@ -1289,6 +1327,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
|
||||
|
||||
@ -1559,7 +1601,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()
|
||||
@ -1595,11 +1637,11 @@ 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()
|
||||
|
||||
if self.preserveUser or (gitConfig("git-p4.skipUserNameCheck") == "true"):
|
||||
if self.preserveUser or gitConfigBool("git-p4.skipUserNameCheck"):
|
||||
self.checkAuthorship = False
|
||||
else:
|
||||
self.checkAuthorship = True
|
||||
@ -1635,7 +1677,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"
|
||||
|
||||
#
|
||||
@ -1719,7 +1761,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:
|
||||
@ -1989,7 +2031,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 = []
|
||||
@ -2134,7 +2175,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":
|
||||
@ -2150,15 +2198,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)
|
||||
@ -2636,7 +2675,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
|
||||
@ -2707,7 +2747,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)
|
||||
@ -2821,7 +2861,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()
|
||||
@ -3061,7 +3101,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:
|
||||
@ -3179,6 +3219,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:
|
||||
|
||||
Reference in New Issue
Block a user