git-p4: Search for parent commit on branch creation

To find out which is its parent the commit of the new branch is compared sequentially to each blob of the parent branch from the newest to the oldest. The first blob which results in a zero diff is considered the parent commit. If none is found, then the commit is applied to the top of the parent branch. A fast-import "checkpoint" call is required because diff-tree is only able to work with blobs on disk. But most of these commits will not be part of the final imported tree, making fast-import fail. To avoid this, the temporary branches are tracked and then removed at the end of the import process. Signed-off-by: Vitor Antunes <vitor.hda@gmail.com> Acked-by: Pete Wyckoff <pw@padd.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Vitor Antunes committed Jan 25, 2012 at 23:48 UTC fed23693ba1a1ea7beb851cd385d458136e91664
1 file changed +45 -1
contrib/fast-import/git-p4
+45 -1
@@ -1429,6 +1429,8 @@ class P4Sync(Command, P4UserMap):
1429 self.cloneExclude = []
1430 self.useClientSpec = False
1431 self.clientSpecDirs = None
1432 + self.tempBranches = []
1433 + self.tempBranchLocation = "git-p4-tmp"
1434
1435 if gitConfig("git-p4.syncFromOrigin") == "false":
1436 self.syncWithOrigin = False
@@ -1450,6 +1452,14 @@ class P4Sync(Command, P4UserMap):
1452 .replace("%25", "%")
1453 return path
1454
1455 + # Force a checkpoint in fast-import and wait for it to finish
1456 + def checkpoint(self):
1457 + self.gitStream.write("checkpoint\n\n")
1458 + self.gitStream.write("progress checkpoint\n\n")
1459 + out = self.gitOutput.readline()
1460 + if self.verbose:
1461 + print "checkpoint finished: " + out
1462 +
1463 def extractFilesFromCommit(self, commit):
1464 self.cloneExclude = [re.sub(r"\.\.\.$", "", path)
1465 for path in self.cloneExclude]
@@ -1957,6 +1967,20 @@ class P4Sync(Command, P4UserMap):
1967 self.importChanges(changes)
1968 return True
1969
1970 + def searchParent(self, parent, branch, target):
1971 + parentFound = False
1972 + for blob in read_pipe_lines(["git", "rev-list", "--reverse", "--no-merges", parent]):
1973 + blob = blob.strip()
1974 + if len(read_pipe(["git", "diff-tree", blob, target])) == 0:
1975 + parentFound = True
1976 + if self.verbose:
1977 + print "Found parent of %s in commit %s" % (branch, blob)
1978 + break
1979 + if parentFound:
1980 + return blob
1981 + else:
1982 + return None
1983 +
1984 def importChanges(self, changes):
1985 cnt = 1
1986 for change in changes:
@@ -2013,7 +2037,21 @@ class P4Sync(Command, P4UserMap):
2037 parent = self.initialParents[branch]
2038 del self.initialParents[branch]
2039
2016 - self.commit(description, filesForCommit, branch, [branchPrefix], parent)
2040 + blob = None
2041 + if len(parent) > 0:
2042 + tempBranch = os.path.join(self.tempBranchLocation, "%d" % (change))
2043 + if self.verbose:
2044 + print "Creating temporary branch: " + tempBranch
2045 + self.commit(description, filesForCommit, tempBranch, [branchPrefix])
2046 + self.tempBranches.append(tempBranch)
2047 + self.checkpoint()
2048 + blob = self.searchParent(parent, branch, tempBranch)
2049 + if blob:
2050 + self.commit(description, filesForCommit, branch, [branchPrefix], blob)
2051 + else:
2052 + if self.verbose:
2053 + print "Parent of %s not found. Committing into head of %s" % (branch, parent)
2054 + self.commit(description, filesForCommit, branch, [branchPrefix], parent)
2055 else:
2056 files = self.extractFilesFromCommit(description)
2057 self.commit(description, files, self.branch, self.depotPaths,
@@ -2348,6 +2386,12 @@ class P4Sync(Command, P4UserMap):
2386 self.gitOutput.close()
2387 self.gitError.close()
2388
2389 + # Cleanup temporary branches created during import
2390 + if self.tempBranches != []:
2391 + for branch in self.tempBranches:
2392 + read_pipe("git update-ref -d %s" % branch)
2393 + os.rmdir(os.path.join(os.environ.get("GIT_DIR", ".git"), self.tempBranchLocation))
2394 +
2395 return True
2396
2397 class P4Rebase(Command):