From 755be38472711b7d104efd5333eb5a2130dd832c Mon Sep 17 00:00:00 2001 From: Avery Pennarun Date: Tue, 5 Jan 2010 22:21:18 -0500 Subject: [PATCH] Fix compatibility with git 1.5.4.3 (Ubuntu Hardy). Thanks to Andy Chong for reporting the problem. Basically it comes down to two things that are missing in that version but exist in git 1.5.6: - git init --bare doesn't work, but git --bare init does. - git cat-file --batch doesn't exist in that version. Unfortunately, the latter problem is pretty serious; bup join is really slow without it. I guess it might be time to implement an internal version of cat-file. --- git.py | 87 +++++++++++++++++++++++++++++++++++++++++++++++---------- test-sh | 7 +++-- 2 files changed, 77 insertions(+), 17 deletions(-) diff --git a/git.py b/git.py index a7e81c5c3..3138f6eb8 100644 --- a/git.py +++ b/git.py @@ -1,4 +1,4 @@ -import os, errno, zlib, time, sha, subprocess, struct, mmap, stat +import os, errno, zlib, time, sha, subprocess, struct, mmap, stat, re from helpers import * verbose = 0 @@ -237,8 +237,9 @@ def close(self): preexec_fn = _gitenv, stdout = subprocess.PIPE) out = p.stdout.read().strip() - if p.wait() or not out: - raise GitError('git index-pack returned an error') + _git_wait('git index-pack', p) + if not out: + raise GitError('git index-pack produced no output') nameprefix = repo('objects/pack/%s' % out) os.rename(self.filename + '.pack', nameprefix + '.pack') os.rename(self.filename + '.idx', nameprefix + '.idx') @@ -285,7 +286,7 @@ def read_ref(refname): preexec_fn = _gitenv, stdout = subprocess.PIPE) out = p.stdout.read().strip() - rv = p.wait() + rv = p.wait() # not fatal if rv: assert(not out) if out: @@ -300,9 +301,7 @@ def update_ref(refname, newval, oldval): p = subprocess.Popen(['git', 'update-ref', '--', refname, newval.encode('hex'), oldval.encode('hex')], preexec_fn = _gitenv) - rv = p.wait() - if rv: - raise GitError('update_ref returned error code %d' % rv) + _git_wait('git update-ref', p) def guess_repo(path=None): @@ -322,9 +321,10 @@ def init_repo(path=None): raise GitError('"%d" exists but is not a directory\n' % d) p = subprocess.Popen(['git', '--bare', 'init'], stdout=sys.stderr, preexec_fn = _gitenv) - rv = p.wait() - if rv != 0: - raise GitError('git init returned %d\n' % rv) + _git_wait('git init', p) + p = subprocess.Popen(['git', 'config', 'pack.indexVersion', '2'], + stdout=sys.stderr, preexec_fn = _gitenv) + _git_wait('git config', p) def check_repo_or_die(path=None): @@ -348,17 +348,60 @@ def _treeparse(buf): ofs += z+1+20 yield (spl[0], spl[1], sha) +_ver = None +def ver(): + global _ver + if not _ver: + p = subprocess.Popen(['git', '--version'], + stdout=subprocess.PIPE) + gvs = p.stdout.read() + _git_wait('git --version', p) + m = re.match(r'git version (\S+.\S+)', gvs) + if not m: + raise GitError('git --version weird output: %r' % gvs) + _ver = tuple(m.group(1).split('.')) + needed = ('1','5','4') + if _ver < needed: + raise GitError('git version %s or higher is required; you have %s' + % ('.'.join(needed), '.'.join(_ver))) + return _ver + + +def _git_wait(cmd, p): + rv = p.wait() + if rv != 0: + raise GitError('%s returned %d' % (cmd, rv)) + + +def _git_capture(argv): + p = subprocess.Popen(argv, stdout=subprocess.PIPE, preexec_fn = _gitenv) + r = p.stdout.read() + _git_wait(repr(argv), p) + return r + +_ver_warned = 0 class CatPipe: def __init__(self): - self.p = subprocess.Popen(['git', 'cat-file', '--batch'], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - preexec_fn = _gitenv) + global _ver_warned + wanted = ('1','5','6') + if ver() < wanted: + if not _ver_warned: + log('warning: git version < %s; bup will be slow.\n' + % '.'.join(wanted)) + _ver_warned = 1 + self.get = self._slow_get + else: + self.p = subprocess.Popen(['git', 'cat-file', '--batch'], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + preexec_fn = _gitenv) + self.get = self._fast_get - def get(self, id): + def _fast_get(self, id): assert(id.find('\n') < 0) assert(id.find('\r') < 0) + assert(id[0] != '-') self.p.stdin.write('%s\n' % id) hdr = self.p.stdout.readline() spl = hdr.split(' ') @@ -370,6 +413,20 @@ def get(self, id): yield blob assert(self.p.stdout.readline() == '\n') + def _slow_get(self, id): + assert(id.find('\n') < 0) + assert(id.find('\r') < 0) + assert(id[0] != '-') + type = _git_capture(['git', 'cat-file', '-t', id]).strip() + yield type + + p = subprocess.Popen(['git', 'cat-file', type, id], + stdout=subprocess.PIPE, + preexec_fn = _gitenv) + for blob in chunkyreader(p.stdout): + yield blob + _git_wait('git cat-file', p) + def _join(self, it): type = it.next() if type == 'blob': diff --git a/test-sh b/test-sh index 0cb868284..cdf94658b 100755 --- a/test-sh +++ b/test-sh @@ -7,7 +7,7 @@ export BUP_DIR="$TOP/buptest.tmp" bup() { - "$TOP/bup" "$@" + "$TOP/bup" "$@" } set -x @@ -35,7 +35,10 @@ diff -u testfile2 out2c.tmp git repack -Ad git prune (cd "$TOP/t/sampledata" && bup save -vvn master .) || exit 1 - n=$(git fsck --full --strict 2>&1 | tee -a /dev/stderr | wc -l) + n=$(git fsck --full --strict 2>&1 | + grep -v 'dangling commit' | + tee -a /dev/stderr | + wc -l) if [ "$n" != 0 ]; then echo "git fsck error." exit 5