diff options
author | Guido Günther <agx@sigxcpu.org> | 2018-02-26 15:08:05 +0100 |
---|---|---|
committer | Guido Günther <agx@sigxcpu.org> | 2018-02-26 15:08:05 +0100 |
commit | 53016921be103ae1e906b09bb840d3f0c7fcca0f (patch) | |
tree | 6e9bd7aad84bac8578895788f91e9168e387fab8 /gbp/deb | |
parent | 23874c2c50ebd57426797d38f4f9c6c40dc336ec (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.py | 8 | ||||
-rw-r--r-- | gbp/deb/rollbackgit.py | 134 |
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 |