X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=bup.git;a=blobdiff_plain;f=lib%2Fbup%2Frm.py;h=3bc2d833e3f853efd20581abdcac065b76312e06;hp=31155fc9718ea1be391ccd3b5ddf1265abb5caf6;hb=6b6559e405d264d4127211b935b21a3dda93ad93;hpb=75bc1e949e669550ac2057788a2909f14e309063 diff --git a/lib/bup/rm.py b/lib/bup/rm.py index 31155fc..3bc2d83 100644 --- a/lib/bup/rm.py +++ b/lib/bup/rm.py @@ -1,17 +1,20 @@ +from __future__ import absolute_import +from binascii import hexlify, unhexlify import sys -from bup import git, vfs +from bup import compat, git, vfs from bup.client import ClientError +from bup.compat import hexstr from bup.git import get_commit_items from bup.helpers import add_error, die_if_errors, log, saved_errors - +from bup.io import path_msg def append_commit(hash, parent, cp, writer): ci = get_commit_items(hash, cp) - tree = ci.tree.decode('hex') - author = '%s <%s>' % (ci.author_name, ci.author_mail) - committer = '%s <%s>' % (ci.committer_name, ci.committer_mail) + tree = unhexlify(ci.tree) + author = b'%s <%s>' % (ci.author_name, ci.author_mail) + committer = b'%s <%s>' % (ci.committer_name, ci.committer_mail) c = writer.new_commit(tree, parent, author, ci.author_sec, ci.author_offset, committer, ci.committer_sec, ci.committer_offset, @@ -21,7 +24,7 @@ def append_commit(hash, parent, cp, writer): def filter_branch(tip_commit_hex, exclude, writer): # May return None if everything is excluded. - commits = [c for _, c in git.rev_list(tip_commit_hex)] + commits = [unhexlify(x) for x in git.rev_list(tip_commit_hex)] commits.reverse() last_c, tree = None, None # Rather than assert that we always find an exclusion here, we'll @@ -29,24 +32,28 @@ def filter_branch(tip_commit_hex, exclude, writer): first_exclusion = next(i for i, c in enumerate(commits) if exclude(c)) if first_exclusion != 0: last_c = commits[first_exclusion - 1] - tree = get_commit_items(last_c.encode('hex'), - git.cp()).tree.decode('hex') + tree = unhexlify(get_commit_items(hexlify(last_c), git.cp()).tree) commits = commits[first_exclusion:] for c in commits: if exclude(c): continue - last_c, tree = append_commit(c.encode('hex'), last_c, git.cp(), writer) + last_c, tree = append_commit(hexlify(c), last_c, git.cp(), writer) return last_c +def commit_oid(item): + if isinstance(item, vfs.Commit): + return item.coid + assert isinstance(item, vfs.RevList) + return item.oid def rm_saves(saves, writer): assert(saves) - branch_node = saves[0].parent - for save in saves: # Be certain they're all on the same branch - assert(save.parent == branch_node) - rm_commits = frozenset([x.dereference().hash for x in saves]) - orig_tip = branch_node.hash - new_tip = filter_branch(orig_tip.encode('hex'), + first_branch_item = saves[0][1] + for save, branch in saves: # Be certain they're all on the same branch + assert(branch == first_branch_item) + rm_commits = frozenset([commit_oid(save) for save, branch in saves]) + orig_tip = commit_oid(first_branch_item) + new_tip = filter_branch(hexlify(orig_tip), lambda x: x in rm_commits, writer) assert(orig_tip) @@ -54,7 +61,7 @@ def rm_saves(saves, writer): return orig_tip, new_tip -def dead_items(vfs_top, paths): +def dead_items(repo, paths): """Return an optimized set of removals, reporting errors via add_error, and if there are any errors, return None, None.""" dead_branches = {} @@ -62,48 +69,53 @@ def dead_items(vfs_top, paths): # Scan for bad requests, and opportunities to optimize for path in paths: try: - n = vfs_top.lresolve(path) - except vfs.NodeError as e: - add_error('unable to resolve %s: %s' % (path, e)) + resolved = vfs.resolve(repo, path, follow=False) + except vfs.IOError as e: + add_error(e) + continue else: - if isinstance(n, vfs.BranchList): # rm /foo - branchname = n.name - dead_branches[branchname] = n - dead_saves.pop(branchname, None) # rm /foo obviates rm /foo/bar - elif isinstance(n, vfs.FakeSymlink) and isinstance(n.parent, - vfs.BranchList): - if n.name == 'latest': + leaf_name, leaf_item = resolved[-1] + if not leaf_item: + add_error('error: cannot access %s in %s' + % (path_msg(b'/'.join(name for name, item in resolved)), + path_msg(path))) + continue + if isinstance(leaf_item, vfs.RevList): # rm /foo + branchname = leaf_name + dead_branches[branchname] = leaf_item + dead_saves.pop(branchname, None) # rm /foo obviates rm /foo/bar + elif isinstance(leaf_item, vfs.Commit): # rm /foo/bar + if leaf_name == b'latest': add_error("error: cannot delete 'latest' symlink") else: - branchname = n.parent.name + branchname, branchitem = resolved[-2] if branchname not in dead_branches: - dead_saves.setdefault(branchname, []).append(n) + dead = leaf_item, branchitem + dead_saves.setdefault(branchname, []).append(dead) else: - add_error("don't know how to remove %r yet" % n.fullname()) + add_error("don't know how to remove %s yet" % path_msg(path)) if saved_errors: return None, None return dead_branches, dead_saves -def bup_rm(paths, compression=6, verbosity=None): - root = vfs.RefList(None) - - dead_branches, dead_saves = dead_items(root, paths) +def bup_rm(repo, paths, compression=6, verbosity=None): + dead_branches, dead_saves = dead_items(repo, paths) die_if_errors('not proceeding with any removals\n') updated_refs = {} # ref_name -> (original_ref, tip_commit(bin)) - for branch, node in dead_branches.iteritems(): - ref = 'refs/heads/' + branch + for branchname, branchitem in compat.items(dead_branches): + ref = b'refs/heads/' + branchname assert(not ref in updated_refs) - updated_refs[ref] = (node.hash, None) + updated_refs[ref] = (branchitem.oid, None) if dead_saves: writer = git.PackWriter(compression_level=compression) try: - for branch, saves in dead_saves.iteritems(): + for branch, saves in compat.items(dead_saves): assert(saves) - updated_refs['refs/heads/' + branch] = rm_saves(saves, writer) + updated_refs[b'refs/heads/' + branch] = rm_saves(saves, writer) except: if writer: writer.abort() @@ -116,25 +128,25 @@ def bup_rm(paths, compression=6, verbosity=None): # Only update the refs here, at the very end, so that if something # goes wrong above, the old refs will be undisturbed. Make an attempt # to update each ref. - for ref_name, info in updated_refs.iteritems(): + for ref_name, info in compat.items(updated_refs): orig_ref, new_ref = info try: if not new_ref: - git.delete_ref(ref_name, orig_ref.encode('hex')) + git.delete_ref(ref_name, hexlify(orig_ref)) else: git.update_ref(ref_name, new_ref, orig_ref) if verbosity: - new_hex = new_ref.encode('hex') - if orig_ref: - orig_hex = orig_ref.encode('hex') - log('updated %r (%s -> %s)\n' - % (ref_name, orig_hex, new_hex)) - else: - log('updated %r (%s)\n' % (ref_name, new_hex)) + log('updated %s (%s%s)\n' + % (path_msg(ref_name), + hexstr(orig_ref) + ' -> ' if orig_ref else '', + hexstr(new_ref))) except (git.GitError, ClientError) as ex: if new_ref: - add_error('while trying to update %r (%s -> %s): %s' - % (ref_name, orig_ref, new_ref, ex)) + add_error('while trying to update %s (%s%s): %s' + % (path_msg(ref_name), + hexstr(orig_ref) + ' -> ' if orig_ref else '', + hexstr(new_ref), + ex)) else: add_error('while trying to delete %r (%s): %s' - % (ref_name, orig_ref, ex)) + % (ref_name, hexstr(orig_ref), ex))