git p4: import/export of labels to/from p4

The existing label import code looks at each commit being imported, and then checks for labels at that commit. This doesn't work in the real world though because it will drop labels applied on changelists that have already been imported, a common pattern. This change adds a new --import-labels option. With this option, at the end of the sync, git p4 gets sets of labels in p4 and git, and then creates a git tag for each missing p4 label. This means that tags created on older changelists are still imported. Tags that could not be imported are added to an ignore list. The same sets of git and p4 tags and labels can also be used to derive a list of git tags to export to p4. This is enabled with --export-labels in 'git p4 submit'. Signed-off-by: Luke Diamand <luke@diamand.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Luke Diamand committed Apr 11, 2012 at 17:21 UTC 06804c76e8f7a61c7c9473decdf9b210d3238e9d
3 files changed +468 -39
Documentation/git-p4.txt
+38 -5
@@ -198,7 +198,11 @@ git repository:
198
199 --detect-labels::
200 Query p4 for labels associated with the depot paths, and add
201 - them as tags in git.
201 + them as tags in git. Limited usefulness as only imports labels
202 + associated with new changelists. Deprecated.
203 +
204 +--import-labels::
205 + Import labels from p4 into git.
206
207 --import-local::
208 By default, p4 branches are stored in 'refs/remotes/p4/',
@@ -263,6 +267,16 @@ These options can be used to modify 'git p4 submit' behavior.
267 Re-author p4 changes before submitting to p4. This option
268 requires p4 admin privileges.
269
270 +--export-labels:
271 + Export tags from git as p4 labels. Tags found in git are applied
272 + to the perforce working directory.
273 +
274 +Rebase options
275 +~~~~~~~~~~~~~~
276 +These options can be used to modify 'git p4 rebase' behavior.
277 +
278 +--import-labels::
279 + Import p4 labels.
280
281 DEPOT PATH SYNTAX
282 -----------------
@@ -427,6 +441,18 @@ git-p4.branchList::
441 enabled. Each entry should be a pair of branch names separated
442 by a colon (:). This example declares that both branchA and
443 branchB were created from main:
444 +
445 +git-p4.ignoredP4Labels::
446 + List of p4 labels to ignore. This is built automatically as
447 + unimportable labels are discovered.
448 +
449 +git-p4.importLabels::
450 + Import p4 labels into git, as per --import-labels.
451 +
452 +git-p4.validLabelRegexp::
453 + Only p4 labels matching this regular expression will be imported. The
454 + default value is '[A-Z0-9_\-.]+$'.
455 +
456 -------------
457 git config git-p4.branchList main:branchA
458 git config --add git-p4.branchList main:branchB
@@ -481,10 +507,17 @@ git-p4.skipUserNameCheck::
507 submission regardless.
508
509 git-p4.attemptRCSCleanup:
484 - If enabled, 'git p4 submit' will attempt to cleanup RCS keywords
485 - ($Header$, etc). These would otherwise cause merge conflicts and prevent
486 - the submit going ahead. This option should be considered experimental at
487 - present.
510 + If enabled, 'git p4 submit' will attempt to cleanup RCS keywords
511 + ($Header$, etc). These would otherwise cause merge conflicts and prevent
512 + the submit going ahead. This option should be considered experimental at
513 + present.
514 +
515 +git-p4.exportLabels::
516 + Export git tags to p4 labels, as per --export-labels.
517 +
518 +git-p4.validLabelRegexp::
519 + Only p4 labels matching this regular expression will be exported. The
520 + default value is '[A-Z0-9_\-.]+$'.
521
522 IMPLEMENTATION DETAILS
523 ----------------------
git-p4.py
+228 -34
@@ -14,6 +14,8 @@ import re, shutil
14
15 verbose = False
16
17 +# Only labels/tags matching this will be imported/exported
18 +defaultLabelRegexp = r'[A-Z0-9_\-.]+$'
19
20 def p4_build_cmd(cmd):
21 """Build a suitable p4 command line.
@@ -253,6 +255,26 @@ def getP4OpenedType(file):
255 else:
256 die("Could not determine file type for %s (result: '%s')" % (file, result))
257
258 +# Return the set of all p4 labels
259 +def getP4Labels(depotPaths):
260 + labels = set()
261 + if isinstance(depotPaths,basestring):
262 + depotPaths = [depotPaths]
263 +
264 + for l in p4CmdList(["labels"] + ["%s..." % p for p in depotPaths]):
265 + label = l['label']
266 + labels.add(label)
267 +
268 + return labels
269 +
270 +# Return the set of all git tags
271 +def getGitTags():
272 + gitTags = set()
273 + for line in read_pipe_lines(["git", "tag"]):
274 + tag = line.strip()
275 + gitTags.add(tag)
276 + return gitTags
277 +
278 def diffTreePattern():
279 # This is a simple generator for the diff tree regex pattern. This could be
280 # a class variable if this and parseDiffTreeEntry were a part of a class.
@@ -792,6 +814,7 @@ class P4Submit(Command, P4UserMap):
814 optparse.make_option("-M", dest="detectRenames", action="store_true"),
815 # preserve the user, requires relevant p4 permissions
816 optparse.make_option("--preserve-user", dest="preserveUser", action="store_true"),
817 + optparse.make_option("--export-labels", dest="exportLabels", action="store_true"),
818 ]
819 self.description = "Submit changes from git to the perforce depot."
820 self.usage += " [name of git branch to submit into perforce depot]"
@@ -801,6 +824,7 @@ class P4Submit(Command, P4UserMap):
824 self.verbose = False
825 self.preserveUser = gitConfig("git-p4.preserveUser").lower() == "true"
826 self.isWindows = (platform.system() == "Windows")
827 + self.exportLabels = False
828
829 def check(self):
830 if len(p4CmdList("opened ...")) > 0:
@@ -1228,6 +1252,73 @@ class P4Submit(Command, P4UserMap):
1252 + "Please review/edit and then use p4 submit -i < %s to submit directly!"
1253 % (fileName, fileName))
1254
1255 + # Export git tags as p4 labels. Create a p4 label and then tag
1256 + # with that.
1257 + def exportGitTags(self, gitTags):
1258 + validTagRegexp = gitConfig("git-p4.validTagRegexp")
1259 + if len(validTagRegexp) == 0:
1260 + validTagRegexp = defaultLabelRegexp
1261 + m = re.compile(validTagRegexp)
1262 + commit_re = re.compile(r'\s*\[git-p4:.*change = (\d+)\s*\]')
1263 +
1264 + for name in gitTags:
1265 +
1266 + if not m.match(name):
1267 + if verbose:
1268 + print "tag %s does not match regexp %s" % (name, validTagRegexp)
1269 + continue
1270 +
1271 + # Get the p4 commit this corresponds to
1272 + changelist = None
1273 + for l in read_pipe_lines(["git", "log", "--max-count=1", name]):
1274 + match = commit_re.match(l)
1275 + if match:
1276 + changelist = match.group(1)
1277 +
1278 + if not changelist:
1279 + # a tag pointing to something not sent to p4; ignore
1280 + if verbose:
1281 + print "git tag %s does not give a p4 commit" % name
1282 + continue
1283 +
1284 + # Get the tag details.
1285 + inHeader = True
1286 + isAnnotated = False
1287 + body = []
1288 + for l in read_pipe_lines(["git", "cat-file", "-p", name]):
1289 + l = l.strip()
1290 + if inHeader:
1291 + if re.match(r'tag\s+', l):
1292 + isAnnotated = True
1293 + elif re.match(r'\s*$', l):
1294 + inHeader = False
1295 + continue
1296 + else:
1297 + body.append(l)
1298 +
1299 + if not isAnnotated:
1300 + body = ["lightweight tag imported by git p4\n"]
1301 +
1302 + # Create the label - use the same view as the client spec we are using
1303 + clientSpec = getClientSpec()
1304 +
1305 + labelTemplate = "Label: %s\n" % name
1306 + labelTemplate += "Description:\n"
1307 + for b in body:
1308 + labelTemplate += "\t" + b + "\n"
1309 + labelTemplate += "View:\n"
1310 + for mapping in clientSpec.mappings:
1311 + labelTemplate += "\t%s\n" % mapping.depot_side.path
1312 +
1313 + p4_write_pipe(["label", "-i"], labelTemplate)
1314 +
1315 + # Use the label
1316 + p4_system(["tag", "-l", name] +
1317 + ["%s@%s" % (mapping.depot_side.path, changelist) for mapping in clientSpec.mappings])
1318 +
1319 + if verbose:
1320 + print "created p4 label for tag %s" % name
1321 +
1322 def run(self, args):
1323 if len(args) == 0:
1324 self.master = currentGitBranch()
@@ -1317,6 +1408,16 @@ class P4Submit(Command, P4UserMap):
1408 rebase = P4Rebase()
1409 rebase.rebase()
1410
1411 + if gitConfig("git-p4.exportLabels", "--bool") == "true":
1412 + self.exportLabels = true
1413 +
1414 + if self.exportLabels:
1415 + p4Labels = getP4Labels(self.depotPath)
1416 + gitTags = getGitTags()
1417 +
1418 + missingGitTags = gitTags - p4Labels
1419 + self.exportGitTags(missingGitTags)
1420 +
1421 return True
1422
1423 class View(object):
@@ -1544,6 +1645,7 @@ class P4Sync(Command, P4UserMap):
1645 optparse.make_option("--changesfile", dest="changesFile"),
1646 optparse.make_option("--silent", dest="silent", action="store_true"),
1647 optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"),
1648 + optparse.make_option("--import-labels", dest="importLabels", action="store_true"),
1649 optparse.make_option("--verbose", dest="verbose", action="store_true"),
1650 optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false",
1651 help="Import into refs/heads/ , not refs/remotes"),
@@ -1568,6 +1670,7 @@ class P4Sync(Command, P4UserMap):
1670 self.branch = ""
1671 self.detectBranches = False
1672 self.detectLabels = False
1673 + self.importLabels = False
1674 self.changesFile = ""
1675 self.syncWithOrigin = True
1676 self.verbose = False
@@ -1829,6 +1932,38 @@ class P4Sync(Command, P4UserMap):
1932 else:
1933 return "%s <a@b>" % userid
1934
1935 + # Stream a p4 tag
1936 + def streamTag(self, gitStream, labelName, labelDetails, commit, epoch):
1937 + if verbose:
1938 + print "writing tag %s for commit %s" % (labelName, commit)
1939 + gitStream.write("tag %s\n" % labelName)
1940 + gitStream.write("from %s\n" % commit)
1941 +
1942 + if labelDetails.has_key('Owner'):
1943 + owner = labelDetails["Owner"]
1944 + else:
1945 + owner = None
1946 +
1947 + # Try to use the owner of the p4 label, or failing that,
1948 + # the current p4 user id.
1949 + if owner:
1950 + email = self.make_email(owner)
1951 + else:
1952 + email = self.make_email(self.p4UserId())
1953 + tagger = "%s %s %s" % (email, epoch, self.tz)
1954 +
1955 + gitStream.write("tagger %s\n" % tagger)
1956 +
1957 + print "labelDetails=",labelDetails
1958 + if labelDetails.has_key('Description'):
1959 + description = labelDetails['Description']
1960 + else:
1961 + description = 'Label from git p4'
1962 +
1963 + gitStream.write("data %d\n" % len(description))
1964 + gitStream.write(description)
1965 + gitStream.write("\n")
1966 +
1967 def commit(self, details, files, branch, branchPrefixes, parent = ""):
1968 epoch = details["time"]
1969 author = details["user"]
@@ -1893,25 +2028,7 @@ class P4Sync(Command, P4UserMap):
2028 cleanedFiles[info["depotFile"]] = info["rev"]
2029
2030 if cleanedFiles == labelRevisions:
1896 - self.gitStream.write("tag tag_%s\n" % labelDetails["label"])
1897 - self.gitStream.write("from %s\n" % branch)
1898 -
1899 - owner = labelDetails["Owner"]
1900 -
1901 - # Try to use the owner of the p4 label, or failing that,
1902 - # the current p4 user id.
1903 - if owner:
1904 - email = self.make_email(owner)
1905 - else:
1906 - email = self.make_email(self.p4UserId())
1907 - tagger = "%s %s %s" % (email, epoch, self.tz)
1908 -
1909 - self.gitStream.write("tagger %s\n" % tagger)
1910 -
1911 - description = labelDetails["Description"]
1912 - self.gitStream.write("data %d\n" % len(description))
1913 - self.gitStream.write(description)
1914 - self.gitStream.write("\n")
2031 + self.streamTag(self.gitStream, 'tag_%s' % labelDetails['label'], labelDetails, branch, epoch)
2032
2033 else:
2034 if not self.silent:
@@ -1923,6 +2040,7 @@ class P4Sync(Command, P4UserMap):
2040 print ("Tag %s does not match with change %s: file count is different."
2041 % (labelDetails["label"], change))
2042
2043 + # Build a dictionary of changelists and labels, for "detect-labels" option.
2044 def getLabels(self):
2045 self.labels = {}
2046
@@ -1949,6 +2067,69 @@ class P4Sync(Command, P4UserMap):
2067 if self.verbose:
2068 print "Label changes: %s" % self.labels.keys()
2069
2070 + # Import p4 labels as git tags. A direct mapping does not
2071 + # exist, so assume that if all the files are at the same revision
2072 + # then we can use that, or it's something more complicated we should
2073 + # just ignore.
2074 + def importP4Labels(self, stream, p4Labels):
2075 + if verbose:
2076 + print "import p4 labels: " + ' '.join(p4Labels)
2077 +
2078 + ignoredP4Labels = gitConfigList("git-p4.ignoredP4Labels")
2079 + validLabelRegexp = gitConfig("git-p4.validLabelRegexp")
2080 + if len(validLabelRegexp) == 0:
2081 + validLabelRegexp = defaultLabelRegexp
2082 + m = re.compile(validLabelRegexp)
2083 +
2084 + for name in p4Labels:
2085 + commitFound = False
2086 +
2087 + if not m.match(name):
2088 + if verbose:
2089 + print "label %s does not match regexp %s" % (name,validLabelRegexp)
2090 + continue
2091 +
2092 + if name in ignoredP4Labels:
2093 + continue
2094 +
2095 + labelDetails = p4CmdList(['label', "-o", name])[0]
2096 +
2097 + # get the most recent changelist for each file in this label
2098 + change = p4Cmd(["changes", "-m", "1"] + ["%s...@%s" % (p, name)
2099 + for p in self.depotPaths])
2100 +
2101 + if change.has_key('change'):
2102 + # find the corresponding git commit; take the oldest commit
2103 + changelist = int(change['change'])
2104 + gitCommit = read_pipe(["git", "rev-list", "--max-count=1",
2105 + "--reverse", ":/\[git-p4:.*change = %d\]" % changelist])
2106 + if len(gitCommit) == 0:
2107 + print "could not find git commit for changelist %d" % changelist
2108 + else:
2109 + gitCommit = gitCommit.strip()
2110 + commitFound = True
2111 + # Convert from p4 time format
2112 + try:
2113 + tmwhen = time.strptime(labelDetails['Update'], "%Y/%m/%d %H:%M:%S")
2114 + except ValueError:
2115 + print "Could not convert label time %s" % labelDetail['Update']
2116 + tmwhen = 1
2117 +
2118 + when = int(time.mktime(tmwhen))
2119 + self.streamTag(stream, name, labelDetails, gitCommit, when)
2120 + if verbose:
2121 + print "p4 label %s mapped to git commit %s" % (name, gitCommit)
2122 + else:
2123 + if verbose:
2124 + print "Label %s has no changelists - possibly deleted?" % name
2125 +
2126 + if not commitFound:
2127 + # We can't import this label; don't try again as it will get very
2128 + # expensive repeatedly fetching all the files for labels that will
2129 + # never be imported. If the label is moved in the future, the
2130 + # ignore will need to be removed manually.
2131 + system(["git", "config", "--add", "git-p4.ignoredP4Labels", name])
2132 +
2133 def guessProjectName(self):
2134 for p in self.depotPaths:
2135 if p.endswith("/"):
@@ -2425,7 +2606,6 @@ class P4Sync(Command, P4UserMap):
2606
2607 self.depotPaths = newPaths
2608
2428 -
2609 self.loadUserMapFromCache()
2610 self.labels = {}
2611 if self.detectLabels:
@@ -2489,22 +2669,31 @@ class P4Sync(Command, P4UserMap):
2669 if len(changes) == 0:
2670 if not self.silent:
2671 print "No changes to import!"
2492 - return True
2672 + else:
2673 + if not self.silent and not self.detectBranches:
2674 + print "Import destination: %s" % self.branch
2675
2494 - if not self.silent and not self.detectBranches:
2495 - print "Import destination: %s" % self.branch
2676 + self.updatedBranches = set()
2677
2497 - self.updatedBranches = set()
2678 + self.importChanges(changes)
2679
2499 - self.importChanges(changes)
2680 + if not self.silent:
2681 + print ""
2682 + if len(self.updatedBranches) > 0:
2683 + sys.stdout.write("Updated branches: ")
2684 + for b in self.updatedBranches:
2685 + sys.stdout.write("%s " % b)
2686 + sys.stdout.write("\n")
2687
2501 - if not self.silent:
2502 - print ""
2503 - if len(self.updatedBranches) > 0:
2504 - sys.stdout.write("Updated branches: ")
2505 - for b in self.updatedBranches:
2506 - sys.stdout.write("%s " % b)
2507 - sys.stdout.write("\n")
2688 + if gitConfig("git-p4.importLabels", "--bool") == "true":
2689 + self.importLabels = true
2690 +
2691 + if self.importLabels:
2692 + p4Labels = getP4Labels(self.depotPaths)
2693 + gitTags = getGitTags()
2694 +
2695 + missingP4Labels = p4Labels - gitTags
2696 + self.importP4Labels(self.gitStream, missingP4Labels)
2697
2698 self.gitStream.close()
2699 if importProcess.wait() != 0:
@@ -2523,13 +2712,18 @@ class P4Sync(Command, P4UserMap):
2712 class P4Rebase(Command):
2713 def __init__(self):
2714 Command.__init__(self)
2526 - self.options = [ ]
2715 + self.options = [
2716 + optparse.make_option("--import-labels", dest="importLabels", action="store_true"),
2717 + optparse.make_option("--verbose", dest="verbose", action="store_true"),
2718 + ]
2719 + self.verbose = False
2720 + self.importLabels = False
2721 self.description = ("Fetches the latest revision from perforce and "
2722 + "rebases the current work (branch) against it")
2529 - self.verbose = False
2723
2724 def run(self, args):
2725 sync = P4Sync()
2726 + sync.importLabels = self.importLabels
2727 sync.run([])
2728
2729 return self.rebase()
t/t9811-git-p4-label-import.sh new
+202
@@ -0,0 +1,202 @@
1 +#!/bin/sh
2 +
3 +test_description='git p4 label tests'
4 +
5 +. ./lib-git-p4.sh
6 +
7 +test_expect_success 'start p4d' '
8 + start_p4d
9 +'
10 +
11 +# Basic p4 label import tests.
12 +#
13 +test_expect_success 'basic p4 labels' '
14 + test_when_finished cleanup_git &&
15 + (
16 + cd "$cli" &&
17 + mkdir -p main &&
18 +
19 + echo f1 >main/f1 &&
20 + p4 add main/f1 &&
21 + p4 submit -d "main/f1" &&
22 +
23 + echo f2 >main/f2 &&
24 + p4 add main/f2 &&
25 + p4 submit -d "main/f2" &&
26 +
27 + echo f3 >main/file_with_\$metachar &&
28 + p4 add main/file_with_\$metachar &&
29 + p4 submit -d "file with metachar" &&
30 +
31 + p4 tag -l TAG_F1_ONLY main/f1 &&
32 + p4 tag -l TAG_WITH\$_SHELL_CHAR main/... &&
33 + p4 tag -l this_tag_will_be_skipped main/... &&
34 +
35 + echo f4 >main/f4 &&
36 + p4 add main/f4 &&
37 + p4 submit -d "main/f4" &&
38 +
39 + p4 label -i <<-EOF &&
40 + Label: TAG_LONG_LABEL
41 + Description:
42 + A Label first line
43 + A Label second line
44 + View: //depot/...
45 + EOF
46 +
47 + p4 tag -l TAG_LONG_LABEL ... &&
48 +
49 + p4 labels ... &&
50 +
51 + git p4 clone --dest="$git" //depot@all &&
52 + cd "$git" &&
53 + git config git-p4.validLabelRegexp ".*TAG.*" &&
54 + git p4 sync --import-labels --verbose &&
55 +
56 + git tag &&
57 + git tag >taglist &&
58 + test_line_count = 3 taglist &&
59 +
60 + cd main &&
61 + git checkout TAG_F1_ONLY &&
62 + ! test -f f2 &&
63 + git checkout TAG_WITH\$_SHELL_CHAR &&
64 + test -f f1 && test -f f2 && test -f file_with_\$metachar &&
65 +
66 + git show TAG_LONG_LABEL | grep -q "A Label second line"
67 + )
68 +'
69 +# Test some label corner cases:
70 +#
71 +# - two tags on the same file; both should be available
72 +# - a tag that is only on one file; this kind of tag
73 +# cannot be imported (at least not easily).
74 +
75 +test_expect_success 'two labels on the same changelist' '
76 + test_when_finished cleanup_git &&
77 + (
78 + cd "$cli" &&
79 + mkdir -p main &&
80 +
81 + p4 edit main/f1 main/f2 &&
82 + echo "hello world" >main/f1 &&
83 + echo "not in the tag" >main/f2 &&
84 + p4 submit -d "main/f[12]: testing two labels" &&
85 +
86 + p4 tag -l TAG_F1_1 main/... &&
87 + p4 tag -l TAG_F1_2 main/... &&
88 +
89 + p4 labels ... &&
90 +
91 + git p4 clone --dest="$git" //depot@all &&
92 + cd "$git" &&
93 + git p4 sync --import-labels &&
94 +
95 + git tag | grep TAG_F1 &&
96 + git tag | grep -q TAG_F1_1 &&
97 + git tag | grep -q TAG_F1_2 &&
98 +
99 + cd main &&
100 +
101 + git checkout TAG_F1_1 &&
102 + ls &&
103 + test -f f1 &&
104 +
105 + git checkout TAG_F1_2 &&
106 + ls &&
107 + test -f f1
108 + )
109 +'
110 +
111 +# Export some git tags to p4
112 +test_expect_success 'export git tags to p4' '
113 + test_when_finished cleanup_git &&
114 + git p4 clone --dest="$git" //depot@all &&
115 + (
116 + cd "$git" &&
117 + git tag -m "A tag created in git:xyzzy" GIT_TAG_1 &&
118 + echo "hello world" >main/f10 &&
119 + git add main/f10 &&
120 + git commit -m "Adding file for export test" &&
121 + git config git-p4.skipSubmitEdit true &&
122 + git p4 submit &&
123 + git tag -m "Another git tag" GIT_TAG_2 &&
124 + git tag LIGHTWEIGHT_TAG &&
125 + git p4 rebase --import-labels --verbose &&
126 + git p4 submit --export-labels --verbose
127 + ) &&
128 + (
129 + cd "$cli" &&
130 + p4 sync ... &&
131 + p4 labels ... | grep GIT_TAG_1 &&
132 + p4 labels ... | grep GIT_TAG_2 &&
133 + p4 labels ... | grep LIGHTWEIGHT_TAG &&
134 + p4 label -o GIT_TAG_1 | grep "tag created in git:xyzzy" &&
135 + p4 sync ...@GIT_TAG_1 &&
136 + ! test -f main/f10
137 + p4 sync ...@GIT_TAG_2 &&
138 + test -f main/f10
139 + )
140 +'
141 +
142 +# Export a tag from git where an affected file is deleted later on
143 +# Need to create git tags after rebase, since only then can the
144 +# git commits be mapped to p4 changelists.
145 +test_expect_success 'export git tags to p4 with deletion' '
146 + test_when_finished cleanup_git &&
147 + git p4 clone --dest="$git" //depot@all &&
148 + (
149 + cd "$git" &&
150 + git p4 sync --import-labels &&
151 + echo "deleted file" >main/deleted_file &&
152 + git add main/deleted_file &&
153 + git commit -m "create deleted file" &&
154 + git rm main/deleted_file &&
155 + echo "new file" >main/f11 &&
156 + git add main/f11 &&
157 + git commit -m "delete the deleted file" &&
158 + git config git-p4.skipSubmitEdit true &&
159 + git p4 submit &&
160 + git p4 rebase --import-labels --verbose &&
161 + git tag -m "tag on deleted file" GIT_TAG_ON_DELETED HEAD~1 &&
162 + git tag -m "tag after deletion" GIT_TAG_AFTER_DELETION HEAD &&
163 + git p4 submit --export-labels --verbose
164 + ) &&
165 + (
166 + cd "$cli" &&
167 + p4 sync ... &&
168 + p4 sync ...@GIT_TAG_ON_DELETED &&
169 + test -f main/deleted_file &&
170 + p4 sync ...@GIT_TAG_AFTER_DELETION &&
171 + ! test -f main/deleted_file &&
172 + echo "checking label contents" &&
173 + p4 label -o GIT_TAG_ON_DELETED | grep "tag on deleted file"
174 + )
175 +'
176 +
177 +# Create a tag in git that cannot be exported to p4
178 +test_expect_success 'tag that cannot be exported' '
179 + test_when_finished cleanup_git &&
180 + git p4 clone --dest="$git" //depot@all &&
181 + (
182 + cd "$git" &&
183 + git checkout -b a_branch &&
184 + echo "hello" >main/f12 &&
185 + git add main/f12 &&
186 + git commit -m "adding f12" &&
187 + git tag -m "tag on a_branch" GIT_TAG_ON_A_BRANCH &&
188 + git checkout master &&
189 + git p4 submit --export-labels
190 + ) &&
191 + (
192 + cd "$cli" &&
193 + p4 sync ... &&
194 + !(p4 labels | grep GIT_TAG_ON_A_BRANCH)
195 + )
196 +'
197 +
198 +test_expect_success 'kill p4d' '
199 + kill_p4d
200 +'
201 +
202 +test_done