Code

First version of a new script to submit changes back to perforce from git repositories.
authorSimon Hausmann <hausmann@kde.org>
Mon, 12 Mar 2007 22:00:34 +0000 (23:00 +0100)
committerSimon Hausmann <hausmann@kde.org>
Mon, 12 Mar 2007 22:00:34 +0000 (23:00 +0100)
Signed-off-by: Simon Hausmann <hausmann@kde.org>
contrib/fast-import/p4-git-sync.py [new file with mode: 0755]

diff --git a/contrib/fast-import/p4-git-sync.py b/contrib/fast-import/p4-git-sync.py
new file mode 100755 (executable)
index 0000000..8982e45
--- /dev/null
@@ -0,0 +1,208 @@
+#!/usr/bin/python
+#
+# p4-git-sync.py
+#
+# Author: Simon Hausmann <hausmann@kde.org>
+# License: MIT <http://www.opensource.org/licenses/mit-license.php>
+#
+
+import os, string, shelve, stat
+import getopt, sys, marshal
+
+def p4CmdList(cmd):
+    cmd = "p4 -G %s" % cmd
+    pipe = os.popen(cmd, "rb")
+
+    result = []
+    try:
+        while True:
+            entry = marshal.load(pipe)
+            result.append(entry)
+    except EOFError:
+        pass
+    pipe.close()
+
+    return result
+
+def p4Cmd(cmd):
+    list = p4CmdList(cmd)
+    result = {}
+    for entry in list:
+        result.update(entry)
+    return result;
+
+try:
+    opts, args = getopt.getopt(sys.argv[1:], "", [ "continue", "--git-dir=", "origin=", "reset", "master=",
+                                                   "submit-log-subst=" ])
+except getopt.GetoptError:
+    print "fixme, syntax error"
+    sys.exit(1)
+
+logSubstitutions = {}
+logSubstitutions["<enter description here>"] = "%log%"
+logSubstitutions["\tDetails:"] = "\tDetails:  %log%"
+gitdir = os.environ.get("GIT_DIR", "")
+origin = "origin"
+master = "master"
+firstTime = True
+reset = False
+
+for o, a in opts:
+    if o == "--git-dir":
+        gitdir = a
+    elif o == "--origin":
+        origin = a
+    elif o == "--master":
+        master = a
+    elif o == "--continue":
+        firstTime = False
+    elif o == "--reset":
+        reset = True
+        firstTime = True
+    elif o == "--submit-log-subst":
+        key = a.split("%")[0]
+        value = a.split("%")[1]
+        logSubstitutions[key] = value
+
+if len(gitdir) == 0:
+    gitdir = ".git"
+else:
+    os.environ["GIT_DIR"] = gitdir
+
+configFile = gitdir + "/p4-git-sync.cfg"
+
+origin = "origin"
+if len(args) == 1:
+    origin = args[0]
+
+def die(msg):
+    sys.stderr.write(msg + "\n")
+    sys.exit(1)
+
+def system(cmd):
+    if os.system(cmd) != 0:
+        die("command failed: %s" % cmd)
+
+def check():
+    return
+    if len(p4CmdList("opened ...")) > 0:
+        die("You have files opened with perforce! Close them before starting the sync.")
+
+def start(config):
+    if len(config) > 0 and not reset:
+        die("Cannot start sync. Previous sync config found at %s" % configFile)
+
+    #if len(os.popen("git-update-index --refresh").read()) > 0:
+    #    die("Your working tree is not clean. Check with git status!")
+
+    commits = []
+    for line in os.popen("git-rev-list --no-merges %s..%s" % (origin, master)).readlines():
+        commits.append(line[:-1])
+    commits.reverse()
+
+    config["commits"] = commits
+
+#    print "Cleaning index..."
+#    system("git checkout -f")
+
+def prepareLogMessage(template, message):
+    result = ""
+
+    substs = logSubstitutions
+    for k in substs.keys():
+        substs[k] = substs[k].replace("%log%", message)
+
+    for line in template.split("\n"):
+        if line.startswith("#"):
+            result += line + "\n"
+            continue
+
+        substituted = False
+        for key in substs.keys():
+            if line.find(key) != -1:
+                value = substs[key]
+                if value != "@remove@":
+                    result += line.replace(key, value) + "\n"
+                substituted = True
+                break
+
+        if not substituted:
+            result += line + "\n"
+
+    return result
+
+def apply(id):
+    print "Applying %s" % (os.popen("git-log --max-count=1 --pretty=oneline %s" % id).read())
+    diff = os.popen("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)).readlines()
+    filesToAdd = set()
+    filesToDelete = set()
+    for line in diff:
+        modifier = line[0]
+        path = line[1:].strip()
+        if modifier == "M":
+            system("p4 edit %s" % path)
+        elif modifier == "A":
+            filesToAdd.add(path)
+            if path in filesToDelete:
+                filesToDelete.remove(path)
+        elif modifier == "D":
+            filesToDelete.add(path)
+            if path in filesToAdd:
+                filesToAdd.remove(path)
+        else:
+            die("unknown modifier %s for %s" % (modifier, path))
+
+    system("git-diff-files --name-only -z | git-update-index --remove -z --stdin")
+    system("git cherry-pick --no-commit \"%s\"" % id)
+
+    for f in filesToAdd:
+        system("p4 add %s" % f)
+    for f in filesToDelete:
+        system("p4 revert %s" % f)
+        system("p4 delete %s" % f)
+
+    logMessage = ""
+    foundTitle = False
+    for log in os.popen("git-cat-file commit %s" % id).readlines():
+        log = log[:-1]
+        if not foundTitle:
+            if len(log) == 0:
+                foundTitle = 1
+            continue
+
+        if len(logMessage) > 0:
+            logMessage += "\t"
+        logMessage += log + "\n"
+
+    template = os.popen("p4 change -o").read()
+    fileName = "submit.txt"
+    file = open(fileName, "w+")
+    file.write(prepareLogMessage(template, logMessage))
+    file.close()
+    print "Perforce submit template written as %s. Please review/edit and then use p4 submit -i < %s to submit directly!" % (fileName, fileName)
+
+check()
+
+config = shelve.open(configFile, writeback=True)
+
+if firstTime:
+    start(config)
+
+commits = config.get("commits", [])
+
+if len(commits) > 0:
+    firstTime = False
+    commit = commits[0]
+    commits = commits[1:]
+    config["commits"] = commits
+    apply(commit)
+
+config.close()
+
+if len(commits) == 0:
+    if firstTime:
+        print "No changes found to apply between %s and current HEAD" % origin
+    else:
+        print "All changes applied!"
+    os.remove(configFile)
+