]> arthur.barton.de Git - bup.git/blobdiff - git.py
Fix some problems running on older Debian.
[bup.git] / git.py
diff --git a/git.py b/git.py
index 832620e6666fe52fbbfec0fb63078b72eac0db2d..ec60ae45b98c9af4fc7fab3d8dc5f27b48eb1877 100644 (file)
--- a/git.py
+++ b/git.py
@@ -1,10 +1,15 @@
-import os, errno, zlib, time, sha, subprocess, struct, mmap
+import os, errno, zlib, time, sha, subprocess, struct, mmap, stat
 from helpers import *
 
 verbose = 0
+repodir = os.environ.get('BUP_DIR', '.git')
 
-def repodir(sub = ''):
-    return os.path.join(os.environ.get('BUP_DIR', '.git'), sub)
+def repo(sub = ''):
+    global repodir
+    gd = os.path.join(repodir, '.git')
+    if os.path.exists(gd):
+        repodir = gd
+    return os.path.join(repodir, sub)
 
 
 class PackIndex:
@@ -15,7 +20,8 @@ class PackIndex:
                              mmap.MAP_SHARED, mmap.PROT_READ)
         f.close()  # map will persist beyond file close
         assert(str(self.map[0:8]) == '\377tOc\0\0\0\2')
-        self.fanout = list(struct.unpack('!256I', buffer(self.map, 8, 256*4)))
+        self.fanout = list(struct.unpack('!256I',
+                                         str(buffer(self.map, 8, 256*4))))
         self.fanout.append(0)  # entry "-1"
         nsha = self.fanout[255]
         self.ofstable = buffer(self.map,
@@ -25,10 +31,11 @@ class PackIndex:
                                  8 + 256*4 + nsha*20 + nsha*4 + nsha*4)
 
     def _ofs_from_idx(self, idx):
-        ofs = struct.unpack('!I', buffer(self.ofstable, idx*4, 4))[0]
+        ofs = struct.unpack('!I', str(buffer(self.ofstable, idx*4, 4)))[0]
         if ofs & 0x80000000:
             idx64 = ofs & 0x7fffffff
-            ofs = struct.unpack('!I', buffer(self.ofs64table, idx64*8, 8))[0]
+            ofs = struct.unpack('!I',
+                                str(buffer(self.ofs64table, idx64*8, 8)))[0]
         return ofs
 
     def _idx_from_hash(self, hash):
@@ -92,14 +99,21 @@ def calc_hash(type, content):
     return sum.digest()
 
 
+def _shalist_sort_key(ent):
+    (mode, name, id) = ent
+    if stat.S_ISDIR(int(mode, 8)):
+        return name + '/'
+    else:
+        return name
+
+
 _typemap = dict(blob=3, tree=2, commit=1, tag=8)
 class PackWriter:
-    def __init__(self):
+    def __init__(self, objcache=None):
         self.count = 0
-        self.binlist = []
-        self.objcache = MultiPackIndex(repodir('objects/pack'))
         self.filename = None
         self.file = None
+        self.objcache = objcache or MultiPackIndex(repo('objects/pack'))
 
     def __del__(self):
         self.close()
@@ -107,35 +121,40 @@ class PackWriter:
     def _open(self):
         assert(not self.file)
         self.objcache.zap_also()
-        self.filename = repodir('objects/bup%d' % os.getpid())
+        self.filename = repo('objects/bup%d' % os.getpid())
         self.file = open(self.filename + '.pack', 'w+')
         self.file.write('PACK\0\0\0\2\0\0\0\0')
 
-    def _write(self, bin, type, content):
+    def _raw_write(self, datalist):
         if not self.file:
             self._open()
         f = self.file
+        for d in datalist:
+            f.write(d)
+        self.count += 1
 
+    def _write(self, bin, type, content):
         if verbose:
             log('>')
-            
+
+        out = []
+
         sz = len(content)
         szbits = (sz & 0x0f) | (_typemap[type]<<4)
         sz >>= 4
         while 1:
             if sz: szbits |= 0x80
-            f.write(chr(szbits))
+            out.append(chr(szbits))
             if not sz:
                 break
             szbits = sz & 0x7f
             sz >>= 7
-        
+
         z = zlib.compressobj(1)
-        f.write(z.compress(content))
-        f.write(z.flush())
+        out.append(z.compress(content))
+        out.append(z.flush())
 
-        self.count += 1
-        self.binlist.append(bin)
+        self._raw_write(out)
         return bin
 
     def write(self, type, content):
@@ -152,7 +171,7 @@ class PackWriter:
         return self.maybe_write('blob', blob)
 
     def new_tree(self, shalist):
-        shalist = sorted(shalist, key = lambda x: x[1])
+        shalist = sorted(shalist, key = _shalist_sort_key)
         l = ['%s %s\0%s' % (mode,name,bin) 
              for (mode,name,bin) in shalist]
         return self.maybe_write('tree', ''.join(l))
@@ -174,8 +193,8 @@ class PackWriter:
         commit = self._new_commit(tree, oldref,
                                   userline, now, userline, now,
                                   msg)
-        self.close()  # UGLY: needed so _update_ref can see the new objects
         if ref:
+            self.close()  # UGLY: needed so _update_ref can see the new objects
             _update_ref(ref, commit.encode('hex'), oldref)
         return commit
 
@@ -215,18 +234,39 @@ class PackWriter:
         out = p.stdout.read().strip()
         if p.wait() or not out:
             raise Exception('git index-pack returned an error')
-        nameprefix = repodir('objects/pack/%s' % out)
+        nameprefix = repo('objects/pack/%s' % out)
         os.rename(self.filename + '.pack', nameprefix + '.pack')
         os.rename(self.filename + '.idx', nameprefix + '.idx')
         return nameprefix
 
 
+class PackWriter_Remote(PackWriter):
+    def __init__(self, conn, objcache=None):
+        PackWriter.__init__(self, objcache)
+        self.file = conn
+        self.filename = 'remote socket'
+
+    def _open(self):
+        assert(not "can't reopen a PackWriter_Remote")
+
+    def close(self):
+        if self.file:
+            self.file.write('\0\0\0\0')
+        self.file = None
+
+    def _raw_write(self, datalist):
+        assert(self.file)
+        data = ''.join(datalist)
+        assert(len(data))
+        self.file.write(struct.pack('!I', len(data)) + data)
+
+
 def _git_date(date):
     return time.strftime('%s %z', time.localtime(date))
 
 
 def _gitenv():
-    os.environ['GIT_DIR'] = os.path.abspath(repodir())
+    os.environ['GIT_DIR'] = os.path.abspath(repo())
 
 
 def _read_ref(refname):
@@ -248,3 +288,18 @@ def _update_ref(refname, newval, oldval):
                          preexec_fn = _gitenv)
     p.wait()
     return newval
+
+
+def init_repo():
+    d = repo()
+    if os.path.exists(d) and not os.path.isdir(os.path.join(d, '.')):
+        raise Exception('"%d" exists but is not a directory\n' % d)
+    p = subprocess.Popen(['git', 'init', '--bare'],
+                         preexec_fn = _gitenv)
+    return p.wait()
+
+
+def check_repo_or_die():
+    if not os.path.isdir(repo('objects/pack/.')):
+        log('error: %r is not a bup/git repository\n' % repo())
+        exit(15)