Merge branch 'ld/git-p4-tags-and-labels'
By Luke Diamand * ld/git-p4-tags-and-labels: git p4: fix unit tests git p4: move verbose to base class git p4: Ignore P4EDITOR if it is empty git p4: Squash P4EDITOR in test harness git p4: fix-up "import/export of labels to/from p4" git p4: import/export of labels to/from p4 git p4: Fixing script editor checks
This commit is contained in:
commit
9768cafe68
@ -158,11 +158,14 @@ OPTIONS
|
|||||||
|
|
||||||
General options
|
General options
|
||||||
~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~
|
||||||
All commands except clone accept this option.
|
All commands except clone accept these options.
|
||||||
|
|
||||||
--git-dir <dir>::
|
--git-dir <dir>::
|
||||||
Set the 'GIT_DIR' environment variable. See linkgit:git[1].
|
Set the 'GIT_DIR' environment variable. See linkgit:git[1].
|
||||||
|
|
||||||
|
--verbose::
|
||||||
|
Provide more progress information.
|
||||||
|
|
||||||
Sync options
|
Sync options
|
||||||
~~~~~~~~~~~~
|
~~~~~~~~~~~~
|
||||||
These options can be used in the initial 'clone' as well as in
|
These options can be used in the initial 'clone' as well as in
|
||||||
@ -193,12 +196,13 @@ git repository:
|
|||||||
--silent::
|
--silent::
|
||||||
Do not print any progress information.
|
Do not print any progress information.
|
||||||
|
|
||||||
--verbose::
|
|
||||||
Provide more progress information.
|
|
||||||
|
|
||||||
--detect-labels::
|
--detect-labels::
|
||||||
Query p4 for labels associated with the depot paths, and add
|
Query p4 for labels associated with the depot paths, and add
|
||||||
them as tags in git.
|
them as tags in git. Limited usefulness as only imports labels
|
||||||
|
associated with new changelists. Deprecated.
|
||||||
|
|
||||||
|
--import-labels::
|
||||||
|
Import labels from p4 into git.
|
||||||
|
|
||||||
--import-local::
|
--import-local::
|
||||||
By default, p4 branches are stored in 'refs/remotes/p4/',
|
By default, p4 branches are stored in 'refs/remotes/p4/',
|
||||||
@ -245,9 +249,6 @@ Submit options
|
|||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
These options can be used to modify 'git p4 submit' behavior.
|
These options can be used to modify 'git p4 submit' behavior.
|
||||||
|
|
||||||
--verbose::
|
|
||||||
Provide more progress information.
|
|
||||||
|
|
||||||
--origin <commit>::
|
--origin <commit>::
|
||||||
Upstream location from which commits are identified to submit to
|
Upstream location from which commits are identified to submit to
|
||||||
p4. By default, this is the most recent p4 commit reachable
|
p4. By default, this is the most recent p4 commit reachable
|
||||||
@ -263,6 +264,16 @@ These options can be used to modify 'git p4 submit' behavior.
|
|||||||
Re-author p4 changes before submitting to p4. This option
|
Re-author p4 changes before submitting to p4. This option
|
||||||
requires p4 admin privileges.
|
requires p4 admin privileges.
|
||||||
|
|
||||||
|
--export-labels::
|
||||||
|
Export tags from git as p4 labels. Tags found in git are applied
|
||||||
|
to the perforce working directory.
|
||||||
|
|
||||||
|
Rebase options
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
These options can be used to modify 'git p4 rebase' behavior.
|
||||||
|
|
||||||
|
--import-labels::
|
||||||
|
Import p4 labels.
|
||||||
|
|
||||||
DEPOT PATH SYNTAX
|
DEPOT PATH SYNTAX
|
||||||
-----------------
|
-----------------
|
||||||
@ -427,11 +438,23 @@ git-p4.branchList::
|
|||||||
enabled. Each entry should be a pair of branch names separated
|
enabled. Each entry should be a pair of branch names separated
|
||||||
by a colon (:). This example declares that both branchA and
|
by a colon (:). This example declares that both branchA and
|
||||||
branchB were created from main:
|
branchB were created from main:
|
||||||
|
|
||||||
-------------
|
-------------
|
||||||
git config git-p4.branchList main:branchA
|
git config git-p4.branchList main:branchA
|
||||||
git config --add git-p4.branchList main:branchB
|
git config --add git-p4.branchList main:branchB
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
git-p4.ignoredP4Labels::
|
||||||
|
List of p4 labels to ignore. This is built automatically as
|
||||||
|
unimportable labels are discovered.
|
||||||
|
|
||||||
|
git-p4.importLabels::
|
||||||
|
Import p4 labels into git, as per --import-labels.
|
||||||
|
|
||||||
|
git-p4.labelImportRegexp::
|
||||||
|
Only p4 labels matching this regular expression will be imported. The
|
||||||
|
default value is '[a-zA-Z0-9_\-.]+$'.
|
||||||
|
|
||||||
git-p4.useClientSpec::
|
git-p4.useClientSpec::
|
||||||
Specify that the p4 client spec should be used to identify p4
|
Specify that the p4 client spec should be used to identify p4
|
||||||
depot paths of interest. This is equivalent to specifying the
|
depot paths of interest. This is equivalent to specifying the
|
||||||
@ -481,10 +504,17 @@ git-p4.skipUserNameCheck::
|
|||||||
submission regardless.
|
submission regardless.
|
||||||
|
|
||||||
git-p4.attemptRCSCleanup:
|
git-p4.attemptRCSCleanup:
|
||||||
If enabled, 'git p4 submit' will attempt to cleanup RCS keywords
|
If enabled, 'git p4 submit' will attempt to cleanup RCS keywords
|
||||||
($Header$, etc). These would otherwise cause merge conflicts and prevent
|
($Header$, etc). These would otherwise cause merge conflicts and prevent
|
||||||
the submit going ahead. This option should be considered experimental at
|
the submit going ahead. This option should be considered experimental at
|
||||||
present.
|
present.
|
||||||
|
|
||||||
|
git-p4.exportLabels::
|
||||||
|
Export git tags to p4 labels, as per --export-labels.
|
||||||
|
|
||||||
|
git-p4.labelExportRegexp::
|
||||||
|
Only p4 labels matching this regular expression will be exported. The
|
||||||
|
default value is '[a-zA-Z0-9_\-.]+$'.
|
||||||
|
|
||||||
IMPLEMENTATION DETAILS
|
IMPLEMENTATION DETAILS
|
||||||
----------------------
|
----------------------
|
||||||
|
289
git-p4.py
289
git-p4.py
@ -14,6 +14,8 @@ import re, shutil
|
|||||||
|
|
||||||
verbose = False
|
verbose = False
|
||||||
|
|
||||||
|
# Only labels/tags matching this will be imported/exported
|
||||||
|
defaultLabelRegexp = r'[a-zA-Z0-9_\-.]+$'
|
||||||
|
|
||||||
def p4_build_cmd(cmd):
|
def p4_build_cmd(cmd):
|
||||||
"""Build a suitable p4 command line.
|
"""Build a suitable p4 command line.
|
||||||
@ -253,6 +255,26 @@ def getP4OpenedType(file):
|
|||||||
else:
|
else:
|
||||||
die("Could not determine file type for %s (result: '%s')" % (file, result))
|
die("Could not determine file type for %s (result: '%s')" % (file, result))
|
||||||
|
|
||||||
|
# Return the set of all p4 labels
|
||||||
|
def getP4Labels(depotPaths):
|
||||||
|
labels = set()
|
||||||
|
if isinstance(depotPaths,basestring):
|
||||||
|
depotPaths = [depotPaths]
|
||||||
|
|
||||||
|
for l in p4CmdList(["labels"] + ["%s..." % p for p in depotPaths]):
|
||||||
|
label = l['label']
|
||||||
|
labels.add(label)
|
||||||
|
|
||||||
|
return labels
|
||||||
|
|
||||||
|
# Return the set of all git tags
|
||||||
|
def getGitTags():
|
||||||
|
gitTags = set()
|
||||||
|
for line in read_pipe_lines(["git", "tag"]):
|
||||||
|
tag = line.strip()
|
||||||
|
gitTags.add(tag)
|
||||||
|
return gitTags
|
||||||
|
|
||||||
def diffTreePattern():
|
def diffTreePattern():
|
||||||
# This is a simple generator for the diff tree regex pattern. This could be
|
# This is a simple generator for the diff tree regex pattern. This could be
|
||||||
# a class variable if this and parseDiffTreeEntry were a part of a class.
|
# a class variable if this and parseDiffTreeEntry were a part of a class.
|
||||||
@ -640,6 +662,7 @@ class Command:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.usage = "usage: %prog [options]"
|
self.usage = "usage: %prog [options]"
|
||||||
self.needsGit = True
|
self.needsGit = True
|
||||||
|
self.verbose = False
|
||||||
|
|
||||||
class P4UserMap:
|
class P4UserMap:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -705,13 +728,9 @@ class P4UserMap:
|
|||||||
class P4Debug(Command):
|
class P4Debug(Command):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
Command.__init__(self)
|
Command.__init__(self)
|
||||||
self.options = [
|
self.options = []
|
||||||
optparse.make_option("--verbose", dest="verbose", action="store_true",
|
|
||||||
default=False),
|
|
||||||
]
|
|
||||||
self.description = "A tool to debug the output of p4 -G."
|
self.description = "A tool to debug the output of p4 -G."
|
||||||
self.needsGit = False
|
self.needsGit = False
|
||||||
self.verbose = False
|
|
||||||
|
|
||||||
def run(self, args):
|
def run(self, args):
|
||||||
j = 0
|
j = 0
|
||||||
@ -725,11 +744,9 @@ class P4RollBack(Command):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
Command.__init__(self)
|
Command.__init__(self)
|
||||||
self.options = [
|
self.options = [
|
||||||
optparse.make_option("--verbose", dest="verbose", action="store_true"),
|
|
||||||
optparse.make_option("--local", dest="rollbackLocalBranches", action="store_true")
|
optparse.make_option("--local", dest="rollbackLocalBranches", action="store_true")
|
||||||
]
|
]
|
||||||
self.description = "A tool to debug the multi-branch import. Don't use :)"
|
self.description = "A tool to debug the multi-branch import. Don't use :)"
|
||||||
self.verbose = False
|
|
||||||
self.rollbackLocalBranches = False
|
self.rollbackLocalBranches = False
|
||||||
|
|
||||||
def run(self, args):
|
def run(self, args):
|
||||||
@ -787,20 +804,20 @@ class P4Submit(Command, P4UserMap):
|
|||||||
Command.__init__(self)
|
Command.__init__(self)
|
||||||
P4UserMap.__init__(self)
|
P4UserMap.__init__(self)
|
||||||
self.options = [
|
self.options = [
|
||||||
optparse.make_option("--verbose", dest="verbose", action="store_true"),
|
|
||||||
optparse.make_option("--origin", dest="origin"),
|
optparse.make_option("--origin", dest="origin"),
|
||||||
optparse.make_option("-M", dest="detectRenames", action="store_true"),
|
optparse.make_option("-M", dest="detectRenames", action="store_true"),
|
||||||
# preserve the user, requires relevant p4 permissions
|
# preserve the user, requires relevant p4 permissions
|
||||||
optparse.make_option("--preserve-user", dest="preserveUser", action="store_true"),
|
optparse.make_option("--preserve-user", dest="preserveUser", action="store_true"),
|
||||||
|
optparse.make_option("--export-labels", dest="exportLabels", action="store_true"),
|
||||||
]
|
]
|
||||||
self.description = "Submit changes from git to the perforce depot."
|
self.description = "Submit changes from git to the perforce depot."
|
||||||
self.usage += " [name of git branch to submit into perforce depot]"
|
self.usage += " [name of git branch to submit into perforce depot]"
|
||||||
self.interactive = True
|
self.interactive = True
|
||||||
self.origin = ""
|
self.origin = ""
|
||||||
self.detectRenames = False
|
self.detectRenames = False
|
||||||
self.verbose = False
|
|
||||||
self.preserveUser = gitConfig("git-p4.preserveUser").lower() == "true"
|
self.preserveUser = gitConfig("git-p4.preserveUser").lower() == "true"
|
||||||
self.isWindows = (platform.system() == "Windows")
|
self.isWindows = (platform.system() == "Windows")
|
||||||
|
self.exportLabels = False
|
||||||
|
|
||||||
def check(self):
|
def check(self):
|
||||||
if len(p4CmdList("opened ...")) > 0:
|
if len(p4CmdList("opened ...")) > 0:
|
||||||
@ -970,7 +987,7 @@ class P4Submit(Command, P4UserMap):
|
|||||||
mtime = os.stat(template_file).st_mtime
|
mtime = os.stat(template_file).st_mtime
|
||||||
|
|
||||||
# invoke the editor
|
# invoke the editor
|
||||||
if os.environ.has_key("P4EDITOR"):
|
if os.environ.has_key("P4EDITOR") and (os.environ.get("P4EDITOR") != ""):
|
||||||
editor = os.environ.get("P4EDITOR")
|
editor = os.environ.get("P4EDITOR")
|
||||||
else:
|
else:
|
||||||
editor = read_pipe("git var GIT_EDITOR").strip()
|
editor = read_pipe("git var GIT_EDITOR").strip()
|
||||||
@ -1228,6 +1245,71 @@ class P4Submit(Command, P4UserMap):
|
|||||||
+ "Please review/edit and then use p4 submit -i < %s to submit directly!"
|
+ "Please review/edit and then use p4 submit -i < %s to submit directly!"
|
||||||
% (fileName, fileName))
|
% (fileName, fileName))
|
||||||
|
|
||||||
|
# Export git tags as p4 labels. Create a p4 label and then tag
|
||||||
|
# with that.
|
||||||
|
def exportGitTags(self, gitTags):
|
||||||
|
validLabelRegexp = gitConfig("git-p4.labelExportRegexp")
|
||||||
|
if len(validLabelRegexp) == 0:
|
||||||
|
validLabelRegexp = defaultLabelRegexp
|
||||||
|
m = re.compile(validLabelRegexp)
|
||||||
|
|
||||||
|
for name in gitTags:
|
||||||
|
|
||||||
|
if not m.match(name):
|
||||||
|
if verbose:
|
||||||
|
print "tag %s does not match regexp %s" % (name, validTagRegexp)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Get the p4 commit this corresponds to
|
||||||
|
logMessage = extractLogMessageFromGitCommit(name)
|
||||||
|
values = extractSettingsGitLog(logMessage)
|
||||||
|
|
||||||
|
if not values.has_key('change'):
|
||||||
|
# a tag pointing to something not sent to p4; ignore
|
||||||
|
if verbose:
|
||||||
|
print "git tag %s does not give a p4 commit" % name
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
changelist = values['change']
|
||||||
|
|
||||||
|
# Get the tag details.
|
||||||
|
inHeader = True
|
||||||
|
isAnnotated = False
|
||||||
|
body = []
|
||||||
|
for l in read_pipe_lines(["git", "cat-file", "-p", name]):
|
||||||
|
l = l.strip()
|
||||||
|
if inHeader:
|
||||||
|
if re.match(r'tag\s+', l):
|
||||||
|
isAnnotated = True
|
||||||
|
elif re.match(r'\s*$', l):
|
||||||
|
inHeader = False
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
body.append(l)
|
||||||
|
|
||||||
|
if not isAnnotated:
|
||||||
|
body = ["lightweight tag imported by git p4\n"]
|
||||||
|
|
||||||
|
# Create the label - use the same view as the client spec we are using
|
||||||
|
clientSpec = getClientSpec()
|
||||||
|
|
||||||
|
labelTemplate = "Label: %s\n" % name
|
||||||
|
labelTemplate += "Description:\n"
|
||||||
|
for b in body:
|
||||||
|
labelTemplate += "\t" + b + "\n"
|
||||||
|
labelTemplate += "View:\n"
|
||||||
|
for mapping in clientSpec.mappings:
|
||||||
|
labelTemplate += "\t%s\n" % mapping.depot_side.path
|
||||||
|
|
||||||
|
p4_write_pipe(["label", "-i"], labelTemplate)
|
||||||
|
|
||||||
|
# Use the label
|
||||||
|
p4_system(["tag", "-l", name] +
|
||||||
|
["%s@%s" % (mapping.depot_side.path, changelist) for mapping in clientSpec.mappings])
|
||||||
|
|
||||||
|
if verbose:
|
||||||
|
print "created p4 label for tag %s" % name
|
||||||
|
|
||||||
def run(self, args):
|
def run(self, args):
|
||||||
if len(args) == 0:
|
if len(args) == 0:
|
||||||
self.master = currentGitBranch()
|
self.master = currentGitBranch()
|
||||||
@ -1317,6 +1399,16 @@ class P4Submit(Command, P4UserMap):
|
|||||||
rebase = P4Rebase()
|
rebase = P4Rebase()
|
||||||
rebase.rebase()
|
rebase.rebase()
|
||||||
|
|
||||||
|
if gitConfig("git-p4.exportLabels", "--bool") == "true":
|
||||||
|
self.exportLabels = true
|
||||||
|
|
||||||
|
if self.exportLabels:
|
||||||
|
p4Labels = getP4Labels(self.depotPath)
|
||||||
|
gitTags = getGitTags()
|
||||||
|
|
||||||
|
missingGitTags = gitTags - p4Labels
|
||||||
|
self.exportGitTags(missingGitTags)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
class View(object):
|
class View(object):
|
||||||
@ -1544,7 +1636,7 @@ class P4Sync(Command, P4UserMap):
|
|||||||
optparse.make_option("--changesfile", dest="changesFile"),
|
optparse.make_option("--changesfile", dest="changesFile"),
|
||||||
optparse.make_option("--silent", dest="silent", action="store_true"),
|
optparse.make_option("--silent", dest="silent", action="store_true"),
|
||||||
optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"),
|
optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"),
|
||||||
optparse.make_option("--verbose", dest="verbose", action="store_true"),
|
optparse.make_option("--import-labels", dest="importLabels", action="store_true"),
|
||||||
optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false",
|
optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false",
|
||||||
help="Import into refs/heads/ , not refs/remotes"),
|
help="Import into refs/heads/ , not refs/remotes"),
|
||||||
optparse.make_option("--max-changes", dest="maxChanges"),
|
optparse.make_option("--max-changes", dest="maxChanges"),
|
||||||
@ -1568,9 +1660,9 @@ class P4Sync(Command, P4UserMap):
|
|||||||
self.branch = ""
|
self.branch = ""
|
||||||
self.detectBranches = False
|
self.detectBranches = False
|
||||||
self.detectLabels = False
|
self.detectLabels = False
|
||||||
|
self.importLabels = False
|
||||||
self.changesFile = ""
|
self.changesFile = ""
|
||||||
self.syncWithOrigin = True
|
self.syncWithOrigin = True
|
||||||
self.verbose = False
|
|
||||||
self.importIntoRemotes = True
|
self.importIntoRemotes = True
|
||||||
self.maxChanges = ""
|
self.maxChanges = ""
|
||||||
self.isWindows = (platform.system() == "Windows")
|
self.isWindows = (platform.system() == "Windows")
|
||||||
@ -1829,6 +1921,38 @@ class P4Sync(Command, P4UserMap):
|
|||||||
else:
|
else:
|
||||||
return "%s <a@b>" % userid
|
return "%s <a@b>" % userid
|
||||||
|
|
||||||
|
# Stream a p4 tag
|
||||||
|
def streamTag(self, gitStream, labelName, labelDetails, commit, epoch):
|
||||||
|
if verbose:
|
||||||
|
print "writing tag %s for commit %s" % (labelName, commit)
|
||||||
|
gitStream.write("tag %s\n" % labelName)
|
||||||
|
gitStream.write("from %s\n" % commit)
|
||||||
|
|
||||||
|
if labelDetails.has_key('Owner'):
|
||||||
|
owner = labelDetails["Owner"]
|
||||||
|
else:
|
||||||
|
owner = None
|
||||||
|
|
||||||
|
# Try to use the owner of the p4 label, or failing that,
|
||||||
|
# the current p4 user id.
|
||||||
|
if owner:
|
||||||
|
email = self.make_email(owner)
|
||||||
|
else:
|
||||||
|
email = self.make_email(self.p4UserId())
|
||||||
|
tagger = "%s %s %s" % (email, epoch, self.tz)
|
||||||
|
|
||||||
|
gitStream.write("tagger %s\n" % tagger)
|
||||||
|
|
||||||
|
print "labelDetails=",labelDetails
|
||||||
|
if labelDetails.has_key('Description'):
|
||||||
|
description = labelDetails['Description']
|
||||||
|
else:
|
||||||
|
description = 'Label from git p4'
|
||||||
|
|
||||||
|
gitStream.write("data %d\n" % len(description))
|
||||||
|
gitStream.write(description)
|
||||||
|
gitStream.write("\n")
|
||||||
|
|
||||||
def commit(self, details, files, branch, branchPrefixes, parent = ""):
|
def commit(self, details, files, branch, branchPrefixes, parent = ""):
|
||||||
epoch = details["time"]
|
epoch = details["time"]
|
||||||
author = details["user"]
|
author = details["user"]
|
||||||
@ -1893,25 +2017,7 @@ class P4Sync(Command, P4UserMap):
|
|||||||
cleanedFiles[info["depotFile"]] = info["rev"]
|
cleanedFiles[info["depotFile"]] = info["rev"]
|
||||||
|
|
||||||
if cleanedFiles == labelRevisions:
|
if cleanedFiles == labelRevisions:
|
||||||
self.gitStream.write("tag tag_%s\n" % labelDetails["label"])
|
self.streamTag(self.gitStream, 'tag_%s' % labelDetails['label'], labelDetails, branch, epoch)
|
||||||
self.gitStream.write("from %s\n" % branch)
|
|
||||||
|
|
||||||
owner = labelDetails["Owner"]
|
|
||||||
|
|
||||||
# Try to use the owner of the p4 label, or failing that,
|
|
||||||
# the current p4 user id.
|
|
||||||
if owner:
|
|
||||||
email = self.make_email(owner)
|
|
||||||
else:
|
|
||||||
email = self.make_email(self.p4UserId())
|
|
||||||
tagger = "%s %s %s" % (email, epoch, self.tz)
|
|
||||||
|
|
||||||
self.gitStream.write("tagger %s\n" % tagger)
|
|
||||||
|
|
||||||
description = labelDetails["Description"]
|
|
||||||
self.gitStream.write("data %d\n" % len(description))
|
|
||||||
self.gitStream.write(description)
|
|
||||||
self.gitStream.write("\n")
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if not self.silent:
|
if not self.silent:
|
||||||
@ -1923,6 +2029,7 @@ class P4Sync(Command, P4UserMap):
|
|||||||
print ("Tag %s does not match with change %s: file count is different."
|
print ("Tag %s does not match with change %s: file count is different."
|
||||||
% (labelDetails["label"], change))
|
% (labelDetails["label"], change))
|
||||||
|
|
||||||
|
# Build a dictionary of changelists and labels, for "detect-labels" option.
|
||||||
def getLabels(self):
|
def getLabels(self):
|
||||||
self.labels = {}
|
self.labels = {}
|
||||||
|
|
||||||
@ -1949,6 +2056,69 @@ class P4Sync(Command, P4UserMap):
|
|||||||
if self.verbose:
|
if self.verbose:
|
||||||
print "Label changes: %s" % self.labels.keys()
|
print "Label changes: %s" % self.labels.keys()
|
||||||
|
|
||||||
|
# Import p4 labels as git tags. A direct mapping does not
|
||||||
|
# exist, so assume that if all the files are at the same revision
|
||||||
|
# then we can use that, or it's something more complicated we should
|
||||||
|
# just ignore.
|
||||||
|
def importP4Labels(self, stream, p4Labels):
|
||||||
|
if verbose:
|
||||||
|
print "import p4 labels: " + ' '.join(p4Labels)
|
||||||
|
|
||||||
|
ignoredP4Labels = gitConfigList("git-p4.ignoredP4Labels")
|
||||||
|
validLabelRegexp = gitConfig("git-p4.labelImportRegexp")
|
||||||
|
if len(validLabelRegexp) == 0:
|
||||||
|
validLabelRegexp = defaultLabelRegexp
|
||||||
|
m = re.compile(validLabelRegexp)
|
||||||
|
|
||||||
|
for name in p4Labels:
|
||||||
|
commitFound = False
|
||||||
|
|
||||||
|
if not m.match(name):
|
||||||
|
if verbose:
|
||||||
|
print "label %s does not match regexp %s" % (name,validLabelRegexp)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if name in ignoredP4Labels:
|
||||||
|
continue
|
||||||
|
|
||||||
|
labelDetails = p4CmdList(['label', "-o", name])[0]
|
||||||
|
|
||||||
|
# get the most recent changelist for each file in this label
|
||||||
|
change = p4Cmd(["changes", "-m", "1"] + ["%s...@%s" % (p, name)
|
||||||
|
for p in self.depotPaths])
|
||||||
|
|
||||||
|
if change.has_key('change'):
|
||||||
|
# find the corresponding git commit; take the oldest commit
|
||||||
|
changelist = int(change['change'])
|
||||||
|
gitCommit = read_pipe(["git", "rev-list", "--max-count=1",
|
||||||
|
"--reverse", ":/\[git-p4:.*change = %d\]" % changelist])
|
||||||
|
if len(gitCommit) == 0:
|
||||||
|
print "could not find git commit for changelist %d" % changelist
|
||||||
|
else:
|
||||||
|
gitCommit = gitCommit.strip()
|
||||||
|
commitFound = True
|
||||||
|
# Convert from p4 time format
|
||||||
|
try:
|
||||||
|
tmwhen = time.strptime(labelDetails['Update'], "%Y/%m/%d %H:%M:%S")
|
||||||
|
except ValueError:
|
||||||
|
print "Could not convert label time %s" % labelDetail['Update']
|
||||||
|
tmwhen = 1
|
||||||
|
|
||||||
|
when = int(time.mktime(tmwhen))
|
||||||
|
self.streamTag(stream, name, labelDetails, gitCommit, when)
|
||||||
|
if verbose:
|
||||||
|
print "p4 label %s mapped to git commit %s" % (name, gitCommit)
|
||||||
|
else:
|
||||||
|
if verbose:
|
||||||
|
print "Label %s has no changelists - possibly deleted?" % name
|
||||||
|
|
||||||
|
if not commitFound:
|
||||||
|
# We can't import this label; don't try again as it will get very
|
||||||
|
# expensive repeatedly fetching all the files for labels that will
|
||||||
|
# never be imported. If the label is moved in the future, the
|
||||||
|
# ignore will need to be removed manually.
|
||||||
|
system(["git", "config", "--add", "git-p4.ignoredP4Labels", name])
|
||||||
|
|
||||||
def guessProjectName(self):
|
def guessProjectName(self):
|
||||||
for p in self.depotPaths:
|
for p in self.depotPaths:
|
||||||
if p.endswith("/"):
|
if p.endswith("/"):
|
||||||
@ -2425,7 +2595,6 @@ class P4Sync(Command, P4UserMap):
|
|||||||
|
|
||||||
self.depotPaths = newPaths
|
self.depotPaths = newPaths
|
||||||
|
|
||||||
|
|
||||||
self.loadUserMapFromCache()
|
self.loadUserMapFromCache()
|
||||||
self.labels = {}
|
self.labels = {}
|
||||||
if self.detectLabels:
|
if self.detectLabels:
|
||||||
@ -2489,22 +2658,31 @@ class P4Sync(Command, P4UserMap):
|
|||||||
if len(changes) == 0:
|
if len(changes) == 0:
|
||||||
if not self.silent:
|
if not self.silent:
|
||||||
print "No changes to import!"
|
print "No changes to import!"
|
||||||
return True
|
else:
|
||||||
|
if not self.silent and not self.detectBranches:
|
||||||
|
print "Import destination: %s" % self.branch
|
||||||
|
|
||||||
if not self.silent and not self.detectBranches:
|
self.updatedBranches = set()
|
||||||
print "Import destination: %s" % self.branch
|
|
||||||
|
|
||||||
self.updatedBranches = set()
|
self.importChanges(changes)
|
||||||
|
|
||||||
self.importChanges(changes)
|
if not self.silent:
|
||||||
|
print ""
|
||||||
|
if len(self.updatedBranches) > 0:
|
||||||
|
sys.stdout.write("Updated branches: ")
|
||||||
|
for b in self.updatedBranches:
|
||||||
|
sys.stdout.write("%s " % b)
|
||||||
|
sys.stdout.write("\n")
|
||||||
|
|
||||||
if not self.silent:
|
if gitConfig("git-p4.importLabels", "--bool") == "true":
|
||||||
print ""
|
self.importLabels = true
|
||||||
if len(self.updatedBranches) > 0:
|
|
||||||
sys.stdout.write("Updated branches: ")
|
if self.importLabels:
|
||||||
for b in self.updatedBranches:
|
p4Labels = getP4Labels(self.depotPaths)
|
||||||
sys.stdout.write("%s " % b)
|
gitTags = getGitTags()
|
||||||
sys.stdout.write("\n")
|
|
||||||
|
missingP4Labels = p4Labels - gitTags
|
||||||
|
self.importP4Labels(self.gitStream, missingP4Labels)
|
||||||
|
|
||||||
self.gitStream.close()
|
self.gitStream.close()
|
||||||
if importProcess.wait() != 0:
|
if importProcess.wait() != 0:
|
||||||
@ -2523,13 +2701,16 @@ class P4Sync(Command, P4UserMap):
|
|||||||
class P4Rebase(Command):
|
class P4Rebase(Command):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
Command.__init__(self)
|
Command.__init__(self)
|
||||||
self.options = [ ]
|
self.options = [
|
||||||
|
optparse.make_option("--import-labels", dest="importLabels", action="store_true"),
|
||||||
|
]
|
||||||
|
self.importLabels = False
|
||||||
self.description = ("Fetches the latest revision from perforce and "
|
self.description = ("Fetches the latest revision from perforce and "
|
||||||
+ "rebases the current work (branch) against it")
|
+ "rebases the current work (branch) against it")
|
||||||
self.verbose = False
|
|
||||||
|
|
||||||
def run(self, args):
|
def run(self, args):
|
||||||
sync = P4Sync()
|
sync = P4Sync()
|
||||||
|
sync.importLabels = self.importLabels
|
||||||
sync.run([])
|
sync.run([])
|
||||||
|
|
||||||
return self.rebase()
|
return self.rebase()
|
||||||
@ -2719,16 +2900,16 @@ def main():
|
|||||||
|
|
||||||
args = sys.argv[2:]
|
args = sys.argv[2:]
|
||||||
|
|
||||||
if len(options) > 0:
|
options.append(optparse.make_option("--verbose", dest="verbose", action="store_true"))
|
||||||
if cmd.needsGit:
|
if cmd.needsGit:
|
||||||
options.append(optparse.make_option("--git-dir", dest="gitdir"))
|
options.append(optparse.make_option("--git-dir", dest="gitdir"))
|
||||||
|
|
||||||
parser = optparse.OptionParser(cmd.usage.replace("%prog", "%prog " + cmdName),
|
parser = optparse.OptionParser(cmd.usage.replace("%prog", "%prog " + cmdName),
|
||||||
options,
|
options,
|
||||||
description = cmd.description,
|
description = cmd.description,
|
||||||
formatter = HelpFormatter())
|
formatter = HelpFormatter())
|
||||||
|
|
||||||
(cmd, args) = parser.parse_args(sys.argv[2:], cmd);
|
(cmd, args) = parser.parse_args(sys.argv[2:], cmd);
|
||||||
global verbose
|
global verbose
|
||||||
verbose = cmd.verbose
|
verbose = cmd.verbose
|
||||||
if cmd.needsGit:
|
if cmd.needsGit:
|
||||||
|
@ -24,6 +24,7 @@ P4DPORT=$((10669 + ($testid - $git_p4_test_start)))
|
|||||||
|
|
||||||
export P4PORT=localhost:$P4DPORT
|
export P4PORT=localhost:$P4DPORT
|
||||||
export P4CLIENT=client
|
export P4CLIENT=client
|
||||||
|
export P4EDITOR=:
|
||||||
|
|
||||||
db="$TRASH_DIRECTORY/db"
|
db="$TRASH_DIRECTORY/db"
|
||||||
cli="$TRASH_DIRECTORY/cli"
|
cli="$TRASH_DIRECTORY/cli"
|
||||||
|
@ -335,7 +335,7 @@ test_expect_success 'detect renames' '
|
|||||||
test_when_finished cleanup_git &&
|
test_when_finished cleanup_git &&
|
||||||
(
|
(
|
||||||
cd "$git" &&
|
cd "$git" &&
|
||||||
git config git-p4.skipSubmitEditCheck true &&
|
git config git-p4.skipSubmitEdit true &&
|
||||||
|
|
||||||
git mv file1 file4 &&
|
git mv file1 file4 &&
|
||||||
git commit -a -m "Rename file1 to file4" &&
|
git commit -a -m "Rename file1 to file4" &&
|
||||||
@ -394,7 +394,7 @@ test_expect_success 'detect copies' '
|
|||||||
test_when_finished cleanup_git &&
|
test_when_finished cleanup_git &&
|
||||||
(
|
(
|
||||||
cd "$git" &&
|
cd "$git" &&
|
||||||
git config git-p4.skipSubmitEditCheck true &&
|
git config git-p4.skipSubmitEdit true &&
|
||||||
|
|
||||||
cp file2 file8 &&
|
cp file2 file8 &&
|
||||||
git add file8 &&
|
git add file8 &&
|
||||||
|
@ -91,7 +91,7 @@ test_expect_success 'no config, edited' '
|
|||||||
cd "$git" &&
|
cd "$git" &&
|
||||||
echo line >>file1 &&
|
echo line >>file1 &&
|
||||||
git commit -a -m "change 5" &&
|
git commit -a -m "change 5" &&
|
||||||
EDITOR="\"$ed\"" git p4 submit &&
|
P4EDITOR="" EDITOR="\"$ed\"" git p4 submit &&
|
||||||
p4 changes //depot/... >wc &&
|
p4 changes //depot/... >wc &&
|
||||||
test_line_count = 5 wc
|
test_line_count = 5 wc
|
||||||
)
|
)
|
||||||
|
202
t/t9811-git-p4-label-import.sh
Executable file
202
t/t9811-git-p4-label-import.sh
Executable file
@ -0,0 +1,202 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
test_description='git p4 label tests'
|
||||||
|
|
||||||
|
. ./lib-git-p4.sh
|
||||||
|
|
||||||
|
test_expect_success 'start p4d' '
|
||||||
|
start_p4d
|
||||||
|
'
|
||||||
|
|
||||||
|
# Basic p4 label import tests.
|
||||||
|
#
|
||||||
|
test_expect_success 'basic p4 labels' '
|
||||||
|
test_when_finished cleanup_git &&
|
||||||
|
(
|
||||||
|
cd "$cli" &&
|
||||||
|
mkdir -p main &&
|
||||||
|
|
||||||
|
echo f1 >main/f1 &&
|
||||||
|
p4 add main/f1 &&
|
||||||
|
p4 submit -d "main/f1" &&
|
||||||
|
|
||||||
|
echo f2 >main/f2 &&
|
||||||
|
p4 add main/f2 &&
|
||||||
|
p4 submit -d "main/f2" &&
|
||||||
|
|
||||||
|
echo f3 >main/file_with_\$metachar &&
|
||||||
|
p4 add main/file_with_\$metachar &&
|
||||||
|
p4 submit -d "file with metachar" &&
|
||||||
|
|
||||||
|
p4 tag -l TAG_F1_ONLY main/f1 &&
|
||||||
|
p4 tag -l TAG_WITH\$_SHELL_CHAR main/... &&
|
||||||
|
p4 tag -l this_tag_will_be\ skipped main/... &&
|
||||||
|
|
||||||
|
echo f4 >main/f4 &&
|
||||||
|
p4 add main/f4 &&
|
||||||
|
p4 submit -d "main/f4" &&
|
||||||
|
|
||||||
|
p4 label -i <<-EOF &&
|
||||||
|
Label: TAG_LONG_LABEL
|
||||||
|
Description:
|
||||||
|
A Label first line
|
||||||
|
A Label second line
|
||||||
|
View: //depot/...
|
||||||
|
EOF
|
||||||
|
|
||||||
|
p4 tag -l TAG_LONG_LABEL ... &&
|
||||||
|
|
||||||
|
p4 labels ... &&
|
||||||
|
|
||||||
|
git p4 clone --dest="$git" //depot@all &&
|
||||||
|
cd "$git" &&
|
||||||
|
git config git-p4.labelImportRegexp ".*TAG.*" &&
|
||||||
|
git p4 sync --import-labels --verbose &&
|
||||||
|
|
||||||
|
git tag &&
|
||||||
|
git tag >taglist &&
|
||||||
|
test_line_count = 3 taglist &&
|
||||||
|
|
||||||
|
cd main &&
|
||||||
|
git checkout TAG_F1_ONLY &&
|
||||||
|
! test -f f2 &&
|
||||||
|
git checkout TAG_WITH\$_SHELL_CHAR &&
|
||||||
|
test -f f1 && test -f f2 && test -f file_with_\$metachar &&
|
||||||
|
|
||||||
|
git show TAG_LONG_LABEL | grep -q "A Label second line"
|
||||||
|
)
|
||||||
|
'
|
||||||
|
# Test some label corner cases:
|
||||||
|
#
|
||||||
|
# - two tags on the same file; both should be available
|
||||||
|
# - a tag that is only on one file; this kind of tag
|
||||||
|
# cannot be imported (at least not easily).
|
||||||
|
|
||||||
|
test_expect_success 'two labels on the same changelist' '
|
||||||
|
test_when_finished cleanup_git &&
|
||||||
|
(
|
||||||
|
cd "$cli" &&
|
||||||
|
mkdir -p main &&
|
||||||
|
|
||||||
|
p4 edit main/f1 main/f2 &&
|
||||||
|
echo "hello world" >main/f1 &&
|
||||||
|
echo "not in the tag" >main/f2 &&
|
||||||
|
p4 submit -d "main/f[12]: testing two labels" &&
|
||||||
|
|
||||||
|
p4 tag -l TAG_F1_1 main/... &&
|
||||||
|
p4 tag -l TAG_F1_2 main/... &&
|
||||||
|
|
||||||
|
p4 labels ... &&
|
||||||
|
|
||||||
|
git p4 clone --dest="$git" //depot@all &&
|
||||||
|
cd "$git" &&
|
||||||
|
git p4 sync --import-labels &&
|
||||||
|
|
||||||
|
git tag | grep TAG_F1 &&
|
||||||
|
git tag | grep -q TAG_F1_1 &&
|
||||||
|
git tag | grep -q TAG_F1_2 &&
|
||||||
|
|
||||||
|
cd main &&
|
||||||
|
|
||||||
|
git checkout TAG_F1_1 &&
|
||||||
|
ls &&
|
||||||
|
test -f f1 &&
|
||||||
|
|
||||||
|
git checkout TAG_F1_2 &&
|
||||||
|
ls &&
|
||||||
|
test -f f1
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
# Export some git tags to p4
|
||||||
|
test_expect_success 'export git tags to p4' '
|
||||||
|
test_when_finished cleanup_git &&
|
||||||
|
git p4 clone --dest="$git" //depot@all &&
|
||||||
|
(
|
||||||
|
cd "$git" &&
|
||||||
|
git tag -m "A tag created in git:xyzzy" GIT_TAG_1 &&
|
||||||
|
echo "hello world" >main/f10 &&
|
||||||
|
git add main/f10 &&
|
||||||
|
git commit -m "Adding file for export test" &&
|
||||||
|
git config git-p4.skipSubmitEdit true &&
|
||||||
|
git p4 submit &&
|
||||||
|
git tag -m "Another git tag" GIT_TAG_2 &&
|
||||||
|
git tag LIGHTWEIGHT_TAG &&
|
||||||
|
git p4 rebase --import-labels --verbose &&
|
||||||
|
git p4 submit --export-labels --verbose
|
||||||
|
) &&
|
||||||
|
(
|
||||||
|
cd "$cli" &&
|
||||||
|
p4 sync ... &&
|
||||||
|
p4 labels ... | grep GIT_TAG_1 &&
|
||||||
|
p4 labels ... | grep GIT_TAG_2 &&
|
||||||
|
p4 labels ... | grep LIGHTWEIGHT_TAG &&
|
||||||
|
p4 label -o GIT_TAG_1 | grep "tag created in git:xyzzy" &&
|
||||||
|
p4 sync ...@GIT_TAG_1 &&
|
||||||
|
! test -f main/f10
|
||||||
|
p4 sync ...@GIT_TAG_2 &&
|
||||||
|
test -f main/f10
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
# Export a tag from git where an affected file is deleted later on
|
||||||
|
# Need to create git tags after rebase, since only then can the
|
||||||
|
# git commits be mapped to p4 changelists.
|
||||||
|
test_expect_success 'export git tags to p4 with deletion' '
|
||||||
|
test_when_finished cleanup_git &&
|
||||||
|
git p4 clone --dest="$git" //depot@all &&
|
||||||
|
(
|
||||||
|
cd "$git" &&
|
||||||
|
git p4 sync --import-labels &&
|
||||||
|
echo "deleted file" >main/deleted_file &&
|
||||||
|
git add main/deleted_file &&
|
||||||
|
git commit -m "create deleted file" &&
|
||||||
|
git rm main/deleted_file &&
|
||||||
|
echo "new file" >main/f11 &&
|
||||||
|
git add main/f11 &&
|
||||||
|
git commit -m "delete the deleted file" &&
|
||||||
|
git config git-p4.skipSubmitEdit true &&
|
||||||
|
git p4 submit &&
|
||||||
|
git p4 rebase --import-labels --verbose &&
|
||||||
|
git tag -m "tag on deleted file" GIT_TAG_ON_DELETED HEAD~1 &&
|
||||||
|
git tag -m "tag after deletion" GIT_TAG_AFTER_DELETION HEAD &&
|
||||||
|
git p4 submit --export-labels --verbose
|
||||||
|
) &&
|
||||||
|
(
|
||||||
|
cd "$cli" &&
|
||||||
|
p4 sync ... &&
|
||||||
|
p4 sync ...@GIT_TAG_ON_DELETED &&
|
||||||
|
test -f main/deleted_file &&
|
||||||
|
p4 sync ...@GIT_TAG_AFTER_DELETION &&
|
||||||
|
! test -f main/deleted_file &&
|
||||||
|
echo "checking label contents" &&
|
||||||
|
p4 label -o GIT_TAG_ON_DELETED | grep "tag on deleted file"
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
# Create a tag in git that cannot be exported to p4
|
||||||
|
test_expect_success 'tag that cannot be exported' '
|
||||||
|
test_when_finished cleanup_git &&
|
||||||
|
git p4 clone --dest="$git" //depot@all &&
|
||||||
|
(
|
||||||
|
cd "$git" &&
|
||||||
|
git checkout -b a_branch &&
|
||||||
|
echo "hello" >main/f12 &&
|
||||||
|
git add main/f12 &&
|
||||||
|
git commit -m "adding f12" &&
|
||||||
|
git tag -m "tag on a_branch" GIT_TAG_ON_A_BRANCH &&
|
||||||
|
git checkout master &&
|
||||||
|
git p4 submit --export-labels
|
||||||
|
) &&
|
||||||
|
(
|
||||||
|
cd "$cli" &&
|
||||||
|
p4 sync ... &&
|
||||||
|
!(p4 labels | grep GIT_TAG_ON_A_BRANCH)
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'kill p4d' '
|
||||||
|
kill_p4d
|
||||||
|
'
|
||||||
|
|
||||||
|
test_done
|
Loading…
Reference in New Issue
Block a user