X-Git-Url: https://git.distorted.org.uk/~mdw/stgit/blobdiff_plain/ff432158b249307d44177db1adc569f726eefb18..3dccdc9bf6d5b2e72a982187f5f947b20f24fc87:/stgit/stack.py diff --git a/stgit/stack.py b/stgit/stack.py index f1f75e4..9958e7a 100644 --- a/stgit/stack.py +++ b/stgit/stack.py @@ -28,7 +28,7 @@ from stgit.run import * from stgit import git, basedir, templates from stgit.config import config from shutil import copyfile - +from stgit.lib import git as libgit, stackupgrade # stack exception class class StackException(StgException): @@ -162,8 +162,6 @@ class Patch(StgitObject): def create(self): os.mkdir(self._dir()) - self.create_empty_field('bottom') - self.create_empty_field('top') def delete(self, keep_log = False): if os.path.isdir(self._dir()): @@ -198,47 +196,35 @@ class Patch(StgitObject): def __update_top_ref(self, ref): git.set_ref(self.__top_ref, ref) + self._set_field('top', ref) + self._set_field('bottom', git.get_commit(ref).get_parent()) def __update_log_ref(self, ref): git.set_ref(self.__log_ref, ref) - def update_top_ref(self): - top = self.get_top() - if top: - self.__update_top_ref(top) - def get_old_bottom(self): - return self._get_field('bottom.old') + return git.get_commit(self.get_old_top()).get_parent() def get_bottom(self): - return self._get_field('bottom') - - def set_bottom(self, value, backup = False): - if backup: - curr = self._get_field('bottom') - self._set_field('bottom.old', curr) - self._set_field('bottom', value) + return git.get_commit(self.get_top()).get_parent() def get_old_top(self): return self._get_field('top.old') def get_top(self): - return self._get_field('top') + return git.rev_parse(self.__top_ref) def set_top(self, value, backup = False): if backup: - curr = self._get_field('top') - self._set_field('top.old', curr) - self._set_field('top', value) + curr_top = self.get_top() + self._set_field('top.old', curr_top) + self._set_field('bottom.old', git.get_commit(curr_top).get_parent()) self.__update_top_ref(value) def restore_old_boundaries(self): - bottom = self._get_field('bottom.old') top = self._get_field('top.old') - if top and bottom: - self._set_field('bottom', bottom) - self._set_field('top', top) + if top: self.__update_top_ref(top) return True else: @@ -296,9 +282,6 @@ class Patch(StgitObject): self._set_field('log', value) self.__update_log_ref(value) -# The current StGIT metadata format version. -FORMAT_VERSION = 2 - class PatchSet(StgitObject): def __init__(self, name = None): try: @@ -366,7 +349,8 @@ class PatchSet(StgitObject): def is_initialised(self): """Checks if series is already initialised """ - return bool(config.get(self.format_version_key())) + return config.get(stackupgrade.format_version_key(self.get_name()) + ) != None def shortlog(patches): @@ -385,7 +369,8 @@ class Series(PatchSet): # Update the branch to the latest format version if it is # initialized, but don't touch it if it isn't. - self.update_to_current_format_version() + stackupgrade.update_to_current_format_version( + libgit.Repository.default(), self.get_name()) self.__refs_base = 'refs/patches/%s' % self.get_name() @@ -399,86 +384,6 @@ class Series(PatchSet): # trash directory self.__trash_dir = os.path.join(self._dir(), 'trash') - def format_version_key(self): - return 'branch.%s.stgit.stackformatversion' % self.get_name() - - def update_to_current_format_version(self): - """Update a potentially older StGIT directory structure to the - latest version. Note: This function should depend as little as - possible on external functions that may change during a format - version bump, since it must remain able to process older formats.""" - - branch_dir = os.path.join(self._basedir(), 'patches', self.get_name()) - def get_format_version(): - """Return the integer format version number, or None if the - branch doesn't have any StGIT metadata at all, of any version.""" - fv = config.get(self.format_version_key()) - ofv = config.get('branch.%s.stgitformatversion' % self.get_name()) - if fv: - # Great, there's an explicitly recorded format version - # number, which means that the branch is initialized and - # of that exact version. - return int(fv) - elif ofv: - # Old name for the version info, upgrade it - config.set(self.format_version_key(), ofv) - config.unset('branch.%s.stgitformatversion' % self.get_name()) - return int(ofv) - elif os.path.isdir(os.path.join(branch_dir, 'patches')): - # There's a .git/patches//patches dirctory, which - # means this is an initialized version 1 branch. - return 1 - elif os.path.isdir(branch_dir): - # There's a .git/patches/ directory, which means - # this is an initialized version 0 branch. - return 0 - else: - # The branch doesn't seem to be initialized at all. - return None - def set_format_version(v): - out.info('Upgraded branch %s to format version %d' % (self.get_name(), v)) - config.set(self.format_version_key(), '%d' % v) - def mkdir(d): - if not os.path.isdir(d): - os.makedirs(d) - def rm(f): - if os.path.exists(f): - os.remove(f) - def rm_ref(ref): - if git.ref_exists(ref): - git.delete_ref(ref) - - # Update 0 -> 1. - if get_format_version() == 0: - mkdir(os.path.join(branch_dir, 'trash')) - patch_dir = os.path.join(branch_dir, 'patches') - mkdir(patch_dir) - refs_base = 'refs/patches/%s' % self.get_name() - for patch in (file(os.path.join(branch_dir, 'unapplied')).readlines() - + file(os.path.join(branch_dir, 'applied')).readlines()): - patch = patch.strip() - os.rename(os.path.join(branch_dir, patch), - os.path.join(patch_dir, patch)) - Patch(patch, patch_dir, refs_base).update_top_ref() - set_format_version(1) - - # Update 1 -> 2. - if get_format_version() == 1: - desc_file = os.path.join(branch_dir, 'description') - if os.path.isfile(desc_file): - desc = read_string(desc_file) - if desc: - config.set('branch.%s.description' % self.get_name(), desc) - rm(desc_file) - rm(os.path.join(branch_dir, 'current')) - rm_ref('refs/bases/%s' % self.get_name()) - set_format_version(2) - - # Make sure we're at the latest version. - if not get_format_version() in [None, FORMAT_VERSION]: - raise StackException('Branch %s is at format version %d, expected %d' - % (self.get_name(), get_format_version(), FORMAT_VERSION)) - def __patch_name_valid(self, name): """Raise an exception if the patch name is not valid. """ @@ -631,7 +536,8 @@ class Series(PatchSet): self.create_empty_field('applied') self.create_empty_field('unapplied') - config.set(self.format_version_key(), str(FORMAT_VERSION)) + config.set(stackupgrade.format_version_key(self.get_name()), + str(stackupgrade.FORMAT_VERSION)) def rename(self, to_name): """Renames a series @@ -755,13 +661,14 @@ class Series(PatchSet): try: git.delete_branch(self.get_name()) - except GitException: + except git.GitException: out.warn('Could not delete branch "%s"' % self.get_name()) config.remove_section('branch.%s' % self.get_name()) config.remove_section('branch.%s.stgit' % self.get_name()) def refresh_patch(self, files = None, message = None, edit = False, + empty = False, show_patch = False, cache_update = True, author_name = None, author_email = None, @@ -793,8 +700,6 @@ class Series(PatchSet): author_name = patch.get_authname() if not author_email: author_email = patch.get_authemail() - if not author_date: - author_date = patch.get_authdate() if not committer_name: committer_name = patch.get_commname() if not committer_email: @@ -805,9 +710,16 @@ class Series(PatchSet): if not bottom: bottom = patch.get_bottom() + if empty: + tree_id = git.get_commit(bottom).get_tree() + else: + tree_id = None + commit_id = git.commit(files = files, message = descr, parents = [bottom], cache_update = cache_update, + tree_id = tree_id, + set_head = True, allowempty = True, author_name = author_name, author_email = author_email, @@ -815,7 +727,6 @@ class Series(PatchSet): committer_name = committer_name, committer_email = committer_email) - patch.set_bottom(bottom, backup = backup) patch.set_top(commit_id, backup = backup) patch.set_description(descr) patch.set_authname(author_name) @@ -829,26 +740,6 @@ class Series(PatchSet): return commit_id - def undo_refresh(self): - """Undo the patch boundaries changes caused by 'refresh' - """ - name = self.get_current() - assert(name) - - patch = self.get_patch(name) - old_bottom = patch.get_old_bottom() - old_top = patch.get_old_top() - - # the bottom of the patch is not changed by refresh. If the - # old_bottom is different, there wasn't any previous 'refresh' - # command (probably only a 'push') - if old_bottom != patch.get_bottom() or old_top == patch.get_top(): - raise StackException, 'No undo information available' - - git.reset(tree_id = old_top, check_out = False) - if patch.restore_old_boundaries(): - self.log_patch(patch, 'undo') - def new_patch(self, name, message = None, can_edit = True, unapplied = False, show_patch = False, top = None, bottom = None, commit = True, @@ -929,11 +820,8 @@ class Series(PatchSet): committer_name = committer_name, committer_email = committer_email) # set the patch top to the new commit - patch.set_bottom(bottom) patch.set_top(commit_id) else: - assert top != bottom - patch.set_bottom(bottom) patch.set_top(top) self.log_patch(patch, 'new') @@ -987,7 +875,6 @@ class Series(PatchSet): if head == bottom: # reset the backup information. No logging since the # patch hasn't changed - patch.set_bottom(head, backup = True) patch.set_top(top, backup = True) else: @@ -1015,7 +902,6 @@ class Series(PatchSet): committer_name = committer_name, committer_email = committer_email) - patch.set_bottom(head, backup = True) patch.set_top(top, backup = True) self.log_patch(patch, 'push(f)') @@ -1088,7 +974,6 @@ class Series(PatchSet): if head == bottom: # A fast-forward push. Just reset the backup # information. No need for logging - patch.set_bottom(bottom, backup = True) patch.set_top(top, backup = True) git.switch(top) @@ -1111,11 +996,10 @@ class Series(PatchSet): # merge can fail but the patch needs to be pushed try: - git.merge(bottom, head, top, recursive = True) + git.merge_recursive(bottom, head, top) except git.GitException, ex: out.error('The merge failed during "push".', - 'Use "refresh" after fixing the conflicts or' - ' revert the operation with "push --undo".') + 'Revert the operation with "stg undo".') append_string(self.__applied_file, name) @@ -1131,39 +1015,14 @@ class Series(PatchSet): log = 'push' self.refresh_patch(bottom = head, cache_update = False, log = log) else: - # we store the correctly merged files only for - # tracking the conflict history. Note that the - # git.merge() operations should always leave the index - # in a valid state (i.e. only stage 0 files) + # we make the patch empty, with the merged state in the + # working tree. self.refresh_patch(bottom = head, cache_update = False, - log = 'push(c)') + empty = True, log = 'push(c)') raise StackException, str(ex) return modified - def undo_push(self): - name = self.get_current() - assert(name) - - patch = self.get_patch(name) - old_bottom = patch.get_old_bottom() - old_top = patch.get_old_top() - - # the top of the patch is changed by a push operation only - # together with the bottom (otherwise the top was probably - # modified by 'refresh'). If they are both unchanged, there - # was a fast forward - if old_bottom == patch.get_bottom() and old_top != patch.get_top(): - raise StackException, 'No undo information available' - - git.reset() - self.pop_patch(name) - ret = patch.restore_old_boundaries() - if ret: - self.log_patch(patch, 'undo') - - return ret - def pop_patch(self, name, keep = False): """Pops the top patch from the stack """