77# 2007 Trolltech ASA
88# License: MIT <http://www.opensource.org/licenses/mit-license.php>
99#
10-
1110import sys
1211if 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
2126try :
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+
188209def 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
570602def 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
575608def 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\n Set 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 "\n Ignoring 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