Skip to content

Commit 9aea11d

Browse files
committed
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 ...
2 parents d5365b4 + 0d60903 commit 9aea11d

10 files changed

+332
-105
lines changed

git-p4.py

Lines changed: 80 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,21 @@
77
# 2007 Trolltech ASA
88
# License: MIT <http://www.opensource.org/licenses/mit-license.php>
99
#
10-
1110
import sys
1211
if sys.hexversion < 0x02040000:
1312
# The limiter is the subprocess module
1413
sys.stderr.write("git-p4: requires Python 2.4 or later.\n")
1514
sys.exit(1)
16-
17-
import optparse, os, marshal, subprocess, shelve
18-
import tempfile, getopt, os.path, time, platform
19-
import re, shutil
15+
import os
16+
import optparse
17+
import marshal
18+
import subprocess
19+
import tempfile
20+
import time
21+
import platform
22+
import re
23+
import shutil
24+
import stat
2025

2126
try:
2227
from subprocess import CalledProcessError
@@ -185,6 +190,22 @@ def p4_system(cmd):
185190
if retcode:
186191
raise CalledProcessError(retcode, real_cmd)
187192

193+
_p4_version_string = None
194+
def p4_version_string():
195+
"""Read the version string, showing just the last line, which
196+
hopefully is the interesting version bit.
197+
198+
$ p4 -V
199+
Perforce - The Fast Software Configuration Management System.
200+
Copyright 1995-2011 Perforce Software. All rights reserved.
201+
Rev. P4/NTX86/2011.1/393975 (2011/12/16).
202+
"""
203+
global _p4_version_string
204+
if not _p4_version_string:
205+
a = p4_read_pipe_lines(["-V"])
206+
_p4_version_string = a[-1].rstrip()
207+
return _p4_version_string
208+
188209
def p4_integrate(src, dest):
189210
p4_system(["integrate", "-Dt", wildcard_encode(src), wildcard_encode(dest)])
190211

@@ -558,18 +579,30 @@ def gitBranchExists(branch):
558579
return proc.wait() == 0;
559580

560581
_gitConfig = {}
561-
def gitConfig(key, args = None): # set args to "--bool", for instance
582+
583+
def gitConfig(key):
584+
if not _gitConfig.has_key(key):
585+
cmd = [ "git", "config", key ]
586+
s = read_pipe(cmd, ignore_error=True)
587+
_gitConfig[key] = s.strip()
588+
return _gitConfig[key]
589+
590+
def gitConfigBool(key):
591+
"""Return a bool, using git config --bool. It is True only if the
592+
variable is set to true, and False if set to false or not present
593+
in the config."""
594+
562595
if not _gitConfig.has_key(key):
563-
argsFilter = ""
564-
if args != None:
565-
argsFilter = "%s " % args
566-
cmd = "git config %s%s" % (argsFilter, key)
567-
_gitConfig[key] = read_pipe(cmd, ignore_error=True).strip()
596+
cmd = [ "git", "config", "--bool", key ]
597+
s = read_pipe(cmd, ignore_error=True)
598+
v = s.strip()
599+
_gitConfig[key] = v == "true"
568600
return _gitConfig[key]
569601

570602
def gitConfigList(key):
571603
if not _gitConfig.has_key(key):
572-
_gitConfig[key] = read_pipe("git config --get-all %s" % key, ignore_error=True).strip().split(os.linesep)
604+
s = read_pipe(["git", "config", "--get-all", key], ignore_error=True)
605+
_gitConfig[key] = s.strip().split(os.linesep)
573606
return _gitConfig[key]
574607

575608
def p4BranchesInGit(branchesAreInRemotes=True):
@@ -716,8 +749,7 @@ def p4PathStartsWith(path, prefix):
716749
#
717750
# we may or may not have a problem. If you have core.ignorecase=true,
718751
# we treat DirA and dira as the same directory
719-
ignorecase = gitConfig("core.ignorecase", "--bool") == "true"
720-
if ignorecase:
752+
if gitConfigBool("core.ignorecase"):
721753
return path.lower().startswith(prefix.lower())
722754
return path.startswith(prefix)
723755

@@ -954,7 +986,7 @@ def __init__(self):
954986
self.usage += " [name of git branch to submit into perforce depot]"
955987
self.origin = ""
956988
self.detectRenames = False
957-
self.preserveUser = gitConfig("git-p4.preserveUser").lower() == "true"
989+
self.preserveUser = gitConfigBool("git-p4.preserveUser")
958990
self.dry_run = False
959991
self.prepare_p4_only = False
960992
self.conflict_behavior = None
@@ -1049,7 +1081,8 @@ def patchRCSKeywords(self, file, pattern):
10491081
def p4UserForCommit(self,id):
10501082
# Return the tuple (perforce user,git email) for a given git commit id
10511083
self.getUserMapFromPerforceServer()
1052-
gitEmail = read_pipe("git log --max-count=1 --format='%%ae' %s" % id)
1084+
gitEmail = read_pipe(["git", "log", "--max-count=1",
1085+
"--format=%ae", id])
10531086
gitEmail = gitEmail.strip()
10541087
if not self.emails.has_key(gitEmail):
10551088
return (None,gitEmail)
@@ -1062,7 +1095,7 @@ def checkValidP4Users(self,commits):
10621095
(user,email) = self.p4UserForCommit(id)
10631096
if not user:
10641097
msg = "Cannot find p4 user for email %s in commit %s." % (email, id)
1065-
if gitConfig('git-p4.allowMissingP4Users').lower() == "true":
1098+
if gitConfigBool("git-p4.allowMissingP4Users"):
10661099
print "%s" % msg
10671100
else:
10681101
die("Error: %s\nSet git-p4.allowMissingP4Users to true to allow this." % msg)
@@ -1157,7 +1190,7 @@ def edit_template(self, template_file):
11571190
message. Return true if okay to continue with the submit."""
11581191

11591192
# if configured to skip the editing part, just submit
1160-
if gitConfig("git-p4.skipSubmitEdit") == "true":
1193+
if gitConfigBool("git-p4.skipSubmitEdit"):
11611194
return True
11621195

11631196
# look at the modification time, to check later if the user saved
@@ -1173,7 +1206,7 @@ def edit_template(self, template_file):
11731206

11741207
# If the file was not saved, prompt to see if this patch should
11751208
# be skipped. But skip this verification step if configured so.
1176-
if gitConfig("git-p4.skipSubmitEditCheck") == "true":
1209+
if gitConfigBool("git-p4.skipSubmitEditCheck"):
11771210
return True
11781211

11791212
# modification time updated means user saved the file
@@ -1231,6 +1264,9 @@ def applyCommit(self, id):
12311264
p4_edit(dest)
12321265
pureRenameCopy.discard(dest)
12331266
filesToChangeExecBit[dest] = diff['dst_mode']
1267+
if self.isWindows:
1268+
# turn off read-only attribute
1269+
os.chmod(dest, stat.S_IWRITE)
12341270
os.unlink(dest)
12351271
editedFiles.add(dest)
12361272
elif modifier == "R":
@@ -1249,6 +1285,8 @@ def applyCommit(self, id):
12491285
p4_edit(dest) # with move: already open, writable
12501286
filesToChangeExecBit[dest] = diff['dst_mode']
12511287
if not self.p4HasMoveCommand:
1288+
if self.isWindows:
1289+
os.chmod(dest, stat.S_IWRITE)
12521290
os.unlink(dest)
12531291
filesToDelete.add(src)
12541292
editedFiles.add(dest)
@@ -1268,7 +1306,7 @@ def applyCommit(self, id):
12681306

12691307
# Patch failed, maybe it's just RCS keyword woes. Look through
12701308
# the patch to see if that's possible.
1271-
if gitConfig("git-p4.attemptRCSCleanup","--bool") == "true":
1309+
if gitConfigBool("git-p4.attemptRCSCleanup"):
12721310
file = None
12731311
pattern = None
12741312
kwfiles = {}
@@ -1289,6 +1327,10 @@ def applyCommit(self, id):
12891327
for file in kwfiles:
12901328
if verbose:
12911329
print "zapping %s with %s" % (line,pattern)
1330+
# File is being deleted, so not open in p4. Must
1331+
# disable the read-only bit on windows.
1332+
if self.isWindows and file not in editedFiles:
1333+
os.chmod(file, stat.S_IWRITE)
12921334
self.patchRCSKeywords(file, kwfiles[file])
12931335
fixed_rcs_keywords = True
12941336

@@ -1559,7 +1601,7 @@ def run(self, args):
15591601
sys.exit(128)
15601602

15611603
self.useClientSpec = False
1562-
if gitConfig("git-p4.useclientspec", "--bool") == "true":
1604+
if gitConfigBool("git-p4.useclientspec"):
15631605
self.useClientSpec = True
15641606
if self.useClientSpec:
15651607
self.clientSpecDirs = getClientSpec()
@@ -1595,11 +1637,11 @@ def run(self, args):
15951637
self.check()
15961638

15971639
commits = []
1598-
for line in read_pipe_lines("git rev-list --no-merges %s..%s" % (self.origin, self.master)):
1640+
for line in read_pipe_lines(["git", "rev-list", "--no-merges", "%s..%s" % (self.origin, self.master)]):
15991641
commits.append(line.strip())
16001642
commits.reverse()
16011643

1602-
if self.preserveUser or (gitConfig("git-p4.skipUserNameCheck") == "true"):
1644+
if self.preserveUser or gitConfigBool("git-p4.skipUserNameCheck"):
16031645
self.checkAuthorship = False
16041646
else:
16051647
self.checkAuthorship = True
@@ -1635,7 +1677,7 @@ def run(self, args):
16351677
else:
16361678
self.diffOpts += " -C%s" % detectCopies
16371679

1638-
if gitConfig("git-p4.detectCopiesHarder", "--bool") == "true":
1680+
if gitConfigBool("git-p4.detectCopiesHarder"):
16391681
self.diffOpts += " --find-copies-harder"
16401682

16411683
#
@@ -1719,7 +1761,7 @@ def run(self, args):
17191761
"--format=format:%h %s", c])
17201762
print "You will have to do 'git p4 sync' and rebase."
17211763

1722-
if gitConfig("git-p4.exportLabels", "--bool") == "true":
1764+
if gitConfigBool("git-p4.exportLabels"):
17231765
self.exportLabels = True
17241766

17251767
if self.exportLabels:
@@ -1989,7 +2031,6 @@ def __init__(self):
19892031
self.syncWithOrigin = True
19902032
self.importIntoRemotes = True
19912033
self.maxChanges = ""
1992-
self.isWindows = (platform.system() == "Windows")
19932034
self.keepRepoPath = False
19942035
self.depotPaths = None
19952036
self.p4BranchesInGit = []
@@ -2134,7 +2175,14 @@ def streamOneP4File(self, file, contents):
21342175
# operations. utf16 is converted to ascii or utf8, perhaps.
21352176
# But ascii text saved as -t utf16 is completely mangled.
21362177
# Invoke print -o to get the real contents.
2178+
#
2179+
# On windows, the newlines will always be mangled by print, so put
2180+
# them back too. This is not needed to the cygwin windows version,
2181+
# just the native "NT" type.
2182+
#
21372183
text = p4_read_pipe(['print', '-q', '-o', '-', file['depotFile']])
2184+
if p4_version_string().find("/NT") >= 0:
2185+
text = text.replace("\r\n", "\n")
21382186
contents = [ text ]
21392187

21402188
if type_base == "apple":
@@ -2150,15 +2198,6 @@ def streamOneP4File(self, file, contents):
21502198
print "\nIgnoring apple filetype file %s" % file['depotFile']
21512199
return
21522200

2153-
# Perhaps windows wants unicode, utf16 newlines translated too;
2154-
# but this is not doing it.
2155-
if self.isWindows and type_base == "text":
2156-
mangled = []
2157-
for data in contents:
2158-
data = data.replace("\r\n", "\n")
2159-
mangled.append(data)
2160-
contents = mangled
2161-
21622201
# Note that we do not try to de-mangle keywords on utf16 files,
21632202
# even though in theory somebody may want that.
21642203
pattern = p4_keywords_regexp_for_type(type_base, type_mods)
@@ -2636,7 +2675,8 @@ def importNewBranch(self, branch, maxChange):
26362675

26372676
def searchParent(self, parent, branch, target):
26382677
parentFound = False
2639-
for blob in read_pipe_lines(["git", "rev-list", "--reverse", "--no-merges", parent]):
2678+
for blob in read_pipe_lines(["git", "rev-list", "--reverse",
2679+
"--no-merges", parent]):
26402680
blob = blob.strip()
26412681
if len(read_pipe(["git", "diff-tree", blob, target])) == 0:
26422682
parentFound = True
@@ -2707,7 +2747,7 @@ def importChanges(self, changes):
27072747

27082748
blob = None
27092749
if len(parent) > 0:
2710-
tempBranch = os.path.join(self.tempBranchLocation, "%d" % (change))
2750+
tempBranch = "%s/%d" % (self.tempBranchLocation, change)
27112751
if self.verbose:
27122752
print "Creating temporary branch: " + tempBranch
27132753
self.commit(description, filesForCommit, tempBranch)
@@ -2821,7 +2861,7 @@ def run(self, args):
28212861
# will use this after clone to set the variable
28222862
self.useClientSpec_from_options = True
28232863
else:
2824-
if gitConfig("git-p4.useclientspec", "--bool") == "true":
2864+
if gitConfigBool("git-p4.useclientspec"):
28252865
self.useClientSpec = True
28262866
if self.useClientSpec:
28272867
self.clientSpecDirs = getClientSpec()
@@ -3061,7 +3101,7 @@ def run(self, args):
30613101
sys.stdout.write("%s " % b)
30623102
sys.stdout.write("\n")
30633103

3064-
if gitConfig("git-p4.importLabels", "--bool") == "true":
3104+
if gitConfigBool("git-p4.importLabels"):
30653105
self.importLabels = True
30663106

30673107
if self.importLabels:
@@ -3179,6 +3219,7 @@ def run(self, args):
31793219
self.cloneExclude = ["/"+p for p in self.cloneExclude]
31803220
for p in depotPaths:
31813221
if not p.startswith("//"):
3222+
sys.stderr.write('Depot paths must start with "//": %s\n' % p)
31823223
return False
31833224

31843225
if not self.cloneDestination:

0 commit comments

Comments
 (0)