Code

Merge branch 'pw/p4'
authorJunio C Hamano <gitster@pobox.com>
Mon, 28 Feb 2011 05:58:30 +0000 (21:58 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 28 Feb 2011 05:58:30 +0000 (21:58 -0800)
* pw/p4:
  git-p4: support clone --bare
  git-p4: decode p4 wildcard characters
  git-p4: better message for "git-p4 sync" when not cloned
  git-p4: reinterpret confusing p4 message
  git-p4: accommodate new move/delete type in p4
  git-p4: add missing newline in initial import message
  git-p4: fix key error for p4 problem
  git-p4: test script

1  2 
contrib/fast-import/git-p4

index a92beb6292b49aebb12bfbb4535a94f148b88a3d,725af75995ce2af79814d61045d3c0a489812e62..e35c674e1c26bad06b1cb39cf05816dff82756f6
@@@ -834,6 -834,8 +834,8 @@@ class P4Submit(Command)
          return True
  
  class P4Sync(Command):
+     delete_actions = ( "delete", "move/delete", "purge" )
      def __init__(self):
          Command.__init__(self)
          self.options = [
          if gitConfig("git-p4.syncFromOrigin") == "false":
              self.syncWithOrigin = False
  
+     #
+     # P4 wildcards are not allowed in filenames.  P4 complains
+     # if you simply add them, but you can force it with "-f", in
+     # which case it translates them into %xx encoding internally.
+     # Search for and fix just these four characters.  Do % last so
+     # that fixing it does not inadvertently create new %-escapes.
+     #
+     def wildcard_decode(self, path):
+         # Cannot have * in a filename in windows; untested as to
+         # what p4 would do in such a case.
+         if not self.isWindows:
+             path = path.replace("%2A", "*")
+         path = path.replace("%23", "#") \
+                    .replace("%40", "@") \
+                    .replace("%25", "%")
+         return path
      def extractFilesFromCommit(self, commit):
          self.cloneExclude = [re.sub(r"\.\.\.$", "", path)
                               for path in self.cloneExclude]
          return files
  
      def stripRepoPath(self, path, prefixes):
 +        if self.useClientSpec:
 +
 +            # if using the client spec, we use the output directory
 +            # specified in the client.  For example, a view
 +            #   //depot/foo/branch/... //client/branch/foo/...
 +            # will end up putting all foo/branch files into
 +            #  branch/foo/
 +            for val in self.clientSpecDirs:
 +                if path.startswith(val[0]):
 +                    # replace the depot path with the client path
 +                    path = path.replace(val[0], val[1][1])
 +                    # now strip out the client (//client/...)
 +                    path = re.sub("^(//[^/]+/)", '', path)
 +                    # the rest is all path
 +                    return path
 +
          if self.keepRepoPath:
              prefixes = [re.sub("^(//[^/]+/).*", r'\1', prefixes[0])]
  
            return
  
          relPath = self.stripRepoPath(file['depotFile'], self.branchPrefixes)
+         relPath = self.wildcard_decode(relPath)
          if verbose:
              sys.stderr.write("%s\n" % relPath)
  
              includeFile = True
              for val in self.clientSpecDirs:
                  if f['path'].startswith(val[0]):
 -                    if val[1] <= 0:
 +                    if val[1][0] <= 0:
                          includeFile = False
                      break
  
              if includeFile:
                  filesForCommit.append(f)
-                 if f['action'] not in ('delete', 'move/delete', 'purge'):
-                     filesToRead.append(f)
-                 else:
+                 if f['action'] in self.delete_actions:
                      filesToDelete.append(f)
+                 else:
+                     filesToRead.append(f)
  
          # deleted files...
          for f in filesToDelete:
  
                  cleanedFiles = {}
                  for info in files:
-                     if info["action"] in ("delete", "purge"):
+                     if info["action"] in self.delete_actions:
                          continue
                      cleanedFiles[info["depotFile"]] = info["rev"]
  
          print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), revision, self.branch)
  
          details = { "user" : "git perforce import user", "time" : int(time.time()) }
-         details["desc"] = ("Initial import of %s from the state at revision %s"
+         details["desc"] = ("Initial import of %s from the state at revision %s\n"
                             % (' '.join(self.depotPaths), revision))
          details["change"] = revision
          newestRevision = 0
                                             % (p, revision)
                                             for p in self.depotPaths])):
  
-             if info['code'] == 'error':
+             if 'code' in info and info['code'] == 'error':
                  sys.stderr.write("p4 returned an error: %s\n"
                                   % info['data'])
+                 if info['data'].find("must refer to client") >= 0:
+                     sys.stderr.write("This particular p4 error is misleading.\n")
+                     sys.stderr.write("Perhaps the depot path was misspelled.\n");
+                     sys.stderr.write("Depot path:  %s\n" % " ".join(self.depotPaths))
+                 sys.exit(1)
+             if 'p4ExitCode' in info:
+                 sys.stderr.write("p4 exitcode: %s\n" % info['p4ExitCode'])
                  sys.exit(1)
  
  
              if change > newestRevision:
                  newestRevision = change
  
-             if info["action"] in ("delete", "purge"):
+             if info["action"] in self.delete_actions:
                  # don't increase the file cnt, otherwise details["depotFile123"] will have gaps!
                  #fileCnt = fileCnt + 1
                  continue
          for entry in specList:
              for k,v in entry.iteritems():
                  if k.startswith("View"):
 +
 +                    # p4 has these %%1 to %%9 arguments in specs to
 +                    # reorder paths; which we can't handle (yet :)
 +                    if re.match('%%\d', v) != None:
 +                        print "Sorry, can't handle %%n arguments in client specs"
 +                        sys.exit(1)
 +
                      if v.startswith('"'):
                          start = 1
                      else:
                          start = 0
                      index = v.find("...")
 +
 +                    # save the "client view"; i.e the RHS of the view
 +                    # line that tells the client where to put the
 +                    # files for this view.
 +                    cv = v[index+3:].strip() # +3 to remove previous '...'
 +
 +                    # if the client view doesn't end with a
 +                    # ... wildcard, then we're going to mess up the
 +                    # output directory, so fail gracefully.
 +                    if not cv.endswith('...'):
 +                        print 'Sorry, client view in "%s" needs to end with wildcard' % (k)
 +                        sys.exit(1)
 +                    cv=cv[:-3]
 +
 +                    # now save the view; +index means included, -index
 +                    # means it should be filtered out.
                      v = v[start:index]
                      if v.startswith("-"):
                          v = v[1:]
 -                        temp[v] = -len(v)
 +                        include = -len(v)
                      else:
 -                        temp[v] = len(v)
 +                        include = len(v)
 +
 +                    temp[v] = (include, cv)
 +
          self.clientSpecDirs = temp.items()
 -        self.clientSpecDirs.sort( lambda x, y: abs( y[1] ) - abs( x[1] ) )
 +        self.clientSpecDirs.sort( lambda x, y: abs( y[1][0] ) - abs( x[1][0] ) )
  
      def run(self, args):
          self.depotPaths = []
  
                  changes.sort()
              else:
+                 if not self.p4BranchesInGit:
+                     die("No remote p4 branches.  Perhaps you never did \"git p4 clone\" in here.");
                  if self.verbose:
                      print "Getting p4 changes for %s...%s" % (', '.join(self.depotPaths),
                                                                self.changeRange)
@@@ -1789,10 -1776,13 +1818,13 @@@ class P4Clone(P4Sync)
                                   help="where to leave result of the clone"),
              optparse.make_option("-/", dest="cloneExclude",
                                   action="append", type="string",
-                                  help="exclude depot path")
+                                  help="exclude depot path"),
+             optparse.make_option("--bare", dest="cloneBare",
+                                  action="store_true", default=False),
          ]
          self.cloneDestination = None
          self.needsGit = False
+         self.cloneBare = False
  
      # This is required for the "append" cloneExclude action
      def ensure_value(self, attr, value):
              self.cloneDestination = self.defaultDestination(args)
  
          print "Importing from %s into %s" % (', '.join(depotPaths), self.cloneDestination)
          if not os.path.exists(self.cloneDestination):
              os.makedirs(self.cloneDestination)
          chdir(self.cloneDestination)
-         system("git init")
-         self.gitdir = os.getcwd() + "/.git"
+         init_cmd = [ "git", "init" ]
+         if self.cloneBare:
+             init_cmd.append("--bare")
+         subprocess.check_call(init_cmd)
          if not P4Sync.run(self, depotPaths):
              return False
          if self.branch != "master":
                  masterbranch = "refs/heads/p4/master"
              if gitBranchExists(masterbranch):
                  system("git branch master %s" % masterbranch)
-                 system("git checkout -f")
+                 if not self.cloneBare:
+                     system("git checkout -f")
              else:
                  print "Could not detect main branch. No checkout/master branch created."