git-p4: fix submit regression with clientSpec and subdir clone

When the --use-client-spec is given to clone, and the clone path is a subset of the full tree as specified in the client, future submits will go to the wrong place. Factor out getClientSpec() so both clone/sync and submit can use it. Introduce getClientRoot() that is needed for the client spec case, and use it instead of p4Where(). Test the five possible submit behaviors (add, modify, rename, copy, delete). Reported-by: Laurent Charrière <lcharriere@promptu.com> Signed-off-by: Pete Wyckoff <pw@padd.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Pete Wyckoff committed Feb 25, 2012 at 20:06 UTC 543987bd475abae502939eb3db9c6879fde09c88
2 files changed +185 -43
contrib/fast-import/git-p4
+54 -32
@@ -555,6 +555,46 @@ def p4PathStartsWith(path, prefix):
555 return path.lower().startswith(prefix.lower())
556 return path.startswith(prefix)
557
558 +def getClientSpec():
559 + """Look at the p4 client spec, create a View() object that contains
560 + all the mappings, and return it."""
561 +
562 + specList = p4CmdList("client -o")
563 + if len(specList) != 1:
564 + die('Output from "client -o" is %d lines, expecting 1' %
565 + len(specList))
566 +
567 + # dictionary of all client parameters
568 + entry = specList[0]
569 +
570 + # just the keys that start with "View"
571 + view_keys = [ k for k in entry.keys() if k.startswith("View") ]
572 +
573 + # hold this new View
574 + view = View()
575 +
576 + # append the lines, in order, to the view
577 + for view_num in range(len(view_keys)):
578 + k = "View%d" % view_num
579 + if k not in view_keys:
580 + die("Expected view key %s missing" % k)
581 + view.append(entry[k])
582 +
583 + return view
584 +
585 +def getClientRoot():
586 + """Grab the client directory."""
587 +
588 + output = p4CmdList("client -o")
589 + if len(output) != 1:
590 + die('Output from "client -o" is %d lines, expecting 1' % len(output))
591 +
592 + entry = output[0]
593 + if "Root" not in entry:
594 + die('Client has no "Root"')
595 +
596 + return entry["Root"]
597 +
598 class Command:
599 def __init__(self):
600 self.usage = "usage: %prog [options]"
@@ -1119,11 +1159,20 @@ class P4Submit(Command, P4UserMap):
1159 print "Internal error: cannot locate perforce depot path from existing branches"
1160 sys.exit(128)
1161
1122 - self.clientPath = p4Where(self.depotPath)
1162 + self.useClientSpec = False
1163 + if gitConfig("git-p4.useclientspec", "--bool") == "true":
1164 + self.useClientSpec = True
1165 + if self.useClientSpec:
1166 + self.clientSpecDirs = getClientSpec()
1167 +
1168 + if self.useClientSpec:
1169 + # all files are relative to the client spec
1170 + self.clientPath = getClientRoot()
1171 + else:
1172 + self.clientPath = p4Where(self.depotPath)
1173
1124 - if len(self.clientPath) == 0:
1125 - print "Error: Cannot locate perforce checkout of %s in client view" % self.depotPath
1126 - sys.exit(128)
1174 + if self.clientPath == "":
1175 + die("Error: Cannot locate perforce checkout of %s in client view" % self.depotPath)
1176
1177 print "Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath)
1178 self.oldWorkingDirectory = os.getcwd()
@@ -2078,33 +2127,6 @@ class P4Sync(Command, P4UserMap):
2127 print self.gitError.read()
2128
2129
2081 - def getClientSpec(self):
2082 - specList = p4CmdList("client -o")
2083 - if len(specList) != 1:
2084 - die('Output from "client -o" is %d lines, expecting 1' %
2085 - len(specList))
2086 -
2087 - # dictionary of all client parameters
2088 - entry = specList[0]
2089 -
2090 - # just the keys that start with "View"
2091 - view_keys = [ k for k in entry.keys() if k.startswith("View") ]
2092 -
2093 - # hold this new View
2094 - view = View()
2095 -
2096 - # append the lines, in order, to the view
2097 - for view_num in range(len(view_keys)):
2098 - k = "View%d" % view_num
2099 - if k not in view_keys:
2100 - die("Expected view key %s missing" % k)
2101 - view.append(entry[k])
2102 -
2103 - self.clientSpecDirs = view
2104 - if self.verbose:
2105 - for i, m in enumerate(self.clientSpecDirs.mappings):
2106 - print "clientSpecDirs %d: %s" % (i, str(m))
2107 -
2130 def run(self, args):
2131 self.depotPaths = []
2132 self.changeRange = ""
@@ -2145,7 +2167,7 @@ class P4Sync(Command, P4UserMap):
2167 if gitConfig("git-p4.useclientspec", "--bool") == "true":
2168 self.useClientSpec = True
2169 if self.useClientSpec:
2148 - self.getClientSpec()
2170 + self.clientSpecDirs = getClientSpec()
2171
2172 # TODO: should always look at previous commits,
2173 # merge with previous imports, if possible.
t/t9809-git-p4-client-view.sh
+131 -11
@@ -71,20 +71,24 @@ git_verify() {
71 # - dir2
72 # - file21
73 # - file22
74 +init_depot() {
75 + for d in 1 2 ; do
76 + mkdir -p dir$d &&
77 + for f in 1 2 ; do
78 + echo dir$d/file$d$f >dir$d/file$d$f &&
79 + p4 add dir$d/file$d$f &&
80 + p4 submit -d "dir$d/file$d$f"
81 + done
82 + done &&
83 + find . -type f ! -name files >files &&
84 + check_files_exist dir1/file11 dir1/file12 \
85 + dir2/file21 dir2/file22
86 +}
87 +
88 test_expect_success 'init depot' '
89 (
90 cd "$cli" &&
77 - for d in 1 2 ; do
78 - mkdir -p dir$d &&
79 - for f in 1 2 ; do
80 - echo dir$d/file$d$f >dir$d/file$d$f &&
81 - p4 add dir$d/file$d$f &&
82 - p4 submit -d "dir$d/file$d$f"
83 - done
84 - done &&
85 - find . -type f ! -name files >files &&
86 - check_files_exist dir1/file11 dir1/file12 \
87 - dir2/file21 dir2/file22
91 + init_depot
92 )
93 '
94
@@ -257,6 +261,122 @@ test_expect_success 'clone --use-client-spec sets useClientSpec' '
261 )
262 '
263
264 +# clone just a subdir of the client spec
265 +test_expect_success 'subdir clone' '
266 + client_view "//depot/... //client/..." &&
267 + files="dir1/file11 dir1/file12 dir2/file21 dir2/file22" &&
268 + client_verify $files &&
269 + test_when_finished cleanup_git &&
270 + "$GITP4" clone --use-client-spec --dest="$git" //depot/dir1 &&
271 + git_verify dir1/file11 dir1/file12
272 +'
273 +
274 +#
275 +# submit back, see what happens: five cases
276 +#
277 +test_expect_success 'subdir clone, submit modify' '
278 + client_view "//depot/... //client/..." &&
279 + test_when_finished cleanup_git &&
280 + "$GITP4" clone --use-client-spec --dest="$git" //depot/dir1 &&
281 + (
282 + cd "$git" &&
283 + git config git-p4.skipSubmitEdit true &&
284 + echo line >>dir1/file12 &&
285 + git add dir1/file12 &&
286 + git commit -m dir1/file12 &&
287 + "$GITP4" submit
288 + ) &&
289 + (
290 + cd "$cli" &&
291 + test_path_is_file dir1/file12 &&
292 + test_line_count = 2 dir1/file12
293 + )
294 +'
295 +
296 +test_expect_success 'subdir clone, submit add' '
297 + client_view "//depot/... //client/..." &&
298 + test_when_finished cleanup_git &&
299 + "$GITP4" clone --use-client-spec --dest="$git" //depot/dir1 &&
300 + (
301 + cd "$git" &&
302 + git config git-p4.skipSubmitEdit true &&
303 + echo file13 >dir1/file13 &&
304 + git add dir1/file13 &&
305 + git commit -m dir1/file13 &&
306 + "$GITP4" submit
307 + ) &&
308 + (
309 + cd "$cli" &&
310 + test_path_is_file dir1/file13
311 + )
312 +'
313 +
314 +test_expect_success 'subdir clone, submit delete' '
315 + client_view "//depot/... //client/..." &&
316 + test_when_finished cleanup_git &&
317 + "$GITP4" clone --use-client-spec --dest="$git" //depot/dir1 &&
318 + (
319 + cd "$git" &&
320 + git config git-p4.skipSubmitEdit true &&
321 + git rm dir1/file12 &&
322 + git commit -m "delete dir1/file12" &&
323 + "$GITP4" submit
324 + ) &&
325 + (
326 + cd "$cli" &&
327 + test_path_is_missing dir1/file12
328 + )
329 +'
330 +
331 +test_expect_success 'subdir clone, submit copy' '
332 + client_view "//depot/... //client/..." &&
333 + test_when_finished cleanup_git &&
334 + "$GITP4" clone --use-client-spec --dest="$git" //depot/dir1 &&
335 + (
336 + cd "$git" &&
337 + git config git-p4.skipSubmitEdit true &&
338 + git config git-p4.detectCopies true &&
339 + cp dir1/file11 dir1/file11a &&
340 + git add dir1/file11a &&
341 + git commit -m "copy to dir1/file11a" &&
342 + "$GITP4" submit
343 + ) &&
344 + (
345 + cd "$cli" &&
346 + test_path_is_file dir1/file11a
347 + )
348 +'
349 +
350 +test_expect_success 'subdir clone, submit rename' '
351 + client_view "//depot/... //client/..." &&
352 + test_when_finished cleanup_git &&
353 + "$GITP4" clone --use-client-spec --dest="$git" //depot/dir1 &&
354 + (
355 + cd "$git" &&
356 + git config git-p4.skipSubmitEdit true &&
357 + git config git-p4.detectRenames true &&
358 + git mv dir1/file13 dir1/file13a &&
359 + git commit -m "rename dir1/file13 to dir1/file13a" &&
360 + "$GITP4" submit
361 + ) &&
362 + (
363 + cd "$cli" &&
364 + test_path_is_missing dir1/file13 &&
365 + test_path_is_file dir1/file13a
366 + )
367 +'
368 +
369 +test_expect_success 'reinit depot' '
370 + (
371 + cd "$cli" &&
372 + p4 sync -f &&
373 + rm files &&
374 + p4 delete */* &&
375 + p4 submit -d "delete all files" &&
376 + init_depot
377 + )
378 +'
379 +
380 #
381 # Rename directories to test quoting in depot-side mappings
382 # //depot