return parse_commit(commit_content)
-def repo(sub = ''):
+def repo(sub = '', repo_dir=None):
"""Get the path to the git repository or one of its subdirectories."""
global repodir
- if not repodir:
+ repo_dir = repo_dir or repodir
+ if not repo_dir:
raise GitError('You should call check_repo_or_die()')
# If there's a .git subdirectory, then the actual repo is in there.
- gd = os.path.join(repodir, '.git')
+ gd = os.path.join(repo_dir, '.git')
if os.path.exists(gd):
repodir = gd
- return os.path.join(repodir, sub)
+ return os.path.join(repo_dir, sub)
def shorten_hash(s):
disambiguate normal files from segmented ones.
"""
if stat.S_ISREG(mode) and not stat.S_ISREG(gitmode):
+ assert(stat.S_ISDIR(gitmode))
return name + '.bup'
elif name.endswith('.bup') or name[:-1].endswith('.bup'):
return name + '.bupl'
return '%d %s' % (date, time.strftime('%z', time.localtime(date)))
-def _gitenv():
- os.environ['GIT_DIR'] = os.path.abspath(repo())
+def _gitenv(repo_dir = None):
+ if not repo_dir:
+ repo_dir = repo()
+ def env():
+ os.environ['GIT_DIR'] = os.path.abspath(repo_dir)
+ return env
-def list_refs(refname = None):
+def list_refs(refname = None, repo_dir = None):
"""Generate a list of tuples in the form (refname,hash).
If a ref name is specified, list only this particular ref.
"""
argv = ['git', 'show-ref', '--']
if refname:
argv += [refname]
- p = subprocess.Popen(argv, preexec_fn = _gitenv, stdout = subprocess.PIPE)
+ p = subprocess.Popen(argv,
+ preexec_fn = _gitenv(repo_dir),
+ stdout = subprocess.PIPE)
out = p.stdout.read().strip()
rv = p.wait() # not fatal
if rv:
yield (name, sha.decode('hex'))
-def read_ref(refname):
+def read_ref(refname, repo_dir = None):
"""Get the commit id of the most recent commit made on a given ref."""
- l = list(list_refs(refname))
+ l = list(list_refs(refname, repo_dir))
if l:
assert(len(l) == 1)
return l[0][1]
return None
-def rev_list(ref, count=None):
+def rev_list(ref, count=None, repo_dir=None):
"""Generate a list of reachable commits in reverse chronological order.
This generator walks through commits, from child to parent, that are
if count:
opts += ['-n', str(atoi(count))]
argv = ['git', 'rev-list', '--pretty=format:%at'] + opts + [ref, '--']
- p = subprocess.Popen(argv, preexec_fn = _gitenv, stdout = subprocess.PIPE)
+ p = subprocess.Popen(argv,
+ preexec_fn = _gitenv(repo_dir),
+ stdout = subprocess.PIPE)
commit = None
for row in p.stdout:
s = row.strip()
raise GitError, 'git rev-list returned error %d' % rv
-def get_commit_dates(refs):
+def get_commit_dates(refs, repo_dir=None):
"""Get the dates for the specified commit refs. For now, every unique
string in refs must resolve to a different commit or this
function will fail."""
result = []
for ref in refs:
- commit = get_commit_items(ref, cp())
+ commit = get_commit_items(ref, cp(repo_dir))
result.append(commit.author_sec)
return result
-def rev_parse(committish):
+def rev_parse(committish, repo_dir=None):
"""Resolve the full hash for 'committish', if it exists.
Should be roughly equivalent to 'git rev-parse'.
Returns the hex value of the hash if it is found, None if 'committish' does
not correspond to anything.
"""
- head = read_ref(committish)
+ head = read_ref(committish, repo_dir=repo_dir)
if head:
debug2("resolved from ref: commit = %s\n" % head.encode('hex'))
return head
- pL = PackIdxList(repo('objects/pack'))
+ pL = PackIdxList(repo('objects/pack', repo_dir=repo_dir))
if len(committish) == 40:
try:
return None
-def update_ref(refname, newval, oldval):
+def update_ref(refname, newval, oldval, repo_dir=None):
"""Change the commit pointed to by a branch."""
if not oldval:
oldval = ''
assert(refname.startswith('refs/heads/'))
p = subprocess.Popen(['git', 'update-ref', refname,
newval.encode('hex'), oldval.encode('hex')],
- preexec_fn = _gitenv)
+ preexec_fn = _gitenv(repo_dir))
_git_wait('git update-ref', p)
if os.path.exists(d) and not os.path.isdir(os.path.join(d, '.')):
raise GitError('"%s" exists but is not a directory\n' % d)
p = subprocess.Popen(['git', '--bare', 'init'], stdout=sys.stderr,
- preexec_fn = _gitenv)
+ preexec_fn = _gitenv())
_git_wait('git init', p)
# Force the index version configuration in order to ensure bup works
# regardless of the version of the installed Git binary.
p = subprocess.Popen(['git', 'config', 'pack.indexVersion', '2'],
- stdout=sys.stderr, preexec_fn = _gitenv)
+ stdout=sys.stderr, preexec_fn = _gitenv())
_git_wait('git config', p)
# Enable the reflog
p = subprocess.Popen(['git', 'config', 'core.logAllRefUpdates', 'true'],
- stdout=sys.stderr, preexec_fn = _gitenv)
+ stdout=sys.stderr, preexec_fn = _gitenv())
_git_wait('git config', p)
def _git_capture(argv):
- p = subprocess.Popen(argv, stdout=subprocess.PIPE, preexec_fn = _gitenv)
+ 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:
"""Link to 'git cat-file' that is used to retrieve blob data."""
- def __init__(self):
+ def __init__(self, repo_dir = None):
global _ver_warned
+ self.repo_dir = repo_dir
wanted = ('1','5','6')
if ver() < wanted:
if not _ver_warned:
stdout=subprocess.PIPE,
close_fds = True,
bufsize = 4096,
- preexec_fn = _gitenv)
+ preexec_fn = _gitenv(self.repo_dir))
def _fast_get(self, id):
if not self.p or self.p.poll() != None:
p = subprocess.Popen(['git', 'cat-file', type, id],
stdout=subprocess.PIPE,
- preexec_fn = _gitenv)
+ preexec_fn = _gitenv(self.repo_dir))
for blob in chunkyreader(p.stdout):
yield blob
_git_wait('git cat-file', p)
log('booger!\n')
-_cp = (None, None)
+_cp = {}
-def cp():
- """Create a CatPipe object or reuse an already existing one."""
+def cp(repo_dir=None):
+ """Create a CatPipe object or reuse the already existing one."""
global _cp
- cp_dir, cp = _cp
- cur_dir = os.path.realpath(repo())
- if cur_dir != cp_dir:
- cp = CatPipe()
- _cp = (cur_dir, cp)
+ if not repo_dir:
+ repo_dir = repo()
+ repo_dir = os.path.abspath(repo_dir)
+ cp = _cp.get(repo_dir)
+ if not cp:
+ cp = CatPipe(repo_dir)
+ _cp[repo_dir] = cp
return cp
-def tags():
+def tags(repo_dir = None):
"""Return a dictionary of all tags in the form {hash: [tag_names, ...]}."""
tags = {}
- for (n,c) in list_refs():
+ for (n,c) in list_refs(repo_dir = repo_dir):
if n.startswith('refs/tags/'):
name = n[10:]
if not c in tags:
tags[c] = []
tags[c].append(name) # more than one tag can point at 'c'
-
return tags