aboutsummaryrefslogtreecommitdiffhomepage
path: root/gbp/deb
diff options
context:
space:
mode:
authorGuido Günther <agx@sigxcpu.org>2018-02-26 15:08:05 +0100
committerGuido Günther <agx@sigxcpu.org>2018-02-26 15:08:05 +0100
commit53016921be103ae1e906b09bb840d3f0c7fcca0f (patch)
tree6e9bd7aad84bac8578895788f91e9168e387fab8 /gbp/deb
parent23874c2c50ebd57426797d38f4f9c6c40dc336ec (diff)
Move rollback code out of import_orig
so it can be reused in gbp import-ref
Diffstat (limited to 'gbp/deb')
-rw-r--r--gbp/deb/git.py8
-rw-r--r--gbp/deb/rollbackgit.py134
2 files changed, 142 insertions, 0 deletions
diff --git a/gbp/deb/git.py b/gbp/deb/git.py
index 98f8ee29..8cda9575 100644
--- a/gbp/deb/git.py
+++ b/gbp/deb/git.py
@@ -364,4 +364,12 @@ class DebianGitRepository(PkgGitRepository):
raise GitRepositoryError("Error creating %s: %s" % (output, e))
return True
+ def vcs_tag_parent(self, vcs_tag_format, version):
+ """If linking to the upstream VCS get the commit id"""
+ if vcs_tag_format:
+ return [self.rev_parse("%s^{}" % self.version_to_tag(vcs_tag_format, version))],
+ else:
+ return None
+
+
# vim:et:ts=4:sw=4:et:sts=4:ai:set list listchars=tab\:»·,trail\:·:
diff --git a/gbp/deb/rollbackgit.py b/gbp/deb/rollbackgit.py
new file mode 100644
index 00000000..8bd75b4a
--- /dev/null
+++ b/gbp/deb/rollbackgit.py
@@ -0,0 +1,134 @@
+# vim: set fileencoding=utf-8 :
+#
+# (C) 2018 Guido Günther <agx@sigxcpu.org>
+"""A git repository for Debian packages that can roll back operations"""
+
+
+from .. import log
+from .. git import GitRepositoryError
+from . git import DebianGitRepository
+
+
+class RollbackError(GitRepositoryError):
+ """
+ Error raised if the rollback failed
+ """
+ def __init__(self, errors):
+ self.msg = "Automatic rollback failed"
+ super(RollbackError, self).__init__(self.msg)
+ self.errors = errors
+
+ def __str__(self):
+ return "%s %s" % (self.msg, self.errors)
+
+
+class RollbackDebianGitRepository(DebianGitRepository):
+ """
+ Like a DebianGitRepository but can also perform rollbacks and knows
+ about some of the inner workings upstream vcs_tag, …
+ """
+ def __init__(self, *args, **kwargs):
+ self.rollbacks = []
+ self.rollback_errors = []
+ DebianGitRepository.__init__(self, *args, **kwargs)
+
+ def has_rollbacks(self):
+ return len(self.rollbacks) > 0
+
+ def rrr(self, refname, action, reftype):
+ """
+ Remember ref for rollback
+
+ @param refname: ref to roll back
+ @param action: the rollback action (delete, reset, ...)
+ @param reftype: the reference type (tag, branch, ...)
+ """
+ sha = None
+
+ if action == 'reset':
+ try:
+ sha = self.rev_parse(refname)
+ except GitRepositoryError as err:
+ log.warn("Failed to rev-parse %s: %s" % (refname, err))
+ elif action == 'delete':
+ pass
+ elif action == 'abortmerge':
+ pass
+ else:
+ raise GitRepositoryError("Unknown action %s for %s %s" % (action, reftype, refname))
+ self.rollbacks.append((refname, reftype, action, sha))
+
+ def rrr_branch(self, branchname, action='reset-or-delete'):
+ if action == 'reset-or-delete':
+ if self.has_branch(branchname):
+ return self.rrr(branchname, 'reset', 'branch')
+ else:
+ return self.rrr(branchname, 'delete', 'branch')
+ else:
+ return self.rrr(branchname, action, 'branch')
+
+ def rrr_tag(self, tagname, action='delete'):
+ return self.rrr(tagname, action, 'tag')
+
+ def rrr_merge(self, commit, action='abortmerge'):
+ return self.rrr(commit, action, 'commit')
+
+ def rollback(self):
+ """
+ Perform a complete rollback
+
+ Try to roll back as much as possible and remember what failed.
+ """
+ for (name, reftype, action, sha) in self.rollbacks:
+ try:
+ if action == 'delete':
+ log.info('Rolling back %s %s by deleting it' % (reftype, name))
+ if reftype == 'tag':
+ self.delete_tag(name)
+ elif reftype == 'branch':
+ self.delete_branch(name)
+ else:
+ raise GitRepositoryError("Don't know how to delete %s %s" % (reftype, name))
+ elif action == 'reset' and reftype == 'branch':
+ log.info('Rolling back branch %s by resetting it to %s' % (name, sha))
+ self.update_ref("refs/heads/%s" % name, sha, msg="gbp import-orig: failure rollback of %s" % name)
+ elif action == 'abortmerge':
+ if self.is_in_merge():
+ log.info('Rolling back failed merge of %s' % name)
+ self.abort_merge()
+ else:
+ log.info("Nothing to rollback for merge of '%s'" % name)
+ else:
+ raise GitRepositoryError("Don't know how to %s %s %s" % (action, reftype, name))
+ except GitRepositoryError as e:
+ self.rollback_errors.append((name, reftype, action, sha, e))
+ if self.rollback_errors:
+ raise RollbackError(self.rollback_errors)
+
+ # Wrapped methods for rollbacks
+ def create_tag(self, *args, **kwargs):
+ name = kwargs['name']
+ ret = super(RollbackDebianGitRepository, self).create_tag(*args, **kwargs)
+ self.rrr_tag(name)
+ return ret
+
+ def commit_dir(self, *args, **kwargs):
+ import_branch = kwargs['branch']
+ self.rrr_branch(import_branch)
+ return super(RollbackDebianGitRepository, self).commit_dir(*args, **kwargs)
+
+ def create_branch(self, *args, **kwargs):
+ branch = kwargs['branch']
+ ret = super(RollbackDebianGitRepository, self).create_branch(*args, **kwargs)
+ self.rrr_branch(branch, 'delete')
+ return ret
+
+ def merge(self, *args, **kwargs):
+ commit = args[0] if args else kwargs['commit']
+ try:
+ return super(RollbackDebianGitRepository, self).merge(*args, **kwargs)
+ except GitRepositoryError:
+ # Only cleanup in the error case to undo working copy
+ # changes. Resetting the refs handles the other cases.
+ self.rrr_merge(commit)
+ raise