Skip to content

Commit

Permalink
Merge branch 'pw/git-p4-on-cygwin'
Browse files Browse the repository at this point in the history
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
  ...
  • Loading branch information
Junio C Hamano committed Feb 4, 2013
2 parents d5365b4 + 0d60903 commit 9aea11d
Show file tree
Hide file tree
Showing 10 changed files with 332 additions and 105 deletions.
119 changes: 80 additions & 39 deletions git-p4.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)])

Expand Down Expand Up @@ -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):
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):
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", "--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):
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -954,7 +986,7 @@ def __init__(self):
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
Expand Down Expand Up @@ -1049,7 +1081,8 @@ def patchRCSKeywords(self, file, pattern):
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)
Expand All @@ -1062,7 +1095,7 @@ def checkValidP4Users(self,commits):
(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)
Expand Down Expand Up @@ -1157,7 +1190,7 @@ def edit_template(self, template_file):
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
Expand All @@ -1173,7 +1206,7 @@ def edit_template(self, template_file):

# 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
Expand Down Expand Up @@ -1231,6 +1264,9 @@ def applyCommit(self, id):
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":
Expand All @@ -1249,6 +1285,8 @@ def applyCommit(self, id):
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)
Expand All @@ -1268,7 +1306,7 @@ def applyCommit(self, id):

# 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 = {}
Expand All @@ -1289,6 +1327,10 @@ def applyCommit(self, id):
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

Expand Down Expand Up @@ -1559,7 +1601,7 @@ def run(self, args):
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()
Expand Down Expand Up @@ -1595,11 +1637,11 @@ def run(self, args):
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
Expand Down Expand Up @@ -1635,7 +1677,7 @@ def run(self, args):
else:
self.diffOpts += " -C%s" % detectCopies

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

#
Expand Down Expand Up @@ -1719,7 +1761,7 @@ def run(self, args):
"--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:
Expand Down Expand Up @@ -1989,7 +2031,6 @@ def __init__(self):
self.syncWithOrigin = True
self.importIntoRemotes = True
self.maxChanges = ""
self.isWindows = (platform.system() == "Windows")
self.keepRepoPath = False
self.depotPaths = None
self.p4BranchesInGit = []
Expand Down Expand Up @@ -2134,7 +2175,14 @@ def streamOneP4File(self, file, contents):
# 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":
Expand All @@ -2150,15 +2198,6 @@ def streamOneP4File(self, file, contents):
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)
Expand Down Expand Up @@ -2636,7 +2675,8 @@ def importNewBranch(self, branch, maxChange):

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
Expand Down Expand Up @@ -2707,7 +2747,7 @@ def importChanges(self, changes):

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)
Expand Down Expand Up @@ -2821,7 +2861,7 @@ def run(self, args):
# 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()
Expand Down Expand Up @@ -3061,7 +3101,7 @@ def run(self, args):
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:
Expand Down Expand Up @@ -3179,6 +3219,7 @@ def run(self, args):
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:
Expand Down
Loading

0 comments on commit 9aea11d

Please sign in to comment.