Merge branch 'ls/p4-retry-thrice'
authorJunio C Hamano <gitster@pobox.com>
Fri, 16 Dec 2016 23:27:50 +0000 (15:27 -0800)
committerJunio C Hamano <gitster@pobox.com>
Fri, 16 Dec 2016 23:27:50 +0000 (15:27 -0800)
* ls/p4-retry-thrice:
git-p4: add config to retry p4 commands; retry 3 times by default

1  2 
Documentation/git-p4.txt
git-p4.py
diff --combined Documentation/git-p4.txt
index ce40b9a5475307e0d3aad90b4ce6fee26485fab2,656587248cc466143589742b093ae8cb43030263..bae862ddcb9fe85765ea5bb126ced4fad8e98268
@@@ -303,15 -303,6 +303,15 @@@ These options can be used to modify 'gi
        submit manually or revert.  This option always stops after the
        first (oldest) commit.  Git tags are not exported to p4.
  
 +--shelve::
 +      Instead of submitting create a series of shelved changelists.
 +      After creating each shelve, the relevant files are reverted/deleted.
 +      If you have multiple commits pending multiple shelves will be created.
 +
 +--update-shelve CHANGELIST::
 +      Update an existing shelved changelist with this commit. Implies
 +      --shelve.
 +
  --conflict=(ask|skip|quit)::
        Conflicts can occur when applying a commit to p4.  When this
        happens, the default behavior ("ask") is to prompt whether to
@@@ -476,6 -467,10 +476,10 @@@ git-p4.client:
        Client specified as an option to all p4 commands, with
        '-c <client>', including the client spec.
  
+ git-p4.retries::
+       Specifies the number of times to retry a p4 command (notably,
+       'p4 sync') if the network times out. The default value is 3.
  Clone and sync variables
  ~~~~~~~~~~~~~~~~~~~~~~~~
  git-p4.syncFromOrigin::
diff --combined git-p4.py
index 1d4bfe64e298849f37e3a13b138a2f6f2c2b27e7,2422178210f344131a783edd3c1d5acbd0229512..13bf44768b352c2cd5b34e718bdc7aaef7d6014d
+++ b/git-p4.py
@@@ -78,6 -78,11 +78,11 @@@ def p4_build_cmd(cmd)
      if len(client) > 0:
          real_cmd += ["-c", client]
  
+     retries = gitConfigInt("git-p4.retries")
+     if retries is None:
+         # Perform 3 retries by default
+         retries = 3
+     real_cmd += ["-r", str(retries)]
  
      if isinstance(cmd,basestring):
          real_cmd = ' '.join(real_cmd) + ' ' + cmd
@@@ -262,10 -267,6 +267,10 @@@ def p4_revert(f)
  def p4_reopen(type, f):
      p4_system(["reopen", "-t", type, wildcard_encode(f)])
  
 +def p4_reopen_in_change(changelist, files):
 +    cmd = ["reopen", "-c", str(changelist)] + files
 +    p4_system(cmd)
 +
  def p4_move(src, dest):
      p4_system(["move", "-k", wildcard_encode(src), wildcard_encode(dest)])
  
@@@ -1009,20 -1010,18 +1014,20 @@@ class LargeFileSystem(object)
             steps."""
          if self.exceedsLargeFileThreshold(relPath, contents) or self.hasLargeFileExtension(relPath):
              contentTempFile = self.generateTempFile(contents)
 -            (git_mode, contents, localLargeFile) = self.generatePointer(contentTempFile)
 -
 -            # Move temp file to final location in large file system
 -            largeFileDir = os.path.dirname(localLargeFile)
 -            if not os.path.isdir(largeFileDir):
 -                os.makedirs(largeFileDir)
 -            shutil.move(contentTempFile, localLargeFile)
 -            self.addLargeFile(relPath)
 -            if gitConfigBool('git-p4.largeFilePush'):
 -                self.pushFile(localLargeFile)
 -            if verbose:
 -                sys.stderr.write("%s moved to large file system (%s)\n" % (relPath, localLargeFile))
 +            (pointer_git_mode, contents, localLargeFile) = self.generatePointer(contentTempFile)
 +            if pointer_git_mode:
 +                git_mode = pointer_git_mode
 +            if localLargeFile:
 +                # Move temp file to final location in large file system
 +                largeFileDir = os.path.dirname(localLargeFile)
 +                if not os.path.isdir(largeFileDir):
 +                    os.makedirs(largeFileDir)
 +                shutil.move(contentTempFile, localLargeFile)
 +                self.addLargeFile(relPath)
 +                if gitConfigBool('git-p4.largeFilePush'):
 +                    self.pushFile(localLargeFile)
 +                if verbose:
 +                    sys.stderr.write("%s moved to large file system (%s)\n" % (relPath, localLargeFile))
          return (git_mode, contents)
  
  class MockLFS(LargeFileSystem):
@@@ -1062,9 -1061,6 +1067,9 @@@ class GitLFS(LargeFileSystem)
             the actual content. Return also the new location of the actual
             content.
             """
 +        if os.path.getsize(contentFile) == 0:
 +            return (None, '', None)
 +
          pointerProcess = subprocess.Popen(
              ['git', 'lfs', 'pointer', '--file=' + contentFile],
              stdout=subprocess.PIPE
@@@ -1298,12 -1294,6 +1303,12 @@@ class P4Submit(Command, P4UserMap)
                  optparse.make_option("--conflict", dest="conflict_behavior",
                                       choices=self.conflict_behavior_choices),
                  optparse.make_option("--branch", dest="branch"),
 +                optparse.make_option("--shelve", dest="shelve", action="store_true",
 +                                     help="Shelve instead of submit. Shelved files are reverted, "
 +                                     "restoring the workspace to the state before the shelve"),
 +                optparse.make_option("--update-shelve", dest="update_shelve", action="store", type="int",
 +                                     metavar="CHANGELIST",
 +                                     help="update an existing shelved changelist, implies --shelve")
          ]
          self.description = "Submit changes from git to the perforce depot."
          self.usage += " [name of git branch to submit into perforce depot]"
          self.detectRenames = False
          self.preserveUser = gitConfigBool("git-p4.preserveUser")
          self.dry_run = False
 +        self.shelve = False
 +        self.update_shelve = None
          self.prepare_p4_only = False
          self.conflict_behavior = None
          self.isWindows = (platform.system() == "Windows")
                      return 1
          return 0
  
 -    def prepareSubmitTemplate(self):
 +    def prepareSubmitTemplate(self, changelist=None):
          """Run "p4 change -o" to grab a change specification template.
             This does not use "p4 -G", as it is nice to keep the submission
             template in original order, since a human might edit it.
  
          template = ""
          inFilesSection = False
 -        for line in p4_read_pipe_lines(['change', '-o']):
 +        args = ['change', '-o']
 +        if changelist:
 +            args.append(str(changelist))
 +
 +        for line in p4_read_pipe_lines(args):
              if line.endswith("\r\n"):
                  line = line[:-2] + "\n"
              if inFilesSection:
          editedFiles = set()
          pureRenameCopy = set()
          filesToChangeExecBit = {}
 +        all_files = list()
  
          for line in diff:
              diff = parseDiffTreeEntry(line)
              modifier = diff['status']
              path = diff['src']
 +            all_files.append(path)
 +
              if modifier == "M":
                  p4_edit(path)
                  if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
              mode = filesToChangeExecBit[f]
              setP4ExecBit(f, mode)
  
 +        if self.update_shelve:
 +            print("all_files = %s" % str(all_files))
 +            p4_reopen_in_change(self.update_shelve, all_files)
 +
          #
          # Build p4 change description, starting with the contents
          # of the git commit message.
          logMessage = logMessage.strip()
          (logMessage, jobs) = self.separate_jobs_from_description(logMessage)
  
 -        template = self.prepareSubmitTemplate()
 +        template = self.prepareSubmitTemplate(self.update_shelve)
          submitTemplate = self.prepareLogMessage(template, logMessage, jobs)
  
          if self.preserveUser:
                  if self.isWindows:
                      message = message.replace("\r\n", "\n")
                  submitTemplate = message[:message.index(separatorLine)]
 -                p4_write_pipe(['submit', '-i'], submitTemplate)
 +
 +                if self.update_shelve:
 +                    p4_write_pipe(['shelve', '-r', '-i'], submitTemplate)
 +                elif self.shelve:
 +                    p4_write_pipe(['shelve', '-i'], submitTemplate)
 +                else:
 +                    p4_write_pipe(['submit', '-i'], submitTemplate)
 +                    # The rename/copy happened by applying a patch that created a
 +                    # new file.  This leaves it writable, which confuses p4.
 +                    for f in pureRenameCopy:
 +                        p4_sync(f, "-f")
  
                  if self.preserveUser:
                      if p4User:
                          changelist = self.lastP4Changelist()
                          self.modifyChangelistUser(changelist, p4User)
  
 -                # The rename/copy happened by applying a patch that created a
 -                # new file.  This leaves it writable, which confuses p4.
 -                for f in pureRenameCopy:
 -                    p4_sync(f, "-f")
                  submitted = True
  
          finally:
              # skip this patch
 -            if not submitted:
 -                print "Submission cancelled, undoing p4 changes."
 -                for f in editedFiles:
 +            if not submitted or self.shelve:
 +                if self.shelve:
 +                    print ("Reverting shelved files.")
 +                else:
 +                    print ("Submission cancelled, undoing p4 changes.")
 +                for f in editedFiles | filesToDelete:
                      p4_revert(f)
                  for f in filesToAdd:
                      p4_revert(f)
                      os.remove(f)
 -                for f in filesToDelete:
 -                    p4_revert(f)
  
          os.remove(fileName)
          return submitted
          if len(self.origin) == 0:
              self.origin = upstream
  
 +        if self.update_shelve:
 +            self.shelve = True
 +
          if self.preserveUser:
              if not self.canChangeChangelists():
                  die("Cannot preserve user names without p4 super-user or admin permissions")
                          break
  
          chdir(self.oldWorkingDirectory)
 -
 +        shelved_applied = "shelved" if self.shelve else "applied"
          if self.dry_run:
              pass
          elif self.prepare_p4_only:
              pass
          elif len(commits) == len(applied):
 -            print "All commits applied!"
 +            print ("All commits {0}!".format(shelved_applied))
  
              sync = P4Sync()
              if self.branch:
  
          else:
              if len(applied) == 0:
 -                print "No commits applied."
 +                print ("No commits {0}.".format(shelved_applied))
              else:
 -                print "Applied only the commits marked with '*':"
 +                print ("{0} only the commits marked with '*':".format(shelved_applied.capitalize()))
                  for c in commits:
                      if c in applied:
                          star = "*"