Code

Fixed the check to make sure to exclude the HEAD symbolic refs when updating
[git.git] / contrib / fast-import / git-p4
index aba4752d4e2a2e8146139797b6baab7e44ea0d76..d03ba0b6ad4c570e33d45538ee9e3ec84d18c4ef 100755 (executable)
@@ -2,8 +2,8 @@
 #
 # git-p4.py -- A tool for bidirectional operation between a Perforce depot and git.
 #
 #
 # git-p4.py -- A tool for bidirectional operation between a Perforce depot and git.
 #
-# Author: Simon Hausmann <hausmann@kde.org>
-# Copyright: 2007 Simon Hausmann <hausmann@kde.org>
+# Author: Simon Hausmann <simon@lst.de>
+# Copyright: 2007 Simon Hausmann <simon@lst.de>
 #            2007 Trolltech ASA
 # License: MIT <http://www.opensource.org/licenses/mit-license.php>
 #
 #            2007 Trolltech ASA
 # License: MIT <http://www.opensource.org/licenses/mit-license.php>
 #
 import optparse, sys, os, marshal, popen2, subprocess, shelve
 import tempfile, getopt, sha, os.path, time, platform
 import re
 import optparse, sys, os, marshal, popen2, subprocess, shelve
 import tempfile, getopt, sha, os.path, time, platform
 import re
+
 from sets import Set;
 
 from sets import Set;
 
-gitdir = os.environ.get("GIT_DIR", "")
+verbose = False
+
+def die(msg):
+    if verbose:
+        raise Exception(msg)
+    else:
+        sys.stderr.write(msg + "\n")
+        sys.exit(1)
+
+def write_pipe(c, str):
+    if verbose:
+        sys.stderr.write('Writing pipe: %s\n' % c)
+
+    pipe = os.popen(c, 'w')
+    val = pipe.write(str)
+    if pipe.close():
+        die('Command failed: %s' % c)
+
+    return val
+
+def read_pipe(c, ignore_error=False):
+    if verbose:
+        sys.stderr.write('Reading pipe: %s\n' % c)
+
+    pipe = os.popen(c, 'rb')
+    val = pipe.read()
+    if pipe.close() and not ignore_error:
+        die('Command failed: %s' % c)
 
 
-def mypopen(command):
-    return os.popen(command, "rb");
+    return val
+
+
+def read_pipe_lines(c):
+    if verbose:
+        sys.stderr.write('Reading pipe: %s\n' % c)
+    ## todo: check return status
+    pipe = os.popen(c, 'rb')
+    val = pipe.readlines()
+    if pipe.close():
+        die('Command failed: %s' % c)
+
+    return val
+
+def system(cmd):
+    if verbose:
+        sys.stderr.write("executing %s\n" % cmd)
+    if os.system(cmd) != 0:
+        die("command failed: %s" % cmd)
 
 def p4CmdList(cmd):
     cmd = "p4 -G %s" % cmd
 
 def p4CmdList(cmd):
     cmd = "p4 -G %s" % cmd
+    if verbose:
+        sys.stderr.write("Opening pipe: %s\n" % cmd)
     pipe = os.popen(cmd, "rb")
 
     result = []
     pipe = os.popen(cmd, "rb")
 
     result = []
@@ -62,29 +109,24 @@ def p4Where(depotPath):
         clientPath = clientPath[:-3]
     return clientPath
 
         clientPath = clientPath[:-3]
     return clientPath
 
-def die(msg):
-    sys.stderr.write(msg + "\n")
-    sys.exit(1)
-
 def currentGitBranch():
 def currentGitBranch():
-    return mypopen("git name-rev HEAD").read().split(" ")[1][:-1]
+    return read_pipe("git name-rev HEAD").split(" ")[1].strip()
 
 def isValidGitDir(path):
 
 def isValidGitDir(path):
-    if os.path.exists(path + "/HEAD") and os.path.exists(path + "/refs") and os.path.exists(path + "/objects"):
+    if (os.path.exists(path + "/HEAD")
+        and os.path.exists(path + "/refs") and os.path.exists(path + "/objects")):
         return True;
     return False
 
 def parseRevision(ref):
         return True;
     return False
 
 def parseRevision(ref):
-    return mypopen("git rev-parse %s" % ref).read()[:-1]
-
-def system(cmd):
-    if os.system(cmd) != 0:
-        die("command failed: %s" % cmd)
+    return read_pipe("git rev-parse %s" % ref).strip()
 
 def extractLogMessageFromGitCommit(commit):
     logMessage = ""
 
 def extractLogMessageFromGitCommit(commit):
     logMessage = ""
+
+    ## fixme: title is first line of commit, not 1st paragraph.
     foundTitle = False
     foundTitle = False
-    for log in mypopen("git cat-file commit %s" % commit).readlines():
+    for log in read_pipe_lines("git cat-file commit %s" % commit):
        if not foundTitle:
            if len(log) == 1:
                foundTitle = True
        if not foundTitle:
            if len(log) == 1:
                foundTitle = True
@@ -93,31 +135,38 @@ def extractLogMessageFromGitCommit(commit):
        logMessage += log
     return logMessage
 
        logMessage += log
     return logMessage
 
-def extractDepotPathAndChangeFromGitLog(log):
+def extractSettingsGitLog(log):
     values = {}
     for line in log.split("\n"):
         line = line.strip()
     values = {}
     for line in log.split("\n"):
         line = line.strip()
-        if line.startswith("[git-p4:") and line.endswith("]"):
-            line = line[8:-1].strip()
-            for assignment in line.split(":"):
-                variable = assignment.strip()
-                value = ""
-                equalPos = assignment.find("=")
-                if equalPos != -1:
-                    variable = assignment[:equalPos].strip()
-                    value = assignment[equalPos + 1:].strip()
-                    if value.startswith("\"") and value.endswith("\""):
-                        value = value[1:-1]
-                values[variable] = value
-
-    return values.get("depot-path"), values.get("change")
+        m = re.search (r"^ *\[git-p4: (.*)\]$", line)
+        if not m:
+            continue
+
+        assignments = m.group(1).split (':')
+        for a in assignments:
+            vals = a.split ('=')
+            key = vals[0].strip()
+            val = ('='.join (vals[1:])).strip()
+            if val.endswith ('\"') and val.startswith('"'):
+                val = val[1:-1]
+
+            values[key] = val
+
+    paths = values.get("depot-paths")
+    if not paths:
+        paths = values.get("depot-path")
+    if paths:
+        values['depot-paths'] = paths.split(',')
+    return values
 
 def gitBranchExists(branch):
 
 def gitBranchExists(branch):
-    proc = subprocess.Popen(["git", "rev-parse", branch], stderr=subprocess.PIPE, stdout=subprocess.PIPE);
+    proc = subprocess.Popen(["git", "rev-parse", branch],
+                            stderr=subprocess.PIPE, stdout=subprocess.PIPE);
     return proc.wait() == 0;
 
 def gitConfig(key):
     return proc.wait() == 0;
 
 def gitConfig(key):
-    return mypopen("git config %s" % key).read()[:-1]
+    return read_pipe("git config %s" % key, ignore_error=True).strip()
 
 class Command:
     def __init__(self):
 
 class Command:
     def __init__(self):
@@ -128,12 +177,18 @@ class P4Debug(Command):
     def __init__(self):
         Command.__init__(self)
         self.options = [
     def __init__(self):
         Command.__init__(self)
         self.options = [
-        ]
+            optparse.make_option("--verbose", dest="verbose", action="store_true",
+                                 default=False),
+            ]
         self.description = "A tool to debug the output of p4 -G."
         self.needsGit = False
         self.description = "A tool to debug the output of p4 -G."
         self.needsGit = False
+        self.verbose = False
 
     def run(self, args):
 
     def run(self, args):
+        j = 0
         for output in p4CmdList(" ".join(args)):
         for output in p4CmdList(" ".join(args)):
+            print 'Element: %d' % j
+            j += 1
             print output
         return True
 
             print output
         return True
 
@@ -158,30 +213,40 @@ class P4RollBack(Command):
 
         if self.rollbackLocalBranches:
             refPrefix = "refs/heads/"
 
         if self.rollbackLocalBranches:
             refPrefix = "refs/heads/"
-            lines = mypopen("git rev-parse --symbolic --branches").readlines()
+            lines = read_pipe_lines("git rev-parse --symbolic --branches")
         else:
             refPrefix = "refs/remotes/"
         else:
             refPrefix = "refs/remotes/"
-            lines = mypopen("git rev-parse --symbolic --remotes").readlines()
+            lines = read_pipe_lines("git rev-parse --symbolic --remotes")
 
         for line in lines:
             if self.rollbackLocalBranches or (line.startswith("p4/") and line != "p4/HEAD\n"):
 
         for line in lines:
             if self.rollbackLocalBranches or (line.startswith("p4/") and line != "p4/HEAD\n"):
-                ref = refPrefix + line[:-1]
+                line = line.strip()
+                ref = refPrefix + line
                 log = extractLogMessageFromGitCommit(ref)
                 log = extractLogMessageFromGitCommit(ref)
-                depotPath, change = extractDepotPathAndChangeFromGitLog(log)
+                settings = extractSettingsGitLog(log)
+
+                depotPaths = settings['depot-paths']
+                change = settings['change']
+
                 changed = False
 
                 changed = False
 
-                if len(p4Cmd("changes -m 1 %s...@%s" % (depotPath, maxChange))) == 0:
+                if len(p4Cmd("changes -m 1 "  + ' '.join (['%s...@%s' % (p, maxChange)
+                                                           for p in depotPaths]))) == 0:
                     print "Branch %s did not exist at change %s, deleting." % (ref, maxChange)
                     system("git update-ref -d %s `git rev-parse %s`" % (ref, ref))
                     continue
 
                     print "Branch %s did not exist at change %s, deleting." % (ref, maxChange)
                     system("git update-ref -d %s `git rev-parse %s`" % (ref, ref))
                     continue
 
-                while len(change) > 0 and int(change) > maxChange:
+                while change and int(change) > maxChange:
                     changed = True
                     if self.verbose:
                         print "%s is at %s ; rewinding towards %s" % (ref, change, maxChange)
                     system("git update-ref %s \"%s^\"" % (ref, ref))
                     log = extractLogMessageFromGitCommit(ref)
                     changed = True
                     if self.verbose:
                         print "%s is at %s ; rewinding towards %s" % (ref, change, maxChange)
                     system("git update-ref %s \"%s^\"" % (ref, ref))
                     log = extractLogMessageFromGitCommit(ref)
-                    depotPath, change = extractDepotPathAndChangeFromGitLog(log)
+                    settings =  extractSettingsGitLog(log)
+
+
+                    depotPaths = settings['depot-paths']
+                    change = settings['change']
 
                 if changed:
                     print "%s rewound to %s" % (ref, change)
 
                 if changed:
                     print "%s rewound to %s" % (ref, change)
@@ -193,6 +258,7 @@ class P4Submit(Command):
         Command.__init__(self)
         self.options = [
                 optparse.make_option("--continue", action="store_false", dest="firstTime"),
         Command.__init__(self)
         self.options = [
                 optparse.make_option("--continue", action="store_false", dest="firstTime"),
+                optparse.make_option("--verbose", dest="verbose", action="store_true"),
                 optparse.make_option("--origin", dest="origin"),
                 optparse.make_option("--reset", action="store_true", dest="reset"),
                 optparse.make_option("--log-substitutions", dest="substFile"),
                 optparse.make_option("--origin", dest="origin"),
                 optparse.make_option("--reset", action="store_true", dest="reset"),
                 optparse.make_option("--log-substitutions", dest="substFile"),
@@ -211,6 +277,8 @@ class P4Submit(Command):
         self.origin = ""
         self.directSubmit = False
         self.trustMeLikeAFool = False
         self.origin = ""
         self.directSubmit = False
         self.trustMeLikeAFool = False
+        self.verbose = False
+        self.isWindows = (platform.system() == "Windows")
 
         self.logSubstitutions = {}
         self.logSubstitutions["<enter description here>"] = "%log%"
 
         self.logSubstitutions = {}
         self.logSubstitutions["<enter description here>"] = "%log%"
@@ -230,8 +298,8 @@ class P4Submit(Command):
         if self.directSubmit:
             commits.append("0")
         else:
         if self.directSubmit:
             commits.append("0")
         else:
-            for line in mypopen("git rev-list --no-merges %s..%s" % (self.origin, self.master)).readlines():
-                commits.append(line[:-1])
+            for line in read_pipe_lines("git rev-list --no-merges %s..%s" % (self.origin, self.master)):
+                commits.append(line.strip())
             commits.reverse()
 
         self.config["commits"] = commits
             commits.reverse()
 
         self.config["commits"] = commits
@@ -259,13 +327,13 @@ class P4Submit(Command):
 
         return result
 
 
         return result
 
-    def apply(self, id):
+    def applyCommit(self, id):
         if self.directSubmit:
             print "Applying local change in working directory/index"
             diff = self.diffStatus
         else:
         if self.directSubmit:
             print "Applying local change in working directory/index"
             diff = self.diffStatus
         else:
-            print "Applying %s" % (mypopen("git log --max-count=1 --pretty=oneline %s" % id).read())
-            diff = mypopen("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)).readlines()
+            print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id))
+            diff = read_pipe_lines("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id))
         filesToAdd = set()
         filesToDelete = set()
         editedFiles = set()
         filesToAdd = set()
         filesToDelete = set()
         editedFiles = set()
@@ -332,13 +400,15 @@ class P4Submit(Command):
         if not self.directSubmit:
             logMessage = extractLogMessageFromGitCommit(id)
             logMessage = logMessage.replace("\n", "\n\t")
         if not self.directSubmit:
             logMessage = extractLogMessageFromGitCommit(id)
             logMessage = logMessage.replace("\n", "\n\t")
-            logMessage = logMessage[:-1]
+            if self.isWindows:
+                logMessage = logMessage.replace("\n", "\r\n")
+            logMessage = logMessage.strip()
 
 
-        template = mypopen("p4 change -o").read()
+        template = read_pipe("p4 change -o")
 
         if self.interactive:
             submitTemplate = self.prepareLogMessage(template, logMessage)
 
         if self.interactive:
             submitTemplate = self.prepareLogMessage(template, logMessage)
-            diff = mypopen("p4 diff -du ...").read()
+            diff = read_pipe("p4 diff -du ...")
 
             for newFile in filesToAdd:
                 diff += "==== new file ====\n"
 
             for newFile in filesToAdd:
                 diff += "==== new file ====\n"
@@ -378,6 +448,8 @@ class P4Submit(Command):
                     tmpFile.close()
                     os.remove(fileName)
                     submitTemplate = message[:message.index(separatorLine)]
                     tmpFile.close()
                     os.remove(fileName)
                     submitTemplate = message[:message.index(separatorLine)]
+                    if self.isWindows:
+                        submitTemplate = submitTemplate.replace("\r\n", "\n")
 
             if response == "y" or response == "yes":
                if self.dryRun:
 
             if response == "y" or response == "yes":
                if self.dryRun:
@@ -387,14 +459,10 @@ class P4Submit(Command):
                    if self.directSubmit:
                        print "Submitting to git first"
                        os.chdir(self.oldWorkingDirectory)
                    if self.directSubmit:
                        print "Submitting to git first"
                        os.chdir(self.oldWorkingDirectory)
-                       pipe = os.popen("git commit -a -F -", "wb")
-                       pipe.write(submitTemplate)
-                       pipe.close()
+                       write_pipe("git commit -a -F -", submitTemplate)
                        os.chdir(self.clientPath)
 
                        os.chdir(self.clientPath)
 
-                   pipe = os.popen("p4 submit -i", "wb")
-                   pipe.write(submitTemplate)
-                   pipe.close()
+                   write_pipe("p4 submit -i", submitTemplate)
             elif response == "s":
                 for f in editedFiles:
                     system("p4 revert \"%s\"" % f);
             elif response == "s":
                 for f in editedFiles:
                     system("p4 revert \"%s\"" % f);
@@ -417,11 +485,6 @@ class P4Submit(Command):
                    % (fileName, fileName))
 
     def run(self, args):
                    % (fileName, fileName))
 
     def run(self, args):
-        global gitdir
-        # 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 gitBranchExists("refs/heads/%s" % self.master):
         if len(args) == 0:
             self.master = currentGitBranch()
             if len(self.master) == 0 or not gitBranchExists("refs/heads/%s" % self.master):
@@ -432,10 +495,27 @@ class P4Submit(Command):
             return False
 
         depotPath = ""
             return False
 
         depotPath = ""
-        if gitBranchExists("p4"):
-            [depotPath, dummy] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("p4"))
-        if len(depotPath) == 0 and gitBranchExists("origin"):
-            [depotPath, dummy] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("origin"))
+        parent = 0
+        while parent < 65535:
+            commit = "HEAD~%s" % parent
+            log = extractLogMessageFromGitCommit(commit)
+            settings = extractSettingsGitLog(log)
+            if not settings.has_key("depot-paths"):
+                parent = parent + 1
+                continue
+
+            depotPath = settings['depot-paths'][0]
+
+            if len(self.origin) == 0:
+                names = read_pipe_lines("git name-rev '--refs=refs/remotes/p4/*' '%s'" % commit)
+                if len(names) > 0:
+                    # strip away the beginning of 'HEAD~42 refs/remotes/p4/foo'
+                    self.origin = names[0].strip()[len(commit) + 1:]
+
+            break
+
+        if self.verbose:
+            print "Origin branch is " + self.origin
 
         if len(depotPath) == 0:
             print "Internal error: cannot locate perforce depot path from existing branches"
 
         if len(depotPath) == 0:
             print "Internal error: cannot locate perforce depot path from existing branches"
@@ -451,12 +531,12 @@ class P4Submit(Command):
         self.oldWorkingDirectory = os.getcwd()
 
         if self.directSubmit:
         self.oldWorkingDirectory = os.getcwd()
 
         if self.directSubmit:
-            self.diffStatus = mypopen("git diff -r --name-status HEAD").readlines()
+            self.diffStatus = read_pipe_lines("git diff -r --name-status HEAD")
             if len(self.diffStatus) == 0:
                 print "No changes in working directory to submit."
                 return True
             if len(self.diffStatus) == 0:
                 print "No changes in working directory to submit."
                 return True
-            patch = mypopen("git diff -p --binary --diff-filter=ACMRTUXB HEAD").read()
-            self.diffFile = gitdir + "/p4-git-diff"
+            patch = read_pipe("git diff -p --binary --diff-filter=ACMRTUXB HEAD")
+            self.diffFile = self.gitdir + "/p4-git-diff"
             f = open(self.diffFile, "wb")
             f.write(patch)
             f.close();
             f = open(self.diffFile, "wb")
             f.write(patch)
             f.close();
@@ -466,22 +546,16 @@ class P4Submit(Command):
         if response == "y" or response == "yes":
             system("p4 sync ...")
 
         if response == "y" or response == "yes":
             system("p4 sync ...")
 
-        if len(self.origin) == 0:
-            if gitBranchExists("p4"):
-                self.origin = "p4"
-            else:
-                self.origin = "origin"
-
         if self.reset:
             self.firstTime = True
 
         if len(self.substFile) > 0:
             for line in open(self.substFile, "r").readlines():
         if self.reset:
             self.firstTime = True
 
         if len(self.substFile) > 0:
             for line in open(self.substFile, "r").readlines():
-                tokens = line[:-1].split("=")
+                tokens = line.strip().split("=")
                 self.logSubstitutions[tokens[0]] = tokens[1]
 
         self.check()
                 self.logSubstitutions[tokens[0]] = tokens[1]
 
         self.check()
-        self.configFile = gitdir + "/p4-git-sync.cfg"
+        self.configFile = self.gitdir + "/p4-git-sync.cfg"
         self.config = shelve.open(self.configFile, writeback=True)
 
         if self.firstTime:
         self.config = shelve.open(self.configFile, writeback=True)
 
         if self.firstTime:
@@ -494,7 +568,7 @@ class P4Submit(Command):
             commit = commits[0]
             commits = commits[1:]
             self.config["commits"] = commits
             commit = commits[0]
             commits = commits[1:]
             self.config["commits"] = commits
-            self.apply(commit)
+            self.applyCommit(commit)
             if not self.interactive:
                 break
 
             if not self.interactive:
                 break
 
@@ -527,8 +601,11 @@ class P4Sync(Command):
                 optparse.make_option("--silent", dest="silent", action="store_true"),
                 optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"),
                 optparse.make_option("--verbose", dest="verbose", action="store_true"),
                 optparse.make_option("--silent", dest="silent", action="store_true"),
                 optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"),
                 optparse.make_option("--verbose", dest="verbose", action="store_true"),
-                optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false"),
-                optparse.make_option("--max-changes", dest="maxChanges")
+                optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false",
+                                     help="Import into refs/heads/ , not refs/remotes"),
+                optparse.make_option("--max-changes", dest="maxChanges"),
+                optparse.make_option("--keep-path", dest="keepRepoPath", action='store_true',
+                                     help="Keep entire BRANCH/DIR/SUBDIR prefix during import")
         ]
         self.description = """Imports from Perforce into a git repository.\n
     example:
         ]
         self.description = """Imports from Perforce into a git repository.\n
     example:
@@ -539,7 +616,6 @@ class P4Sync(Command):
     (a ... is not needed in the path p4 specification, it's added implicitly)"""
 
         self.usage += " //depot/path[@revRange]"
     (a ... is not needed in the path p4 specification, it's added implicitly)"""
 
         self.usage += " //depot/path[@revRange]"
-
         self.silent = False
         self.createdBranches = Set()
         self.committedChanges = Set()
         self.silent = False
         self.createdBranches = Set()
         self.committedChanges = Set()
@@ -552,21 +628,21 @@ class P4Sync(Command):
         self.importIntoRemotes = True
         self.maxChanges = ""
         self.isWindows = (platform.system() == "Windows")
         self.importIntoRemotes = True
         self.maxChanges = ""
         self.isWindows = (platform.system() == "Windows")
+        self.keepRepoPath = False
+        self.depotPaths = None
 
         if gitConfig("git-p4.syncFromOrigin") == "false":
             self.syncWithOrigin = False
 
 
         if gitConfig("git-p4.syncFromOrigin") == "false":
             self.syncWithOrigin = False
 
-    def p4File(self, depotPath):
-        return os.popen("p4 print -q \"%s\"" % depotPath, "rb").read()
-
     def extractFilesFromCommit(self, commit):
         files = []
         fnum = 0
         while commit.has_key("depotFile%s" % fnum):
             path =  commit["depotFile%s" % fnum]
     def extractFilesFromCommit(self, commit):
         files = []
         fnum = 0
         while commit.has_key("depotFile%s" % fnum):
             path =  commit["depotFile%s" % fnum]
-            if not path.startswith(self.depotPath):
-    #            if not self.silent:
-    #                print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, self.depotPath, change)
+
+            found = [p for p in self.depotPaths
+                     if path.startswith (p)]
+            if not found:
                 fnum = fnum + 1
                 continue
 
                 fnum = fnum + 1
                 continue
 
@@ -579,15 +655,24 @@ class P4Sync(Command):
             fnum = fnum + 1
         return files
 
             fnum = fnum + 1
         return files
 
+    def stripRepoPath(self, path, prefixes):
+        if self.keepRepoPath:
+            prefixes = [re.sub("^(//[^/]+/).*", r'\1', prefixes[0])]
+
+        for p in prefixes:
+            if path.startswith(p):
+                path = path[len(p):]
+
+        return path
+
     def splitFilesIntoBranches(self, commit):
         branches = {}
     def splitFilesIntoBranches(self, commit):
         branches = {}
-
         fnum = 0
         while commit.has_key("depotFile%s" % fnum):
             path =  commit["depotFile%s" % fnum]
         fnum = 0
         while commit.has_key("depotFile%s" % fnum):
             path =  commit["depotFile%s" % fnum]
-            if not path.startswith(self.depotPath):
-    #            if not self.silent:
-    #                print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, self.depotPath, change)
+            found = [p for p in self.depotPaths
+                     if path.startswith (p)]
+            if not found:
                 fnum = fnum + 1
                 continue
 
                 fnum = fnum + 1
                 continue
 
@@ -598,25 +683,75 @@ class P4Sync(Command):
             file["type"] = commit["type%s" % fnum]
             fnum = fnum + 1
 
             file["type"] = commit["type%s" % fnum]
             fnum = fnum + 1
 
-            relPath = path[len(self.depotPath):]
+            relPath = self.stripRepoPath(path, self.depotPaths)
 
             for branch in self.knownBranches.keys():
 
             for branch in self.knownBranches.keys():
-                if relPath.startswith(branch + "/"): # add a trailing slash so that a commit into qt/4.2foo doesn't end up in qt/4.2
+
+                # add a trailing slash so that a commit into qt/4.2foo doesn't end up in qt/4.2
+                if relPath.startswith(branch + "/"):
                     if branch not in branches:
                         branches[branch] = []
                     branches[branch].append(file)
 
         return branches
 
                     if branch not in branches:
                         branches[branch] = []
                     branches[branch].append(file)
 
         return branches
 
-    def commit(self, details, files, branch, branchPrefix, parent = ""):
+    ## Should move this out, doesn't use SELF.
+    def readP4Files(self, files):
+        files = [f for f in files
+                 if f['action'] != 'delete']
+
+        if not files:
+            return
+
+        filedata = p4CmdList('print %s' % ' '.join(['"%s#%s"' % (f['path'],
+                                                                 f['rev'])
+                                                    for f in files]))
+
+        j = 0;
+        contents = {}
+        while j < len(filedata):
+            stat = filedata[j]
+            j += 1
+            text = ''
+            while j < len(filedata) and filedata[j]['code'] in ('text',
+                                                                'binary'):
+                text += filedata[j]['data']
+                j += 1
+
+
+            if not stat.has_key('depotFile'):
+                sys.stderr.write("p4 print fails with: %s\n" % repr(stat))
+                continue
+
+            contents[stat['depotFile']] = text
+
+        for f in files:
+            assert not f.has_key('data')
+            f['data'] = contents[f['path']]
+
+    def commit(self, details, files, branch, branchPrefixes, parent = ""):
         epoch = details["time"]
         author = details["user"]
 
         if self.verbose:
             print "commit into %s" % branch
 
         epoch = details["time"]
         author = details["user"]
 
         if self.verbose:
             print "commit into %s" % branch
 
+        # start with reading files; if that fails, we should not
+        # create a commit.
+        new_files = []
+        for f in files:
+            if [p for p in branchPrefixes if f['path'].startswith(p)]:
+                new_files.append (f)
+            else:
+                sys.stderr.write("Ignoring file outside of prefix: %s\n" % path)
+        files = new_files
+        self.readP4Files(files)
+
+
+
+
         self.gitStream.write("commit %s\n" % branch)
         self.gitStream.write("commit %s\n" % branch)
-    #    gitStream.write("mark :%s\n" % details["change"])
+#        gitStream.write("mark :%s\n" % details["change"])
         self.committedChanges.add(int(details["change"]))
         committer = ""
         if author not in self.users:
         self.committedChanges.add(int(details["change"]))
         committer = ""
         if author not in self.users:
@@ -630,7 +765,11 @@ class P4Sync(Command):
 
         self.gitStream.write("data <<EOT\n")
         self.gitStream.write(details["desc"])
 
         self.gitStream.write("data <<EOT\n")
         self.gitStream.write(details["desc"])
-        self.gitStream.write("\n[git-p4: depot-path = \"%s\": change = %s]\n" % (branchPrefix, details["change"]))
+        self.gitStream.write("\n[git-p4: depot-paths = \"%s\": change = %s: "
+                             "options = %s]\n"
+                             % (','.join (branchPrefixes), details["change"],
+                                details['options']
+                                ))
         self.gitStream.write("EOT\n\n")
 
         if len(parent) > 0:
         self.gitStream.write("EOT\n\n")
 
         if len(parent) > 0:
@@ -639,32 +778,24 @@ class P4Sync(Command):
             self.gitStream.write("from %s\n" % parent)
 
         for file in files:
             self.gitStream.write("from %s\n" % parent)
 
         for file in files:
-            path = file["path"]
-            if not path.startswith(branchPrefix):
-    #                print "\nchanged files: ignoring path %s outside of branch prefix %s in change %s" % (path, branchPrefix, details["change"])
-                continue
-            rev = file["rev"]
-            depotPath = path + "#" + rev
-            relPath = path[len(branchPrefix):]
-            action = file["action"]
-
             if file["type"] == "apple":
             if file["type"] == "apple":
-                print "\nfile %s is a strange apple file that forks. Ignoring!" % path
+                print "\nfile %s is a strange apple file that forks. Ignoring!" % file['path']
                 continue
 
                 continue
 
-            if action == "delete":
+            relPath = self.stripRepoPath(file['path'], branchPrefixes)
+            if file["action"] == "delete":
                 self.gitStream.write("D %s\n" % relPath)
             else:
                 mode = 644
                 if file["type"].startswith("x"):
                     mode = 755
 
                 self.gitStream.write("D %s\n" % relPath)
             else:
                 mode = 644
                 if file["type"].startswith("x"):
                     mode = 755
 
-                data = self.p4File(depotPath)
+                data = file['data']
 
                 if self.isWindows and file["type"].endswith("text"):
                     data = data.replace("\r\n", "\n")
 
 
                 if self.isWindows and file["type"].endswith("text"):
                     data = data.replace("\r\n", "\n")
 
-                self.gitStream.write("M %s inline %s\n" % (mode, relPath))
+                self.gitStream.write("M %d inline %s\n" % (mode, relPath))
                 self.gitStream.write("data %s\n" % len(data))
                 self.gitStream.write(data)
                 self.gitStream.write("\n")
                 self.gitStream.write("data %s\n" % len(data))
                 self.gitStream.write(data)
                 self.gitStream.write("\n")
@@ -680,7 +811,8 @@ class P4Sync(Command):
             if self.verbose:
                 print "Change %s is labelled %s" % (change, labelDetails)
 
             if self.verbose:
                 print "Change %s is labelled %s" % (change, labelDetails)
 
-            files = p4CmdList("files %s...@%s" % (branchPrefix, change))
+            files = p4CmdList("files " + ' '.join (["%s...@%s" % (p, change)
+                                                    for p in branchPrefixes]))
 
             if len(files) == len(labelRevisions):
 
 
             if len(files) == len(labelRevisions):
 
@@ -715,6 +847,9 @@ class P4Sync(Command):
                     print ("Tag %s does not match with change %s: file count is different."
                            % (labelDetails["label"], change))
 
                     print ("Tag %s does not match with change %s: file count is different."
                            % (labelDetails["label"], change))
 
+    def getUserCacheFilename(self):
+        return os.environ["HOME"] + "/.gitp4-usercache.txt"
+
     def getUserMapFromPerforceServer(self):
         if self.userMapFromPerforceServer:
             return
     def getUserMapFromPerforceServer(self):
         if self.userMapFromPerforceServer:
             return
@@ -725,21 +860,23 @@ class P4Sync(Command):
                 continue
             self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">"
 
                 continue
             self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">"
 
-        cache = open(gitdir + "/p4-usercache.txt", "wb")
-        for user in self.users.keys():
-            cache.write("%s\t%s\n" % (user, self.users[user]))
-        cache.close();
+
+        s = ''
+        for (key, val) in self.users.items():
+            s += "%s\t%s\n" % (key, val)
+
+        open(self.getUserCacheFilename(), "wb").write(s)
         self.userMapFromPerforceServer = True
 
     def loadUserMapFromCache(self):
         self.users = {}
         self.userMapFromPerforceServer = False
         try:
         self.userMapFromPerforceServer = True
 
     def loadUserMapFromCache(self):
         self.users = {}
         self.userMapFromPerforceServer = False
         try:
-            cache = open(gitdir + "/p4-usercache.txt", "rb")
+            cache = open(self.getUserCacheFilename(), "rb")
             lines = cache.readlines()
             cache.close()
             for line in lines:
             lines = cache.readlines()
             cache.close()
             for line in lines:
-                entry = line[:-1].split("\t")
+                entry = line.strip().split("\t")
                 self.users[entry[0]] = entry[1]
         except IOError:
             self.getUserMapFromPerforceServer()
                 self.users[entry[0]] = entry[1]
         except IOError:
             self.getUserMapFromPerforceServer()
@@ -747,9 +884,9 @@ class P4Sync(Command):
     def getLabels(self):
         self.labels = {}
 
     def getLabels(self):
         self.labels = {}
 
-        l = p4CmdList("labels %s..." % self.depotPath)
+        l = p4CmdList("labels %s..." % ' '.join (self.depotPaths))
         if len(l) > 0 and not self.silent:
         if len(l) > 0 and not self.silent:
-            print "Finding files belonging to labels in %s" % self.depotPath
+            print "Finding files belonging to labels in %s" % `self.depotPath`
 
         for output in l:
             label = output["label"]
 
         for output in l:
             label = output["label"]
@@ -757,7 +894,9 @@ class P4Sync(Command):
             newestChange = 0
             if self.verbose:
                 print "Querying files for label %s" % label
             newestChange = 0
             if self.verbose:
                 print "Querying files for label %s" % label
-            for file in p4CmdList("files %s...@%s" % (self.depotPath, label)):
+            for file in p4CmdList("files "
+                                  +  ' '.join (["%s...@%s" % (p, label)
+                                                for p in self.depotPaths])):
                 revisions[file["depotFile"]] = file["rev"]
                 change = int(file["change"])
                 if change > newestChange:
                 revisions[file["depotFile"]] = file["rev"]
                 change = int(file["change"])
                 if change > newestChange:
@@ -768,9 +907,11 @@ class P4Sync(Command):
         if self.verbose:
             print "Label changes: %s" % self.labels.keys()
 
         if self.verbose:
             print "Label changes: %s" % self.labels.keys()
 
-    def getBranchMapping(self):
-        self.projectName = self.depotPath[self.depotPath[:-1].rfind("/") + 1:]
+    def guessProjectName(self):
+        for p in self.depotPaths:
+            return p [p.strip().rfind("/") + 1:]
 
 
+    def getBranchMapping(self):
         for info in p4CmdList("branches"):
             details = p4Cmd("branch -o %s" % info["branch"])
             viewIdx = 0
         for info in p4CmdList("branches"):
             details = p4Cmd("branch -o %s" % info["branch"])
             viewIdx = 0
@@ -782,9 +923,10 @@ class P4Sync(Command):
                     continue
                 source = paths[0]
                 destination = paths[1]
                     continue
                 source = paths[0]
                 destination = paths[1]
-                if source.startswith(self.depotPath) and destination.startswith(self.depotPath):
-                    source = source[len(self.depotPath):-4]
-                    destination = destination[len(self.depotPath):-4]
+                ## HACK
+                if source.startswith(self.depotPaths[0]) and destination.startswith(self.depotPaths[0]):
+                    source = source[len(self.depotPaths[0]):-4]
+                    destination = destination[len(self.depotPaths[0]):-4]
                     if destination not in self.knownBranches:
                         self.knownBranches[destination] = source
                     if source not in self.knownBranches:
                     if destination not in self.knownBranches:
                         self.knownBranches[destination] = source
                     if source not in self.knownBranches:
@@ -799,31 +941,37 @@ class P4Sync(Command):
         else:
             cmdline += " --branches"
 
         else:
             cmdline += " --branches"
 
-        for line in mypopen(cmdline).readlines():
-            if self.importIntoRemotes and ((not line.startswith("p4/")) or line == "p4/HEAD\n"):
+        for line in read_pipe_lines(cmdline):
+            line = line.strip()
+
+            ## only import to p4/
+            if not line.startswith('p4/') or line == "p4/HEAD":
                 continue
                 continue
-            if self.importIntoRemotes:
-                # strip off p4
-                branch = line[3:-1]
-            else:
-                branch = line[:-1]
+            branch = line
+
+            # strip off p4
+            branch = re.sub ("^p4/", "", line)
+
             self.p4BranchesInGit.append(branch)
             self.p4BranchesInGit.append(branch)
-            self.initialParents[self.refPrefix + branch] = parseRevision(line[:-1])
+            self.initialParents[self.refPrefix + branch] = parseRevision(line)
 
     def createOrUpdateBranchesFromOrigin(self):
         if not self.silent:
 
     def createOrUpdateBranchesFromOrigin(self):
         if not self.silent:
-            print "Creating/updating branch(es) in %s based on origin branch(es)" % self.refPrefix
+            print ("Creating/updating branch(es) in %s based on origin branch(es)"
+                   % self.refPrefix)
 
 
-        for line in mypopen("git rev-parse --symbolic --remotes"):
-            if (not line.startswith("origin/")) or line.endswith("HEAD\n"):
+        for line in read_pipe_lines("git rev-parse --symbolic --remotes"):
+            line = line.strip()
+            if (not line.startswith("origin/")) or line.endswith("HEAD"):
                 continue
 
                 continue
 
-            headName = line[len("origin/"):-1]
+            headName = line[len("origin/"):]
             remoteHead = self.refPrefix + headName
             originHead = "origin/" + headName
 
             remoteHead = self.refPrefix + headName
             originHead = "origin/" + headName
 
-            [originPreviousDepotPath, originP4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(originHead))
-            if len(originPreviousDepotPath) == 0 or len(originP4Change) == 0:
+            original = extractSettingsGitLog(extractLogMessageFromGitCommit(originHead))
+            if (not original.has_key('depot-paths')
+                or not original.has_key('change')):
                 continue
 
             update = False
                 continue
 
             update = False
@@ -832,25 +980,42 @@ class P4Sync(Command):
                     print "creating %s" % remoteHead
                 update = True
             else:
                     print "creating %s" % remoteHead
                 update = True
             else:
-                [p4PreviousDepotPath, p4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(remoteHead))
-                if len(p4Change) > 0:
-                    if originPreviousDepotPath == p4PreviousDepotPath:
-                        originP4Change = int(originP4Change)
-                        p4Change = int(p4Change)
+                settings = extractSettingsGitLog(extractLogMessageFromGitCommit(remoteHead))
+                if settings.has_key('change') > 0:
+                    if settings['depot-paths'] == original['depot-paths']:
+                        originP4Change = int(original['change'])
+                        p4Change = int(settings['change'])
                         if originP4Change > p4Change:
                         if originP4Change > p4Change:
-                            print "%s (%s) is newer than %s (%s). Updating p4 branch from origin." % (originHead, originP4Change, remoteHead, p4Change)
+                            print ("%s (%s) is newer than %s (%s). "
+                                   "Updating p4 branch from origin."
+                                   % (originHead, originP4Change,
+                                      remoteHead, p4Change))
                             update = True
                     else:
                             update = True
                     else:
-                        print "Ignoring: %s was imported from %s while %s was imported from %s" % (originHead, originPreviousDepotPath, remoteHead, p4PreviousDepotPath)
+                        print ("Ignoring: %s was imported from %s while "
+                               "%s was imported from %s"
+                               % (originHead, ','.join(original['depot-paths']),
+                                  remoteHead, ','.join(settings['depot-paths'])))
 
             if update:
                 system("git update-ref %s %s" % (remoteHead, originHead))
 
 
             if update:
                 system("git update-ref %s %s" % (remoteHead, originHead))
 
+    def updateOptionDict(self, d):
+        option_keys = {}
+        if self.keepRepoPath:
+            option_keys['keepRepoPath'] = 1
+
+        d["options"] = ' '.join(sorted(option_keys.keys()))
+
+    def readOptions(self, d):
+        self.keepRepoPath = (d.has_key('options')
+                             and ('keepRepoPath' in d['options']))
+
     def run(self, args):
     def run(self, args):
-        self.depotPath = ""
+        self.depotPaths = []
         self.changeRange = ""
         self.initialParent = ""
         self.changeRange = ""
         self.initialParent = ""
-        self.previousDepotPath = ""
+        self.previousDepotPaths = []
 
         # map from branch depot path to parent branch
         self.knownBranches = {}
 
         # map from branch depot path to parent branch
         self.knownBranches = {}
@@ -860,7 +1025,7 @@ class P4Sync(Command):
         if self.importIntoRemotes:
             self.refPrefix = "refs/remotes/p4/"
         else:
         if self.importIntoRemotes:
             self.refPrefix = "refs/remotes/p4/"
         else:
-            self.refPrefix = "refs/heads/"
+            self.refPrefix = "refs/heads/p4/"
 
         if self.syncWithOrigin and self.hasOrigin:
             if not self.silent:
 
         if self.syncWithOrigin and self.hasOrigin:
             if not self.silent:
@@ -876,7 +1041,9 @@ class P4Sync(Command):
             if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes:
                 system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch))
 
             if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes:
                 system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch))
 
-        if len(args) == 0:
+        # TODO: should always look at previous commits,
+        # merge with previous imports, if possible.
+        if args == []:
             if self.hasOrigin:
                 self.createOrUpdateBranchesFromOrigin()
             self.listExistingP4GitBranches()
             if self.hasOrigin:
                 self.createOrUpdateBranchesFromOrigin()
             self.listExistingP4GitBranches()
@@ -892,73 +1059,82 @@ class P4Sync(Command):
             p4Change = 0
             for branch in self.p4BranchesInGit:
                 logMsg =  extractLogMessageFromGitCommit(self.refPrefix + branch)
             p4Change = 0
             for branch in self.p4BranchesInGit:
                 logMsg =  extractLogMessageFromGitCommit(self.refPrefix + branch)
-                (depotPath, change) = extractDepotPathAndChangeFromGitLog(logMsg)
 
 
-                if self.verbose:
-                    print "path %s change %s" % (depotPath, change)
+                settings = extractSettingsGitLog(logMsg)
 
 
-                if len(depotPath) > 0 and len(change) > 0:
-                    change = int(change) + 1
+                self.readOptions(settings)
+                if (settings.has_key('depot-paths')
+                    and settings.has_key ('change')):
+                    change = int(settings['change']) + 1
                     p4Change = max(p4Change, change)
 
                     p4Change = max(p4Change, change)
 
-                    if len(self.previousDepotPath) == 0:
-                        self.previousDepotPath = depotPath
+                    depotPaths = sorted(settings['depot-paths'])
+                    if self.previousDepotPaths == []:
+                        self.previousDepotPaths = depotPaths
                     else:
                     else:
-                        i = 0
-                        l = min(len(self.previousDepotPath), len(depotPath))
-                        while i < l and self.previousDepotPath[i] == depotPath[i]:
-                            i = i + 1
-                        self.previousDepotPath = self.previousDepotPath[:i]
+                        paths = []
+                        for (prev, cur) in zip(self.previousDepotPaths, depotPaths):
+                            for i in range(0, min(len(cur), len(prev))):
+                                if cur[i] <> prev[i]:
+                                    i = i - 1
+                                    break
+
+                            paths.append (cur[:i + 1])
+
+                        self.previousDepotPaths = paths
 
             if p4Change > 0:
 
             if p4Change > 0:
-                self.depotPath = self.previousDepotPath
+                self.depotPaths = sorted(self.previousDepotPaths)
                 self.changeRange = "@%s,#head" % p4Change
                 self.changeRange = "@%s,#head" % p4Change
-                self.initialParent = parseRevision(self.branch)
+                if not self.detectBranches:
+                    self.initialParent = parseRevision(self.branch)
                 if not self.silent and not self.detectBranches:
                     print "Performing incremental import into %s git branch" % self.branch
 
         if not self.branch.startswith("refs/"):
             self.branch = "refs/heads/" + self.branch
 
                 if not self.silent and not self.detectBranches:
                     print "Performing incremental import into %s git branch" % self.branch
 
         if not self.branch.startswith("refs/"):
             self.branch = "refs/heads/" + self.branch
 
-        if len(self.depotPath) != 0:
-            self.depotPath = self.depotPath[:-1]
-
-        if len(args) == 0 and len(self.depotPath) != 0:
+        if len(args) == 0 and self.depotPaths:
             if not self.silent:
             if not self.silent:
-                print "Depot path: %s" % self.depotPath
-        elif len(args) != 1:
-            return False
+                print "Depot paths: %s" % ' '.join(self.depotPaths)
         else:
         else:
-            if len(self.depotPath) != 0 and self.depotPath != args[0]:
+            if self.depotPaths and self.depotPaths != args:
                 print ("previous import used depot path %s and now %s was specified. "
                 print ("previous import used depot path %s and now %s was specified. "
-                       "This doesn't work!" % (self.depotPath, args[0]))
+                       "This doesn't work!" % (' '.join (self.depotPaths),
+                                               ' '.join (args)))
                 sys.exit(1)
                 sys.exit(1)
-            self.depotPath = args[0]
+
+            self.depotPaths = sorted(args)
 
         self.revision = ""
         self.users = {}
 
 
         self.revision = ""
         self.users = {}
 
-        if self.depotPath.find("@") != -1:
-            atIdx = self.depotPath.index("@")
-            self.changeRange = self.depotPath[atIdx:]
-            if self.changeRange == "@all":
-                self.changeRange = ""
-            elif self.changeRange.find(",") == -1:
-                self.revision = self.changeRange
-                self.changeRange = ""
-            self.depotPath = self.depotPath[0:atIdx]
-        elif self.depotPath.find("#") != -1:
-            hashIdx = self.depotPath.index("#")
-            self.revision = self.depotPath[hashIdx:]
-            self.depotPath = self.depotPath[0:hashIdx]
-        elif len(self.previousDepotPath) == 0:
-            self.revision = "#head"
-
-        if self.depotPath.endswith("..."):
-            self.depotPath = self.depotPath[:-3]
-
-        if not self.depotPath.endswith("/"):
-            self.depotPath += "/"
+        newPaths = []
+        for p in self.depotPaths:
+            if p.find("@") != -1:
+                atIdx = p.index("@")
+                self.changeRange = p[atIdx:]
+                if self.changeRange == "@all":
+                    self.changeRange = ""
+                elif ',' not in self.changeRange:
+                    self.revision = self.changeRange
+                    self.changeRange = ""
+                p = p[0:atIdx]
+            elif p.find("#") != -1:
+                hashIdx = p.index("#")
+                self.revision = p[hashIdx:]
+                p = p[0:hashIdx]
+            elif self.previousDepotPaths == []:
+                self.revision = "#head"
+
+            p = re.sub ("\.\.\.$", "", p)
+            if not p.endswith("/"):
+                p += "/"
+
+            newPaths.append(p)
+
+        self.depotPaths = newPaths
+
 
         self.loadUserMapFromCache()
         self.labels = {}
 
         self.loadUserMapFromCache()
         self.labels = {}
@@ -966,34 +1142,51 @@ class P4Sync(Command):
             self.getLabels();
 
         if self.detectBranches:
             self.getLabels();
 
         if self.detectBranches:
-            self.getBranchMapping();
+            ## FIXME - what's a P4 projectName ?
+            self.projectName = self.guessProjectName()
+
+            if not self.hasOrigin:
+                self.getBranchMapping();
             if self.verbose:
                 print "p4-git branches: %s" % self.p4BranchesInGit
                 print "initial parents: %s" % self.initialParents
             for b in self.p4BranchesInGit:
                 if b != "master":
             if self.verbose:
                 print "p4-git branches: %s" % self.p4BranchesInGit
                 print "initial parents: %s" % self.initialParents
             for b in self.p4BranchesInGit:
                 if b != "master":
+
+                    ## FIXME
                     b = b[len(self.projectName):]
                 self.createdBranches.add(b)
 
         self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) / 60))
 
         importProcess = subprocess.Popen(["git", "fast-import"],
                     b = b[len(self.projectName):]
                 self.createdBranches.add(b)
 
         self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) / 60))
 
         importProcess = subprocess.Popen(["git", "fast-import"],
-                                         stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE);
+                                         stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+                                         stderr=subprocess.PIPE);
         self.gitOutput = importProcess.stdout
         self.gitStream = importProcess.stdin
         self.gitError = importProcess.stderr
 
         self.gitOutput = importProcess.stdout
         self.gitStream = importProcess.stdin
         self.gitError = importProcess.stderr
 
-        if len(self.revision) > 0:
-            print "Doing initial import of %s from revision %s" % (self.depotPath, self.revision)
+        if self.revision:
+            print "Doing initial import of %s from revision %s" % (' '.join(self.depotPaths), self.revision)
 
             details = { "user" : "git perforce import user", "time" : int(time.time()) }
             details["desc"] = ("Initial import of %s from the state at revision %s"
 
             details = { "user" : "git perforce import user", "time" : int(time.time()) }
             details["desc"] = ("Initial import of %s from the state at revision %s"
-                               % (self.depotPath, self.revision))
+                               % (' '.join(self.depotPaths), self.revision))
             details["change"] = self.revision
             newestRevision = 0
 
             fileCnt = 0
             details["change"] = self.revision
             newestRevision = 0
 
             fileCnt = 0
-            for info in p4CmdList("files %s...%s" % (self.depotPath, self.revision)):
+            for info in p4CmdList("files "
+                                  +  ' '.join(["%s...%s"
+                                               % (p, self.revision)
+                                               for p in self.depotPaths])):
+
+                if info['code'] == 'error':
+                    sys.stderr.write("p4 returned an error: %s\n"
+                                     % info['data'])
+                    sys.exit(1)
+
+
                 change = int(info["change"])
                 if change > newestRevision:
                     newestRevision = change
                 change = int(info["change"])
                 if change > newestRevision:
                     newestRevision = change
@@ -1003,15 +1196,15 @@ class P4Sync(Command):
                     #fileCnt = fileCnt + 1
                     continue
 
                     #fileCnt = fileCnt + 1
                     continue
 
-                for prop in [ "depotFile", "rev", "action", "type" ]:
+                for prop in ["depotFile", "rev", "action", "type" ]:
                     details["%s%s" % (prop, fileCnt)] = info[prop]
 
                 fileCnt = fileCnt + 1
 
             details["change"] = newestRevision
                     details["%s%s" % (prop, fileCnt)] = info[prop]
 
                 fileCnt = fileCnt + 1
 
             details["change"] = newestRevision
-
+            self.updateOptionDict(details)
             try:
             try:
-                self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPath)
+                self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPaths)
             except IOError:
                 print "IO error with git fast-import. Is your git version recent enough?"
                 print self.gitError.read()
             except IOError:
                 print "IO error with git fast-import. Is your git version recent enough?"
                 print self.gitError.read()
@@ -1031,8 +1224,11 @@ class P4Sync(Command):
                 changes.sort()
             else:
                 if self.verbose:
                 changes.sort()
             else:
                 if self.verbose:
-                    print "Getting p4 changes for %s...%s" % (self.depotPath, self.changeRange)
-                output = mypopen("p4 changes %s...%s" % (self.depotPath, self.changeRange)).readlines()
+                    print "Getting p4 changes for %s...%s" % (', '.join(self.depotPaths),
+                                                              self.changeRange)
+                assert self.depotPaths
+                output = read_pipe_lines("p4 changes " + ' '.join (["%s...%s" % (p, self.changeRange)
+                                                                    for p in self.depotPaths]))
 
                 for line in output:
                     changeNum = line.split(" ")[1]
 
                 for line in output:
                     changeNum = line.split(" ")[1]
@@ -1053,6 +1249,7 @@ class P4Sync(Command):
             cnt = 1
             for change in changes:
                 description = p4Cmd("describe %s" % change)
             cnt = 1
             for change in changes:
                 description = p4Cmd("describe %s" % change)
+                self.updateOptionDict(description)
 
                 if not self.silent:
                     sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes)))
 
                 if not self.silent:
                     sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes)))
@@ -1063,7 +1260,8 @@ class P4Sync(Command):
                     if self.detectBranches:
                         branches = self.splitFilesIntoBranches(description)
                         for branch in branches.keys():
                     if self.detectBranches:
                         branches = self.splitFilesIntoBranches(description)
                         for branch in branches.keys():
-                            branchPrefix = self.depotPath + branch + "/"
+                            ## HACK  --hwn
+                            branchPrefix = self.depotPaths[0] + branch + "/"
 
                             parent = ""
 
 
                             parent = ""
 
@@ -1086,11 +1284,14 @@ class P4Sync(Command):
                             if branch == "main":
                                 branch = "master"
                             else:
                             if branch == "main":
                                 branch = "master"
                             else:
+
+                                ## FIXME
                                 branch = self.projectName + branch
 
                             if parent == "main":
                                 parent = "master"
                             elif len(parent) > 0:
                                 branch = self.projectName + branch
 
                             if parent == "main":
                                 parent = "master"
                             elif len(parent) > 0:
+                                ## FIXME
                                 parent = self.projectName + parent
 
                             branch = self.refPrefix + branch
                                 parent = self.projectName + parent
 
                             branch = self.refPrefix + branch
@@ -1107,7 +1308,8 @@ class P4Sync(Command):
                             self.commit(description, filesForCommit, branch, branchPrefix, parent)
                     else:
                         files = self.extractFilesFromCommit(description)
                             self.commit(description, filesForCommit, branch, branchPrefix, parent)
                     else:
                         files = self.extractFilesFromCommit(description)
-                        self.commit(description, files, self.branch, self.depotPath, self.initialParent)
+                        self.commit(description, files, self.branch, self.depotPaths,
+                                    self.initialParent)
                         self.initialParent = ""
                 except IOError:
                     print self.gitError.read()
                         self.initialParent = ""
                 except IOError:
                     print self.gitError.read()
@@ -1136,12 +1338,13 @@ class P4Rebase(Command):
         self.options = [ ]
         self.description = ("Fetches the latest revision from perforce and "
                             + "rebases the current work (branch) against it")
         self.options = [ ]
         self.description = ("Fetches the latest revision from perforce and "
                             + "rebases the current work (branch) against it")
+        self.verbose = False
 
     def run(self, args):
         sync = P4Sync()
         sync.run([])
         print "Rebasing the current branch"
 
     def run(self, args):
         sync = P4Sync()
         sync.run([])
         print "Rebasing the current branch"
-        oldHead = mypopen("git rev-parse HEAD").read()[:-1]
+        oldHead = read_pipe("git rev-parse HEAD").strip()
         system("git rebase p4")
         system("git diff-tree --stat --summary -M %s HEAD" % oldHead)
         return True
         system("git rebase p4")
         system("git diff-tree --stat --summary -M %s HEAD" % oldHead)
         return True
@@ -1150,38 +1353,50 @@ class P4Clone(P4Sync):
     def __init__(self):
         P4Sync.__init__(self)
         self.description = "Creates a new git repository and imports from Perforce into it"
     def __init__(self):
         P4Sync.__init__(self)
         self.description = "Creates a new git repository and imports from Perforce into it"
-        self.usage = "usage: %prog [options] //depot/path[@revRange] [directory]"
+        self.usage = "usage: %prog [options] //depot/path[@revRange]"
+        self.options.append(
+            optparse.make_option("--destination", dest="cloneDestination",
+                                 action='store', default=None,
+                                 help="where to leave result of the clone"))
+        self.cloneDestination = None
         self.needsGit = False
 
         self.needsGit = False
 
-    def run(self, args):
-        global gitdir
-
-        if len(args) < 1:
-            return False
+    def defaultDestination(self, args):
+        ## TODO: use common prefix of args?
         depotPath = args[0]
         depotPath = args[0]
-        destination = ""
-        if len(args) == 2:
-            destination = args[1]
-        elif len(args) > 2:
-            return False
-
-        if not depotPath.startswith("//"):
-            return False
-
         depotDir = re.sub("(@[^@]*)$", "", depotPath)
         depotDir = re.sub("(#[^#]*)$", "", depotDir)
         depotDir = re.sub(r"\.\.\.$,", "", depotDir)
         depotDir = re.sub(r"/$", "", depotDir)
         depotDir = re.sub("(@[^@]*)$", "", depotPath)
         depotDir = re.sub("(#[^#]*)$", "", depotDir)
         depotDir = re.sub(r"\.\.\.$,", "", depotDir)
         depotDir = re.sub(r"/$", "", depotDir)
+        return os.path.split(depotDir)[1]
 
 
-        if not destination:
-            destination = os.path.split(depotDir)[-1]
+    def run(self, args):
+        if len(args) < 1:
+            return False
+
+        if self.keepRepoPath and not self.cloneDestination:
+            sys.stderr.write("Must specify destination for --keep-path\n")
+            sys.exit(1)
+
+        depotPaths = args
+
+        if not self.cloneDestination and len(depotPaths) > 1:
+            self.cloneDestination = depotPaths[-1]
+            depotPaths = depotPaths[:-1]
+
+        for p in depotPaths:
+            if not p.startswith("//"):
+                return False
+
+        if not self.cloneDestination:
+            self.cloneDestination = self.defaultDestination(args)
 
 
-        print "Importing from %s into %s" % (depotPath, destination)
-        os.makedirs(destination)
-        os.chdir(destination)
+        print "Importing from %s into %s" % (', '.join(depotPaths), self.cloneDestination)
+        os.makedirs(self.cloneDestination)
+        os.chdir(self.cloneDestination)
         system("git init")
         system("git init")
-        gitdir = os.getcwd() + "/.git"
-        if not P4Sync.run(self, [depotPath]):
+        self.gitdir = os.getcwd() + "/.git"
+        if not P4Sync.run(self, depotPaths):
             return False
         if self.branch != "master":
             if gitBranchExists("refs/remotes/p4/master"):
             return False
         if self.branch != "master":
             if gitBranchExists("refs/remotes/p4/master"):
@@ -1189,6 +1404,7 @@ class P4Clone(P4Sync):
                 system("git checkout -f")
             else:
                 print "Could not detect main branch. No checkout/master branch created."
                 system("git checkout -f")
             else:
                 print "Could not detect main branch. No checkout/master branch created."
+
         return True
 
 class HelpFormatter(optparse.IndentedHelpFormatter):
         return True
 
 class HelpFormatter(optparse.IndentedHelpFormatter):
@@ -1210,62 +1426,68 @@ def printUsage(commands):
     print ""
 
 commands = {
     print ""
 
 commands = {
-    "debug" : P4Debug(),
-    "submit" : P4Submit(),
-    "sync" : P4Sync(),
-    "rebase" : P4Rebase(),
-    "clone" : P4Clone(),
-    "rollback" : P4RollBack()
+    "debug" : P4Debug,
+    "submit" : P4Submit,
+    "sync" : P4Sync,
+    "rebase" : P4Rebase,
+    "clone" : P4Clone,
+    "rollback" : P4RollBack
 }
 
 }
 
-if len(sys.argv[1:]) == 0:
-    printUsage(commands.keys())
-    sys.exit(2)
 
 
-cmd = ""
-cmdName = sys.argv[1]
-try:
-    cmd = commands[cmdName]
-except KeyError:
-    print "unknown command %s" % cmdName
-    print ""
-    printUsage(commands.keys())
-    sys.exit(2)
-
-options = cmd.options
-cmd.gitdir = gitdir
-
-args = sys.argv[2:]
-
-if len(options) > 0:
-    options.append(optparse.make_option("--git-dir", dest="gitdir"))
-
-    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):
-            gitdir = mypopen("git rev-parse --git-dir").read()[:-1]
-            if os.path.exists(gitdir):
-                cdup = mypopen("git rev-parse --show-cdup").read()[:-1];
-                if len(cdup) > 0:
-                    os.chdir(cdup);
-
-    if not isValidGitDir(gitdir):
-        if isValidGitDir(gitdir + "/.git"):
-            gitdir += "/.git"
-        else:
-            die("fatal: cannot locate git repository at %s" % gitdir)
+def main():
+    if len(sys.argv[1:]) == 0:
+        printUsage(commands.keys())
+        sys.exit(2)
+
+    cmd = ""
+    cmdName = sys.argv[1]
+    try:
+        klass = commands[cmdName]
+        cmd = klass()
+    except KeyError:
+        print "unknown command %s" % cmdName
+        print ""
+        printUsage(commands.keys())
+        sys.exit(2)
+
+    options = cmd.options
+    cmd.gitdir = os.environ.get("GIT_DIR", None)
+
+    args = sys.argv[2:]
+
+    if len(options) > 0:
+        options.append(optparse.make_option("--git-dir", dest="gitdir"))
+
+        parser = optparse.OptionParser(cmd.usage.replace("%prog", "%prog " + cmdName),
+                                       options,
+                                       description = cmd.description,
+                                       formatter = HelpFormatter())
+
+        (cmd, args) = parser.parse_args(sys.argv[2:], cmd);
+    global verbose
+    verbose = cmd.verbose
+    if cmd.needsGit:
+        if cmd.gitdir == None:
+            cmd.gitdir = os.path.abspath(".git")
+            if not isValidGitDir(cmd.gitdir):
+                cmd.gitdir = read_pipe("git rev-parse --git-dir").strip()
+                if os.path.exists(cmd.gitdir):
+                    cdup = read_pipe("git rev-parse --show-cdup").strip()
+                    if len(cdup) > 0:
+                        os.chdir(cdup);
+
+        if not isValidGitDir(cmd.gitdir):
+            if isValidGitDir(cmd.gitdir + "/.git"):
+                cmd.gitdir += "/.git"
+            else:
+                die("fatal: cannot locate git repository at %s" % cmd.gitdir)
+
+        os.environ["GIT_DIR"] = cmd.gitdir
 
 
-    os.environ["GIT_DIR"] = gitdir
+    if not cmd.run(args):
+        parser.print_help()
 
 
-if not cmd.run(args):
-    parser.print_help()
 
 
+if __name__ == '__main__':
+    main()