bup repositories are in Git format. This library allows us to
interact with the Git data structures.
"""
-import os, zlib, time, subprocess, struct, stat, re, tempfile, heapq
+import os, sys, zlib, time, subprocess, struct, stat, re, tempfile
from bup.helpers import *
-from bup import _helpers
+from bup import _helpers, path
MIDX_VERSION = 2
def auto_midx(objdir):
- main_exe = os.environ.get('BUP_MAIN_EXE') or sys.argv[0]
- args = [main_exe, 'midx', '--auto', '--dir', objdir]
- rv = subprocess.call(args, stdout=open('/dev/null', 'w'))
+ args = [path.exe(), 'midx', '--auto', '--dir', objdir]
+ try:
+ rv = subprocess.call(args, stdout=open('/dev/null', 'w'))
+ except OSError, e:
+ # make sure 'args' gets printed to help with debugging
+ add_error('%r: exception: %s' % (args, e))
+ raise
if rv:
add_error('%r: returned %d' % (args, rv))
def idxmerge(idxlist, final_progress=True):
"""Generate a list of all the objects reachable in a PackIdxList."""
- total = sum(len(i) for i in idxlist)
- iters = (iter(i) for i in idxlist)
- heap = [(next(it), it) for it in iters]
- heapq.heapify(heap)
- count = 0
- last = None
- while heap:
- if (count % 10024) == 0:
- progress('Reading indexes: %.2f%% (%d/%d)\r'
- % (count*100.0/total, count, total))
- (e, it) = heap[0]
- if e != last:
- yield e
- last = e
- count += 1
- e = next(it)
- if e:
- heapq.heapreplace(heap, (e, it))
- else:
- heapq.heappop(heap)
- if final_progress:
- log('Reading indexes: %.2f%% (%d/%d), done.\n' % (100, total, total))
+ def pfunc(count, total):
+ progress('Reading indexes: %.2f%% (%d/%d)\r'
+ % (count*100.0/total, count, total))
+ def pfinal(count, total):
+ if final_progress:
+ log('Reading indexes: %.2f%% (%d/%d), done.\n' % (100, total, total))
+ return merge_iter(idxlist, 10024, pfunc, pfinal)
+
+def _make_objcache():
+ return PackIdxList(repo('objects/pack'))
class PackWriter:
"""Writes Git objects insid a pack file."""
- def __init__(self, objcache_maker=None):
+ def __init__(self, objcache_maker=_make_objcache):
self.count = 0
self.outbytes = 0
self.filename = None
def __del__(self):
self.close()
- def _make_objcache(self):
- if self.objcache == None:
- if self.objcache_maker:
- self.objcache = self.objcache_maker()
- else:
- self.objcache = PackIdxList(repo('objects/pack'))
-
def _open(self):
if not self.file:
- self._make_objcache()
(fd,name) = tempfile.mkstemp(suffix='.pack', dir=repo('objects'))
self.file = os.fdopen(fd, 'w+b')
assert(name.endswith('.pack'))
# to our hashsplit algorithm.) f.write() does its own buffering,
# but that's okay because we'll flush it in _end().
oneblob = ''.join(datalist)
- f.write(oneblob)
+ try:
+ f.write(oneblob)
+ except IOError, e:
+ raise GitError, e, sys.exc_info()[2]
nw = len(oneblob)
crc = zlib.crc32(oneblob) & 0xffffffff
self._update_idx(sha, crc, nw)
"""Write an object in this pack file."""
return self._write(calc_hash(type, content), type, content)
+ def _require_objcache(self):
+ if self.objcache is None and self.objcache_maker:
+ self.objcache = self.objcache_maker()
+ if self.objcache is None:
+ raise GitError(
+ "PackWriter not opened or can't check exists w/o objcache")
+
def exists(self, id):
"""Return non-empty if an object is found in the object cache."""
- if not self.objcache:
- self._make_objcache()
+ self._require_objcache()
return self.objcache.exists(id)
def maybe_write(self, type, content):
"""Write an object to the pack file if not present and return its id."""
+ self._require_objcache()
sha = calc_hash(type, content)
if not self.exists(sha):
self._write(sha, type, content)
f.close()
os.unlink(self.filename + '.pack')
- def _end(self):
+ def _end(self, run_midx=True):
f = self.file
if not f: return None
self.file = None
os.rename(self.filename + '.pack', nameprefix + '.pack')
os.rename(self.filename + '.idx', nameprefix + '.idx')
- auto_midx(repo('objects/pack'))
+ if run_midx:
+ auto_midx(repo('objects/pack'))
return nameprefix
- def close(self):
+ def close(self, run_midx=True):
"""Close the pack file and move it to its definitive path."""
- return self._end()
+ return self._end(run_midx=run_midx)
def _write_pack_idx_v2(self, file, idx, packbin):
sum = Sha1()
def init_repo(path=None):
"""Create the Git bare repository for bup in a given path."""
guess_repo(path)
- d = repo()
+ d = repo() # appends a / to the path
+ parent = os.path.dirname(os.path.dirname(d))
+ if parent and not os.path.exists(parent):
+ raise GitError('parent directory "%s" does not exist\n' % parent)
if os.path.exists(d) and not os.path.isdir(os.path.join(d, '.')):
raise GitError('"%d" exists but is not a directory\n' % d)
p = subprocess.Popen(['git', '--bare', 'init'], stdout=sys.stderr,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
close_fds = True,
+ bufsize = 4096,
preexec_fn = _gitenv)
def _fast_get(self, id):
assert(not id.startswith('-'))
self.inprogress = id
self.p.stdin.write('%s\n' % id)
+ self.p.stdin.flush()
hdr = self.p.stdout.readline()
if hdr.endswith(' missing\n'):
self.inprogress = None
yield d
except StopIteration:
log('booger!\n')
+
+def tags():
+ """Return a dictionary of all tags in the form {hash: [tag_names, ...]}."""
+ tags = {}
+ for (n,c) in list_refs():
+ 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