From 5466b7448de601832c600a2857d86dafb56f31d3 Mon Sep 17 00:00:00 2001 From: Avery Pennarun Date: Sun, 3 Jan 2010 20:30:25 -0500 Subject: [PATCH] We can now update refs when we do a backup. Supported by both cmd-save and cmd-split, albeit with a disturbing amount of code duplication. Also updated bup-join to use a default BUP_DIR if none is specified. --- client.py | 20 +++++++++++++++++++- cmd-join.sh | 5 +++++ cmd-save.py | 14 ++++++++++++-- cmd-server.py | 17 +++++++++++++++++ cmd-split.py | 14 ++++++++++++-- git.py | 28 ++++++++++++++-------------- helpers.py | 4 +++- 7 files changed, 82 insertions(+), 20 deletions(-) diff --git a/client.py b/client.py index cf9b996..3380956 100644 --- a/client.py +++ b/client.py @@ -48,7 +48,7 @@ class Client: raise ClientError('server tunnel returned exit code %d' % rv) self.conn = None self.p = None - + def check_busy(self): if self._busy: raise ClientError('already busy with command %r' % self._busy) @@ -105,3 +105,21 @@ class Client: objcache = git.MultiPackIndex(self.cachedir) return git.PackWriter_Remote(self.conn, objcache = objcache, onclose = self._not_busy) + + def read_ref(self, refname): + self.check_busy() + self.conn.write('read-ref %s\n' % refname) + r = self.conn.readline().strip() + self.conn.check_ok() + if r: + assert(len(r) == 40) # hexified sha + return r.decode('hex') + else: + return None # nonexistent ref + + def update_ref(self, refname, newval, oldval): + self.check_busy() + self.conn.write('update-ref %s\n%s\n%s\n' + % (refname, newval.encode('hex'), + (oldval or '').encode('hex'))) + self.conn.check_ok() diff --git a/cmd-join.sh b/cmd-join.sh index 9fc7e15..4fe1b49 100755 --- a/cmd-join.sh +++ b/cmd-join.sh @@ -1,5 +1,10 @@ #!/bin/sh set -e + +if [ -z "$BUP_DIR" ]; then + BUP_DIR="$HOME/.bup" +fi + export GIT_DIR="$BUP_DIR" get_one() diff --git a/cmd-save.py b/cmd-save.py index dc78ee0..69d5626 100755 --- a/cmd-save.py +++ b/cmd-save.py @@ -115,12 +115,15 @@ if opt.verbose >= 2: git.verbose = opt.verbose - 1 hashsplit.split_verbosely = opt.verbose - 1 +refname = opt.name and 'refs/heads/%s' % opt.name or None if opt.remote: cli = client.Client(opt.remote) + oldref = refname and cli.read_ref(refname) or None cli.sync_indexes() w = cli.new_packwriter() else: cli = None + oldref = refname and git.read_ref(refname) or None w = git.PackWriter() root = Tree(None, '') @@ -149,13 +152,20 @@ if opt.tree: if opt.commit or opt.name: msg = 'bup save\n\nGenerated by command:\n%r' % sys.argv ref = opt.name and ('refs/heads/%s' % opt.name) or None - commit = w.new_commit(ref, tree, msg) + commit = w.new_commit(oldref, tree, msg) if opt.commit: if opt.verbose: log('\n') print commit.encode('hex') -w.close() +w.close() # must close before we can update the ref + +if opt.name: + if cli: + cli.update_ref(refname, commit, oldref) + else: + git.update_ref(refname, commit, oldref) + if cli: cli.close() diff --git a/cmd-server.py b/cmd-server.py index 92aa807..6baea70 100755 --- a/cmd-server.py +++ b/cmd-server.py @@ -60,6 +60,21 @@ def receive_objects(conn, junk): conn.ok() +def read_ref(conn, refname): + git.check_repo_or_die() + r = git.read_ref(refname) + conn.write('%s\n' % (r or '').encode('hex')) + conn.ok() + + +def update_ref(conn, refname): + git.check_repo_or_die() + newval = conn.readline().strip() + oldval = conn.readline().strip() + git.update_ref(refname, newval.decode('hex'), oldval.decode('hex')) + conn.ok() + + optspec = """ bup server """ @@ -78,6 +93,8 @@ commands = { 'list-indexes': list_indexes, 'send-index': send_index, 'receive-objects': receive_objects, + 'read-ref': read_ref, + 'update-ref': update_ref, } # FIXME: this protocol is totally lame and not at all future-proof. diff --git a/cmd-split.py b/cmd-split.py index 5551c73..4928733 100755 --- a/cmd-split.py +++ b/cmd-split.py @@ -31,12 +31,15 @@ if opt.verbose >= 2: start_time = time.time() +refname = opt.name and 'refs/heads/%s' % opt.name or None if opt.remote: cli = client.Client(opt.remote) + oldref = refname and cli.read_ref(refname) or None cli.sync_indexes() w = cli.new_packwriter() else: cli = None + oldref = refname and git.read_ref(refname) or None w = git.PackWriter() (shalist,tree) = hashsplit.split_to_tree(w, hashsplit.autofiles(extra)) @@ -51,11 +54,18 @@ if opt.tree: if opt.commit or opt.name: msg = 'bup split\n\nGenerated by command:\n%r' % sys.argv ref = opt.name and ('refs/heads/%s' % opt.name) or None - commit = w.new_commit(ref, tree, msg) + commit = w.new_commit(oldref, tree, msg) if opt.commit: print commit.encode('hex') -w.close() +w.close() # must close before we can update the ref + +if opt.name: + if cli: + cli.update_ref(refname, commit, oldref) + else: + git.update_ref(refname, commit, oldref) + if cli: cli.close() diff --git a/git.py b/git.py index 8593f35..3b758ea 100644 --- a/git.py +++ b/git.py @@ -187,23 +187,19 @@ class PackWriter: def _new_commit(self, tree, parent, author, adate, committer, cdate, msg): l = [] if tree: l.append('tree %s' % tree.encode('hex')) - if parent: l.append('parent %s' % parent) + if parent: l.append('parent %s' % parent.encode('hex')) if author: l.append('author %s %s' % (author, _git_date(adate))) if committer: l.append('committer %s %s' % (committer, _git_date(cdate))) l.append('') l.append(msg) return self.maybe_write('commit', '\n'.join(l)) - def new_commit(self, ref, tree, msg): + def new_commit(self, parent, tree, msg): now = time.time() userline = '%s <%s@%s>' % (userfullname(), username(), hostname()) - oldref = ref and _read_ref(ref) or None - commit = self._new_commit(tree, oldref, + commit = self._new_commit(tree, parent, userline, now, userline, now, msg) - if ref: - self.close() # UGLY: needed so _update_ref can see the new objects - _update_ref(ref, commit.encode('hex'), oldref) return commit def abort(self): @@ -283,25 +279,29 @@ def _gitenv(): os.environ['GIT_DIR'] = os.path.abspath(repo()) -def _read_ref(refname): +def read_ref(refname): p = subprocess.Popen(['git', 'show-ref', '--', refname], preexec_fn = _gitenv, stdout = subprocess.PIPE) out = p.stdout.read().strip() - p.wait() + rv = p.wait() + if rv: + assert(not out) if out: - return out.split()[0] + return out.split()[0].decode('hex') else: return None -def _update_ref(refname, newval, oldval): +def update_ref(refname, newval, oldval): if not oldval: oldval = '' - p = subprocess.Popen(['git', 'update-ref', '--', refname, newval, oldval], + p = subprocess.Popen(['git', 'update-ref', '--', refname, + newval.encode('hex'), oldval.encode('hex')], preexec_fn = _gitenv) - p.wait() - return newval + rv = p.wait() + if rv: + raise GitError('update_ref returned error code %d' % rv) def guess_repo(path=None): diff --git a/helpers.py b/helpers.py index c07cd7b..0cb4332 100644 --- a/helpers.py +++ b/helpers.py @@ -78,13 +78,15 @@ class Conn: def check_ok(self): self.outp.flush() + rl = '' for rl in linereader(self.inp): if not rl: continue elif rl == 'ok': return True else: - raise Exception('expected "ok", got %r' % rl) + break + raise Exception('expected "ok", got %r' % rl) def linereader(f): -- 2.39.2