Merge branch 'maint'
authorJeff King <peff@peff.net>
Tue, 1 Dec 2015 22:32:38 +0000 (17:32 -0500)
committerJeff King <peff@peff.net>
Tue, 1 Dec 2015 22:32:38 +0000 (17:32 -0500)
* maint:
http: treat config options sslCAPath and sslCAInfo as paths
Documentation/diff: give --word-diff-regex=. example
filter-branch: deal with object name vs. pathname ambiguity in tree-filter
check-ignore: correct documentation about output
git-p4: clean up after p4 submit failure
git-p4: work with a detached head
git-p4: add option to system() to return subshell status
git-p4: add failing test for submit from detached head
remote-http(s): support SOCKS proxies
t5813: avoid creating urls that break on cygwin
Escape Git's exec path in contrib/rerere-train.sh script
allow hooks to ignore their standard input stream
rebase-i-exec: Allow space in SHELL_PATH
Documentation: make environment variable formatting more consistent

1  2 
git-filter-branch.sh
git-p4.py
http.c
transport.c
diff --combined git-filter-branch.sh
index 27c9c54fbd24ef3a2995fd90b04911e5a1d8aed9,ad24c8d854e5d615179a24f7335d58ef1eb2e931..cefd1452c66663fee9b00d1406b1d4f14e7964ac
@@@ -275,41 -275,11 +275,41 @@@ commits=$(wc -l <../revs | tr -d " "
  test $commits -eq 0 && die "Found nothing to rewrite"
  
  # Rewrite the commits
 +report_progress ()
 +{
 +      if test -n "$progress" &&
 +              test $git_filter_branch__commit_count -gt $next_sample_at
 +      then
 +              count=$git_filter_branch__commit_count
 +
 +              now=$(date +%s)
 +              elapsed=$(($now - $start_timestamp))
 +              remaining=$(( ($commits - $count) * $elapsed / $count ))
 +              if test $elapsed -gt 0
 +              then
 +                      next_sample_at=$(( ($elapsed + 1) * $count / $elapsed ))
 +              else
 +                      next_sample_at=$(($next_sample_at + 1))
 +              fi
 +              progress=" ($elapsed seconds passed, remaining $remaining predicted)"
 +      fi
 +      printf "\rRewrite $commit ($count/$commits)$progress    "
 +}
  
  git_filter_branch__commit_count=0
 +
 +progress= start_timestamp=
 +if date '+%s' 2>/dev/null | grep -q '^[0-9][0-9]*$'
 +then
 +      next_sample_at=0
 +      progress="dummy to ensure this is not empty"
 +      start_timestamp=$(date '+%s')
 +fi
 +
  while read commit parents; do
        git_filter_branch__commit_count=$(($git_filter_branch__commit_count+1))
 -      printf "\rRewrite $commit ($git_filter_branch__commit_count/$commits)"
 +
 +      report_progress
  
        case "$filter_subdir" in
        "")
                        die "tree filter failed: $filter_tree"
  
                (
-                       git diff-index -r --name-only --ignore-submodules $commit &&
+                       git diff-index -r --name-only --ignore-submodules $commit -- &&
                        git ls-files --others
                ) > "$tempdir"/tree-state || exit
                git update-index --add --replace --remove --stdin \
diff --combined git-p4.py
index 212ef2be9670bc2fe5573eee856471113eb96889,a79b6d82ab5ac8def38e7a213f0b990f4688379a..13f124061f43a4bc212066f865aaaac39feb5385
+++ b/git-p4.py
@@@ -22,9 -22,6 +22,9 @@@ import platfor
  import re
  import shutil
  import stat
 +import zipfile
 +import zlib
 +import ctypes
  
  try:
      from subprocess import CalledProcessError
@@@ -107,16 -104,6 +107,16 @@@ def chdir(path, is_client_path=False)
          path = os.getcwd()
      os.environ['PWD'] = path
  
 +def calcDiskFree():
 +    """Return free space in bytes on the disk of the given dirname."""
 +    if platform.system() == 'Windows':
 +        free_bytes = ctypes.c_ulonglong(0)
 +        ctypes.windll.kernel32.GetDiskFreeSpaceExW(ctypes.c_wchar_p(os.getcwd()), None, None, ctypes.pointer(free_bytes))
 +        return free_bytes.value
 +    else:
 +        st = os.statvfs(os.getcwd())
 +        return st.f_bavail * st.f_frsize
 +
  def die(msg):
      if verbose:
          raise Exception(msg)
@@@ -203,14 -190,16 +203,16 @@@ def p4_has_move_command()
      # assume it failed because @... was invalid changelist
      return True
  
- def system(cmd):
+ def system(cmd, ignore_error=False):
      expand = isinstance(cmd,basestring)
      if verbose:
          sys.stderr.write("executing %s\n" % str(cmd))
      retcode = subprocess.call(cmd, shell=expand)
-     if retcode:
+     if retcode and not ignore_error:
          raise CalledProcessError(retcode, cmd)
  
+     return retcode
  def p4_system(cmd):
      """Specifically invoke p4 as the system command. """
      real_cmd = p4_build_cmd(cmd)
@@@ -553,7 -542,12 +555,12 @@@ def p4Where(depotPath)
      return clientPath
  
  def currentGitBranch():
-     return read_pipe("git name-rev HEAD").split(" ")[1].strip()
+     retcode = system(["git", "symbolic-ref", "-q", "HEAD"], ignore_error=True)
+     if retcode != 0:
+         # on a detached head
+         return None
+     else:
+         return read_pipe(["git", "name-rev", "HEAD"]).split(" ")[1].strip()
  
  def isValidGitDir(path):
      if (os.path.exists(path + "/HEAD")
@@@ -615,12 -609,9 +622,12 @@@ def gitBranchExists(branch)
  
  _gitConfig = {}
  
 -def gitConfig(key):
 +def gitConfig(key, typeSpecifier=None):
      if not _gitConfig.has_key(key):
 -        cmd = [ "git", "config", key ]
 +        cmd = [ "git", "config" ]
 +        if typeSpecifier:
 +            cmd += [ typeSpecifier ]
 +        cmd += [ key ]
          s = read_pipe(cmd, ignore_error=True)
          _gitConfig[key] = s.strip()
      return _gitConfig[key]
@@@ -631,26 -622,16 +638,26 @@@ def gitConfigBool(key)
         in the config."""
  
      if not _gitConfig.has_key(key):
 -        cmd = [ "git", "config", "--bool", key ]
 +        _gitConfig[key] = gitConfig(key, '--bool') == "true"
 +    return _gitConfig[key]
 +
 +def gitConfigInt(key):
 +    if not _gitConfig.has_key(key):
 +        cmd = [ "git", "config", "--int", key ]
          s = read_pipe(cmd, ignore_error=True)
          v = s.strip()
 -        _gitConfig[key] = v == "true"
 +        try:
 +            _gitConfig[key] = int(gitConfig(key, '--int'))
 +        except ValueError:
 +            _gitConfig[key] = None
      return _gitConfig[key]
  
  def gitConfigList(key):
      if not _gitConfig.has_key(key):
          s = read_pipe(["git", "config", "--get-all", key], ignore_error=True)
          _gitConfig[key] = s.strip().split(os.linesep)
 +        if _gitConfig[key] == ['']:
 +            _gitConfig[key] = []
      return _gitConfig[key]
  
  def p4BranchesInGit(branchesAreInRemotes=True):
@@@ -933,182 -914,6 +940,182 @@@ def wildcard_present(path)
      m = re.search("[*#@%]", path)
      return m is not None
  
 +class LargeFileSystem(object):
 +    """Base class for large file system support."""
 +
 +    def __init__(self, writeToGitStream):
 +        self.largeFiles = set()
 +        self.writeToGitStream = writeToGitStream
 +
 +    def generatePointer(self, cloneDestination, contentFile):
 +        """Return the content of a pointer file that is stored in Git instead of
 +           the actual content."""
 +        assert False, "Method 'generatePointer' required in " + self.__class__.__name__
 +
 +    def pushFile(self, localLargeFile):
 +        """Push the actual content which is not stored in the Git repository to
 +           a server."""
 +        assert False, "Method 'pushFile' required in " + self.__class__.__name__
 +
 +    def hasLargeFileExtension(self, relPath):
 +        return reduce(
 +            lambda a, b: a or b,
 +            [relPath.endswith('.' + e) for e in gitConfigList('git-p4.largeFileExtensions')],
 +            False
 +        )
 +
 +    def generateTempFile(self, contents):
 +        contentFile = tempfile.NamedTemporaryFile(prefix='git-p4-large-file', delete=False)
 +        for d in contents:
 +            contentFile.write(d)
 +        contentFile.close()
 +        return contentFile.name
 +
 +    def exceedsLargeFileThreshold(self, relPath, contents):
 +        if gitConfigInt('git-p4.largeFileThreshold'):
 +            contentsSize = sum(len(d) for d in contents)
 +            if contentsSize > gitConfigInt('git-p4.largeFileThreshold'):
 +                return True
 +        if gitConfigInt('git-p4.largeFileCompressedThreshold'):
 +            contentsSize = sum(len(d) for d in contents)
 +            if contentsSize <= gitConfigInt('git-p4.largeFileCompressedThreshold'):
 +                return False
 +            contentTempFile = self.generateTempFile(contents)
 +            compressedContentFile = tempfile.NamedTemporaryFile(prefix='git-p4-large-file', delete=False)
 +            zf = zipfile.ZipFile(compressedContentFile.name, mode='w')
 +            zf.write(contentTempFile, compress_type=zipfile.ZIP_DEFLATED)
 +            zf.close()
 +            compressedContentsSize = zf.infolist()[0].compress_size
 +            os.remove(contentTempFile)
 +            os.remove(compressedContentFile.name)
 +            if compressedContentsSize > gitConfigInt('git-p4.largeFileCompressedThreshold'):
 +                return True
 +        return False
 +
 +    def addLargeFile(self, relPath):
 +        self.largeFiles.add(relPath)
 +
 +    def removeLargeFile(self, relPath):
 +        self.largeFiles.remove(relPath)
 +
 +    def isLargeFile(self, relPath):
 +        return relPath in self.largeFiles
 +
 +    def processContent(self, git_mode, relPath, contents):
 +        """Processes the content of git fast import. This method decides if a
 +           file is stored in the large file system and handles all necessary
 +           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))
 +        return (git_mode, contents)
 +
 +class MockLFS(LargeFileSystem):
 +    """Mock large file system for testing."""
 +
 +    def generatePointer(self, contentFile):
 +        """The pointer content is the original content prefixed with "pointer-".
 +           The local filename of the large file storage is derived from the file content.
 +           """
 +        with open(contentFile, 'r') as f:
 +            content = next(f)
 +            gitMode = '100644'
 +            pointerContents = 'pointer-' + content
 +            localLargeFile = os.path.join(os.getcwd(), '.git', 'mock-storage', 'local', content[:-1])
 +            return (gitMode, pointerContents, localLargeFile)
 +
 +    def pushFile(self, localLargeFile):
 +        """The remote filename of the large file storage is the same as the local
 +           one but in a different directory.
 +           """
 +        remotePath = os.path.join(os.path.dirname(localLargeFile), '..', 'remote')
 +        if not os.path.exists(remotePath):
 +            os.makedirs(remotePath)
 +        shutil.copyfile(localLargeFile, os.path.join(remotePath, os.path.basename(localLargeFile)))
 +
 +class GitLFS(LargeFileSystem):
 +    """Git LFS as backend for the git-p4 large file system.
 +       See https://git-lfs.github.com/ for details."""
 +
 +    def __init__(self, *args):
 +        LargeFileSystem.__init__(self, *args)
 +        self.baseGitAttributes = []
 +
 +    def generatePointer(self, contentFile):
 +        """Generate a Git LFS pointer for the content. Return LFS Pointer file
 +           mode and content which is stored in the Git repository instead of
 +           the actual content. Return also the new location of the actual
 +           content.
 +           """
 +        pointerProcess = subprocess.Popen(
 +            ['git', 'lfs', 'pointer', '--file=' + contentFile],
 +            stdout=subprocess.PIPE
 +        )
 +        pointerFile = pointerProcess.stdout.read()
 +        if pointerProcess.wait():
 +            os.remove(contentFile)
 +            die('git-lfs pointer command failed. Did you install the extension?')
 +        pointerContents = [i+'\n' for i in pointerFile.split('\n')[2:][:-1]]
 +        oid = pointerContents[1].split(' ')[1].split(':')[1][:-1]
 +        localLargeFile = os.path.join(
 +            os.getcwd(),
 +            '.git', 'lfs', 'objects', oid[:2], oid[2:4],
 +            oid,
 +        )
 +        # LFS Spec states that pointer files should not have the executable bit set.
 +        gitMode = '100644'
 +        return (gitMode, pointerContents, localLargeFile)
 +
 +    def pushFile(self, localLargeFile):
 +        uploadProcess = subprocess.Popen(
 +            ['git', 'lfs', 'push', '--object-id', 'origin', os.path.basename(localLargeFile)]
 +        )
 +        if uploadProcess.wait():
 +            die('git-lfs push command failed. Did you define a remote?')
 +
 +    def generateGitAttributes(self):
 +        return (
 +            self.baseGitAttributes +
 +            [
 +                '\n',
 +                '#\n',
 +                '# Git LFS (see https://git-lfs.github.com/)\n',
 +                '#\n',
 +            ] +
 +            ['*.' + f.replace(' ', '[[:space:]]') + ' filter=lfs -text\n'
 +                for f in sorted(gitConfigList('git-p4.largeFileExtensions'))
 +            ] +
 +            ['/' + f.replace(' ', '[[:space:]]') + ' filter=lfs -text\n'
 +                for f in sorted(self.largeFiles) if not self.hasLargeFileExtension(f)
 +            ]
 +        )
 +
 +    def addLargeFile(self, relPath):
 +        LargeFileSystem.addLargeFile(self, relPath)
 +        self.writeToGitStream('100644', '.gitattributes', self.generateGitAttributes())
 +
 +    def removeLargeFile(self, relPath):
 +        LargeFileSystem.removeLargeFile(self, relPath)
 +        self.writeToGitStream('100644', '.gitattributes', self.generateGitAttributes())
 +
 +    def processContent(self, git_mode, relPath, contents):
 +        if relPath == '.gitattributes':
 +            self.baseGitAttributes = contents
 +            return (git_mode, self.generateGitAttributes())
 +        else:
 +            return LargeFileSystem.processContent(self, git_mode, relPath, contents)
 +
  class Command:
      def __init__(self):
          self.usage = "usage: %prog [options]"
@@@ -1282,9 -1087,6 +1289,9 @@@ class P4Submit(Command, P4UserMap)
          self.p4HasMoveCommand = p4_has_move_command()
          self.branch = None
  
 +        if gitConfig('git-p4.largeFileSystem'):
 +            die("Large file system not supported for git-p4 submit command. Please remove it from config.")
 +
      def check(self):
          if len(p4CmdList("opened ...")) > 0:
              die("You have files opened with perforce! Close them before starting the sync.")
          #
          # Let the user edit the change description, then submit it.
          #
-         if self.edit_template(fileName):
-             # read the edited message and submit
-             ret = True
-             tmpFile = open(fileName, "rb")
-             message = tmpFile.read()
-             tmpFile.close()
-             if self.isWindows:
-                 message = message.replace("\r\n", "\n")
-             submitTemplate = message[:message.index(separatorLine)]
-             p4_write_pipe(['submit', '-i'], submitTemplate)
-             if self.preserveUser:
-                 if p4User:
-                     # Get last changelist number. Cannot easily get it from
-                     # the submit command output as the output is
-                     # unmarshalled.
-                     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 = False
  
-         else:
+         try:
+             if self.edit_template(fileName):
+                 # read the edited message and submit
+                 tmpFile = open(fileName, "rb")
+                 message = tmpFile.read()
+                 tmpFile.close()
+                 if self.isWindows:
+                     message = message.replace("\r\n", "\n")
+                 submitTemplate = message[:message.index(separatorLine)]
+                 p4_write_pipe(['submit', '-i'], submitTemplate)
+                 if self.preserveUser:
+                     if p4User:
+                         # Get last changelist number. Cannot easily get it from
+                         # the submit command output as the output is
+                         # unmarshalled.
+                         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
-             ret = False
-             print "Submission cancelled, undoing p4 changes."
-             for f in editedFiles:
-                 p4_revert(f)
-             for f in filesToAdd:
-                 p4_revert(f)
-                 os.remove(f)
-             for f in filesToDelete:
-                 p4_revert(f)
+             if not submitted:
+                 print "Submission cancelled, undoing p4 changes."
+                 for f in editedFiles:
+                     p4_revert(f)
+                 for f in filesToAdd:
+                     p4_revert(f)
+                     os.remove(f)
+                 for f in filesToDelete:
+                     p4_revert(f)
  
          os.remove(fileName)
-         return ret
+         return submitted
  
      # Export git tags as p4 labels. Create a p4 label and then tag
      # with that.
      def run(self, args):
          if len(args) == 0:
              self.master = currentGitBranch()
-             if len(self.master) == 0 or not gitBranchExists("refs/heads/%s" % self.master):
-                 die("Detecting current git branch failed!")
          elif len(args) == 1:
              self.master = args[0]
              if not branchExists(self.master):
          else:
              return False
  
-         allowSubmit = gitConfig("git-p4.allowSubmit")
-         if len(allowSubmit) > 0 and not self.master in allowSubmit.split(","):
-             die("%s is not in git-p4.allowSubmit" % self.master)
+         if self.master:
+             allowSubmit = gitConfig("git-p4.allowSubmit")
+             if len(allowSubmit) > 0 and not self.master in allowSubmit.split(","):
+                 die("%s is not in git-p4.allowSubmit" % self.master)
  
          [upstream, settings] = findUpstreamBranchPoint()
          self.depotPath = settings['depot-paths'][0]
          self.check()
  
          commits = []
-         for line in read_pipe_lines(["git", "rev-list", "--no-merges", "%s..%s" % (self.origin, self.master)]):
+         if self.master:
+             commitish = self.master
+         else:
+             commitish = 'HEAD'
+         for line in read_pipe_lines(["git", "rev-list", "--no-merges", "%s..%s" % (self.origin, commitish)]):
              commits.append(line.strip())
          commits.reverse()
  
@@@ -2235,13 -2044,6 +2249,13 @@@ class P4Sync(Command, P4UserMap)
          self.clientSpecDirs = None
          self.tempBranches = []
          self.tempBranchLocation = "git-p4-tmp"
 +        self.largeFileSystem = None
 +
 +        if gitConfig('git-p4.largeFileSystem'):
 +            largeFileSystemConstructor = globals()[gitConfig('git-p4.largeFileSystem')]
 +            self.largeFileSystem = largeFileSystemConstructor(
 +                lambda git_mode, relPath, contents: self.writeToGitStream(git_mode, relPath, contents)
 +            )
  
          if gitConfig("git-p4.syncFromOrigin") == "false":
              self.syncWithOrigin = False
  
          return branches
  
 +    def writeToGitStream(self, gitMode, relPath, contents):
 +        self.gitStream.write('M %s inline %s\n' % (gitMode, relPath))
 +        self.gitStream.write('data %d\n' % sum(len(d) for d in contents))
 +        for d in contents:
 +            self.gitStream.write(d)
 +        self.gitStream.write('\n')
 +
      # output one file from the P4 stream
      # - helper for streamP4Files
  
      def streamOneP4File(self, file, contents):
          relPath = self.stripRepoPath(file['depotFile'], self.branchPrefixes)
          if verbose:
 -            sys.stderr.write("%s\n" % relPath)
 +            size = int(self.stream_file['fileSize'])
 +            sys.stdout.write('\r%s --> %s (%i MB)\n' % (file['depotFile'], relPath, size/1024/1024))
 +            sys.stdout.flush()
  
          (type_base, type_mods) = split_p4_type(file["type"])
  
              text = regexp.sub(r'$\1$', text)
              contents = [ text ]
  
 -        self.gitStream.write("M %s inline %s\n" % (git_mode, relPath))
 +        try:
 +            relPath.decode('ascii')
 +        except:
 +            encoding = 'utf8'
 +            if gitConfig('git-p4.pathEncoding'):
 +                encoding = gitConfig('git-p4.pathEncoding')
 +            relPath = relPath.decode(encoding, 'replace').encode('utf8', 'replace')
 +            if self.verbose:
 +                print 'Path with non-ASCII characters detected. Used %s to encode: %s ' % (encoding, relPath)
  
 -        # total length...
 -        length = 0
 -        for d in contents:
 -            length = length + len(d)
 +        if self.largeFileSystem:
 +            (git_mode, contents) = self.largeFileSystem.processContent(git_mode, relPath, contents)
  
 -        self.gitStream.write("data %d\n" % length)
 -        for d in contents:
 -            self.gitStream.write(d)
 -        self.gitStream.write("\n")
 +        self.writeToGitStream(git_mode, relPath, contents)
  
      def streamOneP4Deletion(self, file):
          relPath = self.stripRepoPath(file['path'], self.branchPrefixes)
          if verbose:
 -            sys.stderr.write("delete %s\n" % relPath)
 +            sys.stdout.write("delete %s\n" % relPath)
 +            sys.stdout.flush()
          self.gitStream.write("D %s\n" % relPath)
  
 +        if self.largeFileSystem and self.largeFileSystem.isLargeFile(relPath):
 +            self.largeFileSystem.removeLargeFile(relPath)
 +
      # handle another chunk of streaming data
      def streamP4FilesCb(self, marshalled):
  
              if marshalled["code"] == "error":
                  if "data" in marshalled:
                      err = marshalled["data"].rstrip()
 +
 +        if not err and 'fileSize' in self.stream_file:
 +            required_bytes = int((4 * int(self.stream_file["fileSize"])) - calcDiskFree())
 +            if required_bytes > 0:
 +                err = 'Not enough space left on %s! Free at least %i MB.' % (
 +                    os.getcwd(), required_bytes/1024/1024
 +                )
 +
          if err:
              f = None
              if self.stream_have_file_info:
          # 'data' field we need to append to our array
          for k in marshalled.keys():
              if k == 'data':
 +                if 'streamContentSize' not in self.stream_file:
 +                    self.stream_file['streamContentSize'] = 0
 +                self.stream_file['streamContentSize'] += len(marshalled['data'])
                  self.stream_contents.append(marshalled['data'])
              else:
                  self.stream_file[k] = marshalled[k]
  
 +        if (verbose and
 +            'streamContentSize' in self.stream_file and
 +            'fileSize' in self.stream_file and
 +            'depotFile' in self.stream_file):
 +            size = int(self.stream_file["fileSize"])
 +            if size > 0:
 +                progress = 100*self.stream_file['streamContentSize']/size
 +                sys.stdout.write('\r%s %d%% (%i MB)' % (self.stream_file['depotFile'], progress, int(size/1024/1024)))
 +                sys.stdout.flush()
 +
          self.stream_have_file_info = True
  
      # Stream directly from "p4 files" into "git fast-import"
diff --combined http.c
index 42f29ce0aaf7aac1e81849cf733e8ea92738f5b0,c29ce81ccc0432a80ac5d228d975f246ca47f4b4..553d85674acac02856eac0fa19cf880993405e1b
--- 1/http.c
--- 2/http.c
+++ b/http.c
@@@ -30,6 -30,7 +30,6 @@@ static CURL *curl_default
  #endif
  
  #define PREV_BUF_SIZE 4096
 -#define RANGE_HEADER_SIZE 30
  
  char curl_errorstr[CURL_ERROR_SIZE];
  
@@@ -214,10 -215,10 +214,10 @@@ static int http_options(const char *var
  #endif
  #if LIBCURL_VERSION_NUM >= 0x070908
        if (!strcmp("http.sslcapath", var))
-               return git_config_string(&ssl_capath, var, value);
+               return git_config_pathname(&ssl_capath, var, value);
  #endif
        if (!strcmp("http.sslcainfo", var))
-               return git_config_string(&ssl_cainfo, var, value);
+               return git_config_pathname(&ssl_cainfo, var, value);
        if (!strcmp("http.sslcertpasswordprotected", var)) {
                ssl_cert_password_required = git_config_bool(var, value);
                return 0;
@@@ -464,6 -465,17 +464,17 @@@ static CURL *get_curl_handle(void
  
        if (curl_http_proxy) {
                curl_easy_setopt(result, CURLOPT_PROXY, curl_http_proxy);
+ #if LIBCURL_VERSION_NUM >= 0x071800
+               if (starts_with(curl_http_proxy, "socks5"))
+                       curl_easy_setopt(result,
+                               CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
+               else if (starts_with(curl_http_proxy, "socks4a"))
+                       curl_easy_setopt(result,
+                               CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4A);
+               else if (starts_with(curl_http_proxy, "socks"))
+                       curl_easy_setopt(result,
+                               CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
+ #endif
        }
  #if LIBCURL_VERSION_NUM >= 0x070a07
        curl_easy_setopt(result, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
@@@ -680,7 -692,6 +691,7 @@@ struct active_request_slot *get_active_
        curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 0);
        curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
        curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 1);
 +      curl_easy_setopt(slot->curl, CURLOPT_RANGE, NULL);
  #ifdef LIBCURL_CAN_HANDLE_AUTH_ANY
        curl_easy_setopt(slot->curl, CURLOPT_HTTPAUTH, http_auth_methods);
  #endif
@@@ -1122,7 -1133,7 +1133,7 @@@ static void write_accept_language(struc
                     decimal_places++, max_q *= 10)
                        ;
  
 -              sprintf(q_format, ";q=0.%%0%dd", decimal_places);
 +              xsnprintf(q_format, sizeof(q_format), ";q=0.%%0%dd", decimal_places);
  
                strbuf_addstr(buf, "Accept-Language: ");
  
@@@ -1173,13 -1184,6 +1184,13 @@@ static const char *get_accept_language(
        return cached_accept_language;
  }
  
 +static void http_opt_request_remainder(CURL *curl, off_t pos)
 +{
 +      char buf[128];
 +      xsnprintf(buf, sizeof(buf), "%"PRIuMAX"-", (uintmax_t)pos);
 +      curl_easy_setopt(curl, CURLOPT_RANGE, buf);
 +}
 +
  /* http_request() targets */
  #define HTTP_REQUEST_STRBUF   0
  #define HTTP_REQUEST_FILE     1
@@@ -1205,11 -1209,14 +1216,11 @@@ static int http_request(const char *url
                curl_easy_setopt(slot->curl, CURLOPT_FILE, result);
  
                if (target == HTTP_REQUEST_FILE) {
 -                      long posn = ftell(result);
 +                      off_t posn = ftello(result);
                        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
                                         fwrite);
 -                      if (posn > 0) {
 -                              strbuf_addf(&buf, "Range: bytes=%ld-", posn);
 -                              headers = curl_slist_append(headers, buf.buf);
 -                              strbuf_reset(&buf);
 -                      }
 +                      if (posn > 0)
 +                              http_opt_request_remainder(slot->curl, posn);
                } else
                        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
                                         fwrite_buffer);
@@@ -1519,6 -1526,10 +1530,6 @@@ void release_http_pack_request(struct h
                fclose(preq->packfile);
                preq->packfile = NULL;
        }
 -      if (preq->range_header != NULL) {
 -              curl_slist_free_all(preq->range_header);
 -              preq->range_header = NULL;
 -      }
        preq->slot = NULL;
        free(preq->url);
        free(preq);
@@@ -1529,7 -1540,6 +1540,7 @@@ int finish_http_pack_request(struct htt
        struct packed_git **lst;
        struct packed_git *p = preq->target;
        char *tmp_idx;
 +      size_t len;
        struct child_process ip = CHILD_PROCESS_INIT;
        const char *ip_argv[8];
  
                lst = &((*lst)->next);
        *lst = (*lst)->next;
  
 -      tmp_idx = xstrdup(preq->tmpfile);
 -      strcpy(tmp_idx + strlen(tmp_idx) - strlen(".pack.temp"),
 -             ".idx.temp");
 +      if (!strip_suffix(preq->tmpfile, ".pack.temp", &len))
 +              die("BUG: pack tmpfile does not end in .pack.temp?");
 +      tmp_idx = xstrfmt("%.*s.idx.temp", (int)len, preq->tmpfile);
  
        ip_argv[0] = "index-pack";
        ip_argv[1] = "-o";
  struct http_pack_request *new_http_pack_request(
        struct packed_git *target, const char *base_url)
  {
 -      long prev_posn = 0;
 -      char range[RANGE_HEADER_SIZE];
 +      off_t prev_posn = 0;
        struct strbuf buf = STRBUF_INIT;
        struct http_pack_request *preq;
  
         * If there is data present from a previous transfer attempt,
         * resume where it left off
         */
 -      prev_posn = ftell(preq->packfile);
 +      prev_posn = ftello(preq->packfile);
        if (prev_posn>0) {
                if (http_is_verbose)
                        fprintf(stderr,
                                "Resuming fetch of pack %s at byte %ld\n",
                                sha1_to_hex(target->sha1), prev_posn);
 -              sprintf(range, "Range: bytes=%ld-", prev_posn);
 -              preq->range_header = curl_slist_append(NULL, range);
 -              curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,
 -                      preq->range_header);
 +              http_opt_request_remainder(preq->slot->curl, prev_posn);
        }
  
        return preq;
@@@ -1668,7 -1682,9 +1679,7 @@@ struct http_object_request *new_http_ob
        int prevlocal;
        char prev_buf[PREV_BUF_SIZE];
        ssize_t prev_read = 0;
 -      long prev_posn = 0;
 -      char range[RANGE_HEADER_SIZE];
 -      struct curl_slist *range_header = NULL;
 +      off_t prev_posn = 0;
        struct http_object_request *freq;
  
        freq = xcalloc(1, sizeof(*freq));
                        fprintf(stderr,
                                "Resuming fetch of object %s at byte %ld\n",
                                hex, prev_posn);
 -              sprintf(range, "Range: bytes=%ld-", prev_posn);
 -              range_header = curl_slist_append(range_header, range);
 -              curl_easy_setopt(freq->slot->curl,
 -                               CURLOPT_HTTPHEADER, range_header);
 +              http_opt_request_remainder(freq->slot->curl, prev_posn);
        }
  
        return freq;
diff --combined transport.c
index 23b2ed6f0cf6f521c4d5afa6c1cffe0786d29a5f,37e4f5e42cbe86d69221256ab63af609d02c7d07..e34ab929729ad1b69cde22544e44b48b715cb984
@@@ -15,6 -15,7 +15,7 @@@
  #include "submodule.h"
  #include "string-list.h"
  #include "sha1-array.h"
+ #include "sigchain.h"
  
  /* rsync support */
  
@@@ -654,24 -655,23 +655,24 @@@ static void print_ok_ref_status(struct 
                        "[new branch]"),
                        ref, ref->peer_ref, NULL, porcelain);
        else {
 -              char quickref[84];
 +              struct strbuf quickref = STRBUF_INIT;
                char type;
                const char *msg;
  
 -              strcpy(quickref, status_abbrev(ref->old_sha1));
 +              strbuf_addstr(&quickref, status_abbrev(ref->old_sha1));
                if (ref->forced_update) {
 -                      strcat(quickref, "...");
 +                      strbuf_addstr(&quickref, "...");
                        type = '+';
                        msg = "forced update";
                } else {
 -                      strcat(quickref, "..");
 +                      strbuf_addstr(&quickref, "..");
                        type = ' ';
                        msg = NULL;
                }
 -              strcat(quickref, status_abbrev(ref->new_sha1));
 +              strbuf_addstr(&quickref, status_abbrev(ref->new_sha1));
  
 -              print_ref_status(type, quickref, ref, ref->peer_ref, msg, porcelain);
 +              print_ref_status(type, quickref.buf, ref, ref->peer_ref, msg, porcelain);
 +              strbuf_release(&quickref);
        }
  }
  
@@@ -1127,6 -1127,8 +1128,8 @@@ static int run_pre_push_hook(struct tra
                return -1;
        }
  
+       sigchain_push(SIGPIPE, SIG_IGN);
        strbuf_init(&buf, 256);
  
        for (r = remote_refs; r; r = r->next) {
                         r->peer_ref->name, sha1_to_hex(r->new_sha1),
                         r->name, sha1_to_hex(r->old_sha1));
  
-               if (write_in_full(proc.in, buf.buf, buf.len) != buf.len) {
-                       ret = -1;
+               if (write_in_full(proc.in, buf.buf, buf.len) < 0) {
+                       /* We do not mind if a hook does not read all refs. */
+                       if (errno != EPIPE)
+                               ret = -1;
                        break;
                }
        }
        if (!ret)
                ret = x;
  
+       sigchain_pop(SIGPIPE);
        x = finish_command(&proc);
        if (!ret)
                ret = x;