Turn off potentially slow label detection by default
[gitweb.git] / contrib / fast-import / git-p4
index 09990be3734bd05931ecb388b5af1c9baf264dfb..fcdfe22a45ad58ea4bf8b7fa0979b256a622c987 100755 (executable)
@@ -107,6 +107,7 @@ def gitBranchExists(branch):
 class Command:
     def __init__(self):
         self.usage = "usage: %prog [options]"
+        self.needsGit = True
 
 class P4Debug(Command):
     def __init__(self):
@@ -114,6 +115,7 @@ class P4Debug(Command):
         self.options = [
         ]
         self.description = "A tool to debug the output of p4 -G."
+        self.needsGit = False
 
     def run(self, args):
         for output in p4CmdList(" ".join(args)):
@@ -158,20 +160,20 @@ class P4CleanTags(Command):
         print "%s tags removed." % len(allTags)
         return True
 
-class P4Sync(Command):
+class P4Submit(Command):
     def __init__(self):
         Command.__init__(self)
         self.options = [
                 optparse.make_option("--continue", action="store_false", dest="firstTime"),
                 optparse.make_option("--origin", dest="origin"),
                 optparse.make_option("--reset", action="store_true", dest="reset"),
-                optparse.make_option("--master", dest="master"),
                 optparse.make_option("--log-substitutions", dest="substFile"),
                 optparse.make_option("--noninteractive", action="store_false"),
                 optparse.make_option("--dry-run", action="store_true"),
                 optparse.make_option("--apply-as-patch", action="store_true", dest="applyAsPatch")
         ]
         self.description = "Submit changes from git to the perforce depot."
+        self.usage += " [name of git branch to submit into perforce depot]"
         self.firstTime = True
         self.reset = False
         self.interactive = True
@@ -179,7 +181,6 @@ class P4Sync(Command):
         self.substFile = ""
         self.firstTime = True
         self.origin = ""
-        self.master = ""
         self.applyAsPatch = True
 
         self.logSubstitutions = {}
@@ -324,6 +325,16 @@ class P4Sync(Command):
         # make gitdir absolute so we can cd out into the perforce checkout
         gitdir = os.path.abspath(gitdir)
         os.environ["GIT_DIR"] = gitdir
+
+        if len(args) == 0:
+            self.master = currentGitBranch()
+            if len(self.master) == 0 or not os.path.exists("%s/refs/heads/%s" % (gitdir, self.master)):
+                die("Detecting current git branch failed!")
+        elif len(args) == 1:
+            self.master = args[0]
+        else:
+            return False
+
         depotPath = ""
         if gitBranchExists("p4"):
             [depotPath, dummy] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("p4"))
@@ -360,11 +371,6 @@ class P4Sync(Command):
                 tokens = line[:-1].split("=")
                 self.logSubstitutions[tokens[0]] = tokens[1]
 
-        if len(self.master) == 0:
-            self.master = currentGitBranch()
-            if len(self.master) == 0 or not os.path.exists("%s/refs/heads/%s" % (gitdir, self.master)):
-                die("Detecting current git branch failed!")
-
         self.check()
         self.configFile = gitdir + "/p4-git-sync.cfg"
         self.config = shelve.open(self.configFile, writeback=True)
@@ -401,7 +407,7 @@ class P4Sync(Command):
 
         return True
 
-class GitSync(Command):
+class P4Sync(Command):
     def __init__(self):
         Command.__init__(self)
         self.options = [
@@ -410,8 +416,9 @@ class GitSync(Command):
                 optparse.make_option("--changesfile", dest="changesFile"),
                 optparse.make_option("--silent", dest="silent", action="store_true"),
                 optparse.make_option("--known-branches", dest="knownBranches"),
-                optparse.make_option("--cache", dest="doCache", action="store_true"),
-                optparse.make_option("--command-cache", dest="commandCache", action="store_true")
+                optparse.make_option("--data-cache", dest="dataCache", action="store_true"),
+                optparse.make_option("--command-cache", dest="commandCache", action="store_true"),
+                optparse.make_option("--detect-labels", dest="detectLabels", action="store_true")
         ]
         self.description = """Imports from Perforce into a git repository.\n
     example:
@@ -431,6 +438,7 @@ class GitSync(Command):
         self.committedChanges = Set()
         self.branch = ""
         self.detectBranches = False
+        self.detectLabels = False
         self.changesFile = ""
 
     def p4File(self, depotPath):
@@ -494,7 +502,7 @@ class GitSync(Command):
             if knownBranch:
                 continue
 
-            for branch in knownBranches:
+            for branch in self.knownBranches:
                 #if relativePath.startswith(branch):
                 if self.isSubPathOf(relativePath, branch):
                     if len(branches) == 0:
@@ -623,7 +631,47 @@ class GitSync(Command):
 
         self.gitStream.write("\n")
 
-        self.lastChange = int(details["change"])
+        change = int(details["change"])
+
+        self.lastChange = change
+
+        if change in self.labels:
+            label = self.labels[change]
+            labelDetails = label[0]
+            labelRevisions = label[1]
+
+            files = p4CmdList("files %s...@%s" % (branchPrefix, change))
+
+            if len(files) == len(labelRevisions):
+
+                cleanedFiles = {}
+                for info in files:
+                    if info["action"] == "delete":
+                        continue
+                    cleanedFiles[info["depotFile"]] = info["rev"]
+
+                if cleanedFiles == labelRevisions:
+                    self.gitStream.write("tag tag_%s\n" % labelDetails["label"])
+                    self.gitStream.write("from %s\n" % branch)
+
+                    owner = labelDetails["Owner"]
+                    tagger = ""
+                    if author in self.users:
+                        tagger = "%s %s %s" % (self.users[owner], epoch, self.tz)
+                    else:
+                        tagger = "%s <a@b> %s %s" % (owner, epoch, self.tz)
+                    self.gitStream.write("tagger %s\n" % tagger)
+                    self.gitStream.write("data <<EOT\n")
+                    self.gitStream.write(labelDetails["Description"])
+                    self.gitStream.write("EOT\n\n")
+
+                else:
+                    if not self.silent:
+                        print "Tag %s does not match with change %s: files do not match." % (labelDetails["label"], change)
+
+            else:
+                if not self.silent:
+                    print "Tag %s does not match with change %s: file count is different." % (labelDetails["label"], change)
 
     def extractFilesInCommitToBranch(self, files, branchPrefix):
         newFiles = []
@@ -755,6 +803,25 @@ class GitSync(Command):
                 continue
             self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">"
 
+    def getLabels(self):
+        self.labels = {}
+
+        l = p4CmdList("labels %s..." % self.globalPrefix)
+        if len(l) > 0:
+            print "Finding files belonging to labels in %s" % self.globalPrefix
+
+        for output in l:
+            label = output["label"]
+            revisions = {}
+            newestChange = 0
+            for file in p4CmdList("files //...@%s" % label):
+                revisions[file["depotFile"]] = file["rev"]
+                change = int(file["change"])
+                if change > newestChange:
+                    newestChange = change
+
+            self.labels[newestChange] = [output, revisions]
+
     def run(self, args):
         self.globalPrefix = ""
         self.changeRange = ""
@@ -827,6 +894,9 @@ class GitSync(Command):
             self.globalPrefix += "/"
 
         self.getUserMap()
+        self.labels = {}
+        if self.detectLabels:
+            self.getLabels();
 
         if len(self.changeRange) == 0:
             try:
@@ -908,7 +978,7 @@ class GitSync(Command):
             if len(changes) == 0:
                 if not self.silent:
                     print "no changes to import!"
-                sys.exit(1)
+                return True
 
             cnt = 1
             for change in changes:
@@ -977,6 +1047,21 @@ class GitSync(Command):
 
         return True
 
+class P4Rebase(Command):
+    def __init__(self):
+        Command.__init__(self)
+        self.options = [ ]
+        self.description = "Fetches the latest revision from perforce and rebases the current work (branch) against it"
+
+    def run(self, args):
+        sync = P4Sync()
+        sync.run([])
+        print "Rebasing the current branch"
+        oldHead = os.popen("git rev-parse HEAD").read()[:-1]
+        system("git rebase p4")
+        system("git diff-tree --stat --summary -M %s HEAD" % oldHead)
+        return True
+
 class HelpFormatter(optparse.IndentedHelpFormatter):
     def __init__(self):
         optparse.IndentedHelpFormatter.__init__(self)
@@ -998,8 +1083,9 @@ def printUsage(commands):
 commands = {
     "debug" : P4Debug(),
     "clean-tags" : P4CleanTags(),
-    "submit" : P4Sync(),
-    "sync" : GitSync()
+    "submit" : P4Submit(),
+    "sync" : P4Sync(),
+    "rebase" : P4Rebase()
 }
 
 if len(sys.argv[1:]) == 0:
@@ -1018,30 +1104,35 @@ except KeyError:
 
 options = cmd.options
 cmd.gitdir = gitdir
-options.append(optparse.make_option("--git-dir", dest="gitdir"))
 
-parser = optparse.OptionParser(cmd.usage.replace("%prog", "%prog " + cmdName),
-                               options,
-                               description = cmd.description,
-                               formatter = HelpFormatter())
+args = sys.argv[2:]
 
-(cmd, args) = parser.parse_args(sys.argv[2:], cmd);
+if len(options) > 0:
+    options.append(optparse.make_option("--git-dir", dest="gitdir"))
 
-gitdir = cmd.gitdir
-if len(gitdir) == 0:
-    gitdir = ".git"
-    if not isValidGitDir(gitdir):
-        cdup = os.popen("git rev-parse --show-cdup").read()[:-1]
-        if isValidGitDir(cdup + "/" + gitdir):
-            os.chdir(cdup)
+    parser = optparse.OptionParser(cmd.usage.replace("%prog", "%prog " + cmdName),
+                                   options,
+                                   description = cmd.description,
+                                   formatter = HelpFormatter())
+
+    (cmd, args) = parser.parse_args(sys.argv[2:], cmd);
+
+if cmd.needsGit:
+    gitdir = cmd.gitdir
+    if len(gitdir) == 0:
+        gitdir = ".git"
+        if not isValidGitDir(gitdir):
+            cdup = os.popen("git rev-parse --show-cdup").read()[:-1]
+            if isValidGitDir(cdup + "/" + gitdir):
+                os.chdir(cdup)
 
-if not isValidGitDir(gitdir):
-    if isValidGitDir(gitdir + "/.git"):
-        gitdir += "/.git"
-    else:
-        die("fatal: cannot locate git repository at %s" % gitdir)
+    if not isValidGitDir(gitdir):
+        if isValidGitDir(gitdir + "/.git"):
+            gitdir += "/.git"
+        else:
+            die("fatal: cannot locate git repository at %s" % gitdir)
 
-os.environ["GIT_DIR"] = gitdir
+    os.environ["GIT_DIR"] = gitdir
 
 if not cmd.run(args):
     parser.print_help()