Merge branch 'bc/git-p4-for-python-2.4'
authorJunio C Hamano <gitster@pobox.com>
Fri, 1 Feb 2013 20:40:10 +0000 (12:40 -0800)
committerJunio C Hamano <gitster@pobox.com>
Fri, 1 Feb 2013 20:40:10 +0000 (12:40 -0800)
With small updates to remove dependency on newer features of
Python, keep git-p4 usable with older Python.

* bc/git-p4-for-python-2.4:
INSTALL: git-p4 does not support Python 3
git-p4.py: support Python 2.4
git-p4.py: support Python 2.5

1  2 
git-p4.py
diff --combined git-p4.py
index 2da564995dd82ac3fa96d8d7ad6273152f6f161d,0682e61e90dca3f88f399c20aa19c5336274bc95..625a3968c29f3097148788e8289b067f3c83a441
+++ b/git-p4.py
@@@ -8,16 -8,25 +8,31 @@@
  # License: MIT <http://www.opensource.org/licenses/mit-license.php>
  #
  
 -import optparse, sys, os, marshal, subprocess, shelve
 +import sys
 +if sys.hexversion < 0x02040000:
 +    # The limiter is the subprocess module
 +    sys.stderr.write("git-p4: requires Python 2.4 or later.\n")
 +    sys.exit(1)
 +
 +import optparse, os, marshal, subprocess, shelve
  import tempfile, getopt, os.path, time, platform
  import re, shutil
  
+ try:
+     from subprocess import CalledProcessError
+ except ImportError:
+     # from python2.7:subprocess.py
+     # Exception classes used by this module.
+     class CalledProcessError(Exception):
+         """This exception is raised when a process run by check_call() returns
+         a non-zero exit status.  The exit status will be stored in the
+         returncode attribute."""
+         def __init__(self, returncode, cmd):
+             self.returncode = returncode
+             self.cmd = cmd
+         def __str__(self):
+             return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode)
  verbose = False
  
  # Only labels/tags matching this will be imported/exported
@@@ -158,13 -167,17 +173,17 @@@ def system(cmd)
      expand = isinstance(cmd,basestring)
      if verbose:
          sys.stderr.write("executing %s\n" % str(cmd))
-     subprocess.check_call(cmd, shell=expand)
+     retcode = subprocess.call(cmd, shell=expand)
+     if retcode:
+         raise CalledProcessError(retcode, cmd)
  
  def p4_system(cmd):
      """Specifically invoke p4 as the system command. """
      real_cmd = p4_build_cmd(cmd)
      expand = isinstance(real_cmd, basestring)
-     subprocess.check_call(real_cmd, shell=expand)
+     retcode = subprocess.call(real_cmd, shell=expand)
+     if retcode:
+         raise CalledProcessError(retcode, real_cmd)
  
  def p4_integrate(src, dest):
      p4_system(["integrate", "-Dt", wildcard_encode(src), wildcard_encode(dest)])
@@@ -553,49 -566,29 +572,49 @@@ def gitConfigList(key)
          _gitConfig[key] = read_pipe("git config --get-all %s" % key, ignore_error=True).strip().split(os.linesep)
      return _gitConfig[key]
  
 -def p4BranchesInGit(branchesAreInRemotes = True):
 +def p4BranchesInGit(branchesAreInRemotes=True):
 +    """Find all the branches whose names start with "p4/", looking
 +       in remotes or heads as specified by the argument.  Return
 +       a dictionary of { branch: revision } for each one found.
 +       The branch names are the short names, without any
 +       "p4/" prefix."""
 +
      branches = {}
  
      cmdline = "git rev-parse --symbolic "
      if branchesAreInRemotes:
 -        cmdline += " --remotes"
 +        cmdline += "--remotes"
      else:
 -        cmdline += " --branches"
 +        cmdline += "--branches"
  
      for line in read_pipe_lines(cmdline):
          line = line.strip()
  
 -        ## only import to p4/
 -        if not line.startswith('p4/') or line == "p4/HEAD":
 +        # only import to p4/
 +        if not line.startswith('p4/'):
 +            continue
 +        # special symbolic ref to p4/master
 +        if line == "p4/HEAD":
              continue
 -        branch = line
  
 -        # strip off p4
 -        branch = re.sub ("^p4/", "", line)
 +        # strip off p4/ prefix
 +        branch = line[len("p4/"):]
  
          branches[branch] = parseRevision(line)
 +
      return branches
  
 +def branch_exists(branch):
 +    """Make sure that the given ref name really exists."""
 +
 +    cmd = [ "git", "rev-parse", "--symbolic", "--verify", branch ]
 +    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 +    out, _ = p.communicate()
 +    if p.returncode:
 +        return False
 +    # expect exactly one line of output: the branch name
 +    return out.rstrip() == branch
 +
  def findUpstreamBranchPoint(head = "HEAD"):
      branches = p4BranchesInGit()
      # map from depot-path to branch name
@@@ -768,7 -761,8 +787,8 @@@ def wildcard_encode(path)
      return path
  
  def wildcard_present(path):
-     return path.translate(None, "*#@%") != path
+     m = re.search("[*#@%]", path)
+     return m is not None
  
  class Command:
      def __init__(self):
@@@ -927,8 -921,7 +947,8 @@@ class P4Submit(Command, P4UserMap)
                  optparse.make_option("--dry-run", "-n", dest="dry_run", action="store_true"),
                  optparse.make_option("--prepare-p4-only", dest="prepare_p4_only", action="store_true"),
                  optparse.make_option("--conflict", dest="conflict_behavior",
 -                                     choices=self.conflict_behavior_choices)
 +                                     choices=self.conflict_behavior_choices),
 +                optparse.make_option("--branch", dest="branch"),
          ]
          self.description = "Submit changes from git to the perforce depot."
          self.usage += " [name of git branch to submit into perforce depot]"
          self.isWindows = (platform.system() == "Windows")
          self.exportLabels = False
          self.p4HasMoveCommand = p4_has_move_command()
 +        self.branch = None
  
      def check(self):
          if len(p4CmdList("opened ...")) > 0:
              print "All commits applied!"
  
              sync = P4Sync()
 +            if self.branch:
 +                sync.branch = self.branch
              sync.run([])
  
              rebase = P4Rebase()
@@@ -2533,6 -2523,13 +2553,6 @@@ class P4Sync(Command, P4UserMap)
                  branch = branch[len(self.projectName):]
              self.knownBranches[branch] = branch
  
 -    def listExistingP4GitBranches(self):
 -        # branches holds mapping from name to commit
 -        branches = p4BranchesInGit(self.importIntoRemotes)
 -        self.p4BranchesInGit = branches.keys()
 -        for branch in branches.keys():
 -            self.initialParents[self.refPrefix + branch] = branches[branch]
 -
      def updateOptionDict(self, d):
          option_keys = {}
          if self.keepRepoPath:
                      files = self.extractFilesFromCommit(description)
                      self.commit(description, files, self.branch,
                                  self.initialParent)
 +                    # only needed once, to connect to the previous commit
                      self.initialParent = ""
              except IOError:
                  print self.gitError.read()
      def run(self, args):
          self.depotPaths = []
          self.changeRange = ""
 -        self.initialParent = ""
          self.previousDepotPaths = []
 +        self.hasOrigin = False
  
          # map from branch depot path to parent branch
          self.knownBranches = {}
          self.initialParents = {}
 -        self.hasOrigin = originP4BranchesExist()
 -        if not self.syncWithOrigin:
 -            self.hasOrigin = False
  
          if self.importIntoRemotes:
              self.refPrefix = "refs/remotes/p4/"
          else:
              self.refPrefix = "refs/heads/p4/"
  
 -        if self.syncWithOrigin and self.hasOrigin:
 -            if not self.silent:
 -                print "Syncing with origin first by calling git fetch origin"
 -            system("git fetch origin")
 +        if self.syncWithOrigin:
 +            self.hasOrigin = originP4BranchesExist()
 +            if self.hasOrigin:
 +                if not self.silent:
 +                    print 'Syncing with origin first, using "git fetch origin"'
 +                system("git fetch origin")
  
 +        branch_arg_given = bool(self.branch)
          if len(self.branch) == 0:
              self.branch = self.refPrefix + "master"
              if gitBranchExists("refs/heads/p4") and self.importIntoRemotes:
                  system("git update-ref %s refs/heads/p4" % self.branch)
 -                system("git branch -D p4");
 -            # create it /after/ importing, when master exists
 -            if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes and gitBranchExists(self.branch):
 -                system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch))
 +                system("git branch -D p4")
  
          # accept either the command-line option, or the configuration variable
          if self.useClientSpec:
          if args == []:
              if self.hasOrigin:
                  createOrUpdateBranchesFromOrigin(self.refPrefix, self.silent)
 -            self.listExistingP4GitBranches()
 +
 +            # branches holds mapping from branch name to sha1
 +            branches = p4BranchesInGit(self.importIntoRemotes)
 +
 +            # restrict to just this one, disabling detect-branches
 +            if branch_arg_given:
 +                short = self.branch.split("/")[-1]
 +                if short in branches:
 +                    self.p4BranchesInGit = [ short ]
 +            else:
 +                self.p4BranchesInGit = branches.keys()
  
              if len(self.p4BranchesInGit) > 1:
                  if not self.silent:
                      print "Importing from/into multiple branches"
                  self.detectBranches = True
 +                for branch in branches.keys():
 +                    self.initialParents[self.refPrefix + branch] = \
 +                        branches[branch]
  
              if self.verbose:
                  print "branches: %s" % self.p4BranchesInGit
              if p4Change > 0:
                  self.depotPaths = sorted(self.previousDepotPaths)
                  self.changeRange = "@%s,#head" % p4Change
 -                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
  
 +        # accept multiple ref name abbreviations:
 +        #    refs/foo/bar/branch -> use it exactly
 +        #    p4/branch -> prepend refs/remotes/ or refs/heads/
 +        #    branch -> prepend refs/remotes/p4/ or refs/heads/p4/
          if not self.branch.startswith("refs/"):
 -            self.branch = "refs/heads/" + self.branch
 +            if self.importIntoRemotes:
 +                prepend = "refs/remotes/"
 +            else:
 +                prepend = "refs/heads/"
 +            if not self.branch.startswith("p4/"):
 +                prepend += "p4/"
 +            self.branch = prepend + self.branch
  
          if len(args) == 0 and self.depotPaths:
              if not self.silent:
              else:
                  # catch "git p4 sync" with no new branches, in a repo that
                  # does not have any existing p4 branches
 -                if len(args) == 0 and not self.p4BranchesInGit:
 -                    die("No remote p4 branches.  Perhaps you never did \"git p4 clone\" in here.");
 +                if len(args) == 0:
 +                    if not self.p4BranchesInGit:
 +                        die("No remote p4 branches.  Perhaps you never did \"git p4 clone\" in here.")
 +
 +                    # The default branch is master, unless --branch is used to
 +                    # specify something else.  Make sure it exists, or complain
 +                    # nicely about how to use --branch.
 +                    if not self.detectBranches:
 +                        if not branch_exists(self.branch):
 +                            if branch_arg_given:
 +                                die("Error: branch %s does not exist." % self.branch)
 +                            else:
 +                                die("Error: no branch %s; perhaps specify one with --branch." %
 +                                    self.branch)
 +
                  if self.verbose:
                      print "Getting p4 changes for %s...%s" % (', '.join(self.depotPaths),
                                                                self.changeRange)
  
                  self.updatedBranches = set()
  
 +                if not self.detectBranches:
 +                    if args:
 +                        # start a new branch
 +                        self.initialParent = ""
 +                    else:
 +                        # build on a previous revision
 +                        self.initialParent = parseRevision(self.branch)
 +
                  self.importChanges(changes)
  
                  if not self.silent:
                  read_pipe("git update-ref -d %s" % branch)
              os.rmdir(os.path.join(os.environ.get("GIT_DIR", ".git"), self.tempBranchLocation))
  
 +        # Create a symbolic ref p4/HEAD pointing to p4/<branch> to allow
 +        # a convenient shortcut refname "p4".
 +        if self.importIntoRemotes:
 +            head_ref = self.refPrefix + "HEAD"
 +            if not gitBranchExists(head_ref) and gitBranchExists(self.branch):
 +                system(["git", "symbolic-ref", head_ref, self.branch])
 +
          return True
  
  class P4Rebase(Command):
@@@ -3173,19 -3123,23 +3193,21 @@@ class P4Clone(P4Sync)
          init_cmd = [ "git", "init" ]
          if self.cloneBare:
              init_cmd.append("--bare")
-         subprocess.check_call(init_cmd)
+         retcode = subprocess.call(init_cmd)
+         if retcode:
+             raise CalledProcessError(retcode, init_cmd)
  
          if not P4Sync.run(self, depotPaths):
              return False
 -        if self.branch != "master":
 -            if self.importIntoRemotes:
 -                masterbranch = "refs/remotes/p4/master"
 -            else:
 -                masterbranch = "refs/heads/p4/master"
 -            if gitBranchExists(masterbranch):
 -                system("git branch master %s" % masterbranch)
 -                if not self.cloneBare:
 -                    system("git checkout -f")
 -            else:
 -                print "Could not detect main branch. No checkout/master branch created."
 +
 +        # create a master branch and check out a work tree
 +        if gitBranchExists(self.branch):
 +            system([ "git", "branch", "master", self.branch ])
 +            if not self.cloneBare:
 +                system([ "git", "checkout", "-f" ])
 +        else:
 +            print 'Not checking out any branch, use ' \
 +                  '"git checkout -q -b master <branch>"'
  
          # auto-set this variable if invoked with --use-client-spec
          if self.useClientSpec_from_options: