X-Git-Url: https://git.distorted.org.uk/~mdw/stgit/blobdiff_plain/bd69feaf7c3c94b6e7e216ea8091064af9cdfa97..c2a8af1d63280cf5d00574e3e059735a61fd64c9:/stgit/stack.py diff --git a/stgit/stack.py b/stgit/stack.py index 2c05407..634588d 100644 --- a/stgit/stack.py +++ b/stgit/stack.py @@ -276,79 +276,62 @@ class Patch(StgitObject): # The current StGIT metadata format version. FORMAT_VERSION = 2 -def format_version_key(branch): - return 'branch.%s.stgitformatversion' % branch - -def update_to_current_format_version(branch, git_dir): - """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(git_dir, 'patches', branch) - 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(format_version_key(branch)) - 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 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 +class PatchSet(StgitObject): + def get_name(self): + return self.__name + def set_name(self, name): + self.__name = name + + def get_head(self): + """Return the head of the branch + """ + crt = self.get_current_patch() + if crt: + return crt.get_top() 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' % (branch, v)) - config.set(format_version_key(branch), '%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) - - # 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_dir = os.path.join(git_dir, 'refs', 'patches', branch) - mkdir(refs_dir) - 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_dir).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' % branch, desc) - rm(desc_file) - rm(os.path.join(branch_dir, 'current')) - rm(os.path.join(git_dir, 'refs', 'bases', branch)) - 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' - % (branch, get_format_version(), FORMAT_VERSION)) - -class Series(StgitObject): + return self.get_base() + + def get_protected(self): + return os.path.isfile(os.path.join(self._dir(), 'protected')) + + def protect(self): + protect_file = os.path.join(self._dir(), 'protected') + if not os.path.isfile(protect_file): + create_empty_file(protect_file) + + def unprotect(self): + protect_file = os.path.join(self._dir(), 'protected') + if os.path.isfile(protect_file): + os.remove(protect_file) + + def __branch_descr(self): + return 'branch.%s.description' % self.get_name() + + def get_description(self): + return config.get(self.__branch_descr()) or '' + + def set_description(self, line): + if line: + config.set(self.__branch_descr(), line) + else: + config.unset(self.__branch_descr()) + + def head_top_equal(self): + """Return true if the head and the top are the same + """ + crt = self.get_current_patch() + if not crt: + # we don't care, no patches applied + return True + return git.get_head() == crt.get_top() + + def is_initialised(self): + """Checks if series is already initialised + """ + return bool(config.get(self.format_version_key())) + + +class Series(PatchSet): """Class including the operations on series """ def __init__(self, name = None): @@ -356,21 +339,21 @@ class Series(StgitObject): """ try: if name: - self.__name = name + self.set_name (name) else: - self.__name = git.get_head_file() + self.set_name (git.get_head_file()) self.__base_dir = basedir.get() except git.GitException, ex: raise StackException, 'GIT tree not initialised: %s' % ex - self._set_dir(os.path.join(self.__base_dir, 'patches', self.__name)) + self._set_dir(os.path.join(self.__base_dir, 'patches', self.get_name())) # Update the branch to the latest format version if it is # initialized, but don't touch it if it isn't. - update_to_current_format_version(self.__name, self.__base_dir) + self.update_to_current_format_version() self.__refs_dir = os.path.join(self.__base_dir, 'refs', 'patches', - self.__name) + self.get_name()) self.__applied_file = os.path.join(self._dir(), 'applied') self.__unapplied_file = os.path.join(self._dir(), 'unapplied') @@ -382,17 +365,90 @@ class Series(StgitObject): # 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.__base_dir, '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) + + # 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_dir = os.path.join(self.__base_dir, 'refs', 'patches', self.get_name()) + mkdir(refs_dir) + 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_dir).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(os.path.join(self.__base_dir, 'refs', 'bases', 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. """ if not name or re.search('[^\w.-]', name): raise StackException, 'Invalid patch name: "%s"' % name - def get_branch(self): - """Return the branch name for the Series object - """ - return self.__name - def get_patch(self, name): """Return a Patch object for the given name """ @@ -422,12 +478,12 @@ class Series(StgitObject): def get_applied(self): if not os.path.isfile(self.__applied_file): - raise StackException, 'Branch "%s" not initialised' % self.__name + raise StackException, 'Branch "%s" not initialised' % self.get_name() return read_strings(self.__applied_file) def get_unapplied(self): if not os.path.isfile(self.__unapplied_file): - raise StackException, 'Branch "%s" not initialised' % self.__name + raise StackException, 'Branch "%s" not initialised' % self.get_name() return read_strings(self.__unapplied_file) def get_hidden(self): @@ -444,76 +500,42 @@ class Series(StgitObject): # No bottommost patch, so just return HEAD return git.get_head() - def get_head(self): - """Return the head of the branch - """ - crt = self.get_current_patch() - if crt: - return crt.get_top() - else: - return self.get_base() - - def get_protected(self): - return os.path.isfile(os.path.join(self._dir(), 'protected')) - - def protect(self): - protect_file = os.path.join(self._dir(), 'protected') - if not os.path.isfile(protect_file): - create_empty_file(protect_file) - - def unprotect(self): - protect_file = os.path.join(self._dir(), 'protected') - if os.path.isfile(protect_file): - os.remove(protect_file) - - def __branch_descr(self): - return 'branch.%s.description' % self.get_branch() - - def get_description(self): - return config.get(self.__branch_descr()) or '' - - def set_description(self, line): - if line: - config.set(self.__branch_descr(), line) - else: - config.unset(self.__branch_descr()) - def get_parent_remote(self): - value = config.get('branch.%s.remote' % self.__name) + value = config.get('branch.%s.remote' % self.get_name()) if value: return value elif 'origin' in git.remotes_list(): out.note(('No parent remote declared for stack "%s",' - ' defaulting to "origin".' % self.__name), + ' defaulting to "origin".' % self.get_name()), ('Consider setting "branch.%s.remote" and' ' "branch.%s.merge" with "git repo-config".' - % (self.__name, self.__name))) + % (self.get_name(), self.get_name()))) return 'origin' else: - raise StackException, 'Cannot find a parent remote for "%s"' % self.__name + raise StackException, 'Cannot find a parent remote for "%s"' % self.get_name() def __set_parent_remote(self, remote): - value = config.set('branch.%s.remote' % self.__name, remote) + value = config.set('branch.%s.remote' % self.get_name(), remote) def get_parent_branch(self): - value = config.get('branch.%s.stgit.parentbranch' % self.__name) + value = config.get('branch.%s.stgit.parentbranch' % self.get_name()) if value: return value elif git.rev_parse('heads/origin'): out.note(('No parent branch declared for stack "%s",' - ' defaulting to "heads/origin".' % self.__name), + ' defaulting to "heads/origin".' % self.get_name()), ('Consider setting "branch.%s.stgit.parentbranch"' - ' with "git repo-config".' % self.__name)) + ' with "git repo-config".' % self.get_name())) return 'heads/origin' else: - raise StackException, 'Cannot find a parent branch for "%s"' % self.__name + raise StackException, 'Cannot find a parent branch for "%s"' % self.get_name() def __set_parent_branch(self, name): - if config.get('branch.%s.remote' % self.__name): + if config.get('branch.%s.remote' % self.get_name()): # Never set merge if remote is not set to avoid # possibly-erroneous lookups into 'origin' - config.set('branch.%s.merge' % self.__name, name) - config.set('branch.%s.stgit.parentbranch' % self.__name, name) + config.set('branch.%s.merge' % self.get_name(), name) + config.set('branch.%s.stgit.parentbranch' % self.get_name(), name) def set_parent(self, remote, localbranch): if localbranch: @@ -521,7 +543,7 @@ class Series(StgitObject): self.__set_parent_branch(localbranch) # We'll enforce this later # else: -# raise StackException, 'Parent branch (%s) should be specified for %s' % localbranch, self.__name +# raise StackException, 'Parent branch (%s) should be specified for %s' % localbranch, self.get_name() def __patch_is_current(self, patch): return patch.get_name() == self.get_current() @@ -547,31 +569,17 @@ class Series(StgitObject): return self.patch_applied(name) or self.patch_unapplied(name) \ or self.patch_hidden(name) - def head_top_equal(self): - """Return true if the head and the top are the same - """ - crt = self.get_current_patch() - if not crt: - # we don't care, no patches applied - return True - return git.get_head() == crt.get_top() - - def is_initialised(self): - """Checks if series is already initialised - """ - return bool(config.get(format_version_key(self.get_branch()))) - def init(self, create_at=False, parent_remote=None, parent_branch=None): """Initialises the stgit series """ if self.is_initialised(): - raise StackException, '%s already initialized' % self.get_branch() + raise StackException, '%s already initialized' % self.get_name() for d in [self._dir(), self.__refs_dir]: if os.path.exists(d): raise StackException, '%s already exists' % d if (create_at!=False): - git.create_branch(self.__name, create_at) + git.create_branch(self.get_name(), create_at) os.makedirs(self.__patch_dir) @@ -580,8 +588,9 @@ class Series(StgitObject): self.create_empty_field('applied') self.create_empty_field('unapplied') os.makedirs(self.__refs_dir) + self._set_field('orig-base', git.get_head()) - config.set(format_version_key(self.get_branch()), str(FORMAT_VERSION)) + config.set(self.format_version_key(), str(FORMAT_VERSION)) def rename(self, to_name): """Renames a series @@ -589,19 +598,19 @@ class Series(StgitObject): to_stack = Series(to_name) if to_stack.is_initialised(): - raise StackException, '"%s" already exists' % to_stack.get_branch() + raise StackException, '"%s" already exists' % to_stack.get_name() - git.rename_branch(self.__name, to_name) + git.rename_branch(self.get_name(), to_name) if os.path.isdir(self._dir()): rename(os.path.join(self.__base_dir, 'patches'), - self.__name, to_stack.__name) + self.get_name(), to_stack.get_name()) if os.path.exists(self.__refs_dir): rename(os.path.join(self.__base_dir, 'refs', 'patches'), - self.__name, to_stack.__name) + self.get_name(), to_stack.get_name()) # Rename the config section - config.rename_section("branch.%s" % self.__name, + config.rename_section("branch.%s" % self.get_name(), "branch.%s" % to_name) self.__init__(to_name) @@ -618,7 +627,7 @@ class Series(StgitObject): new_series = Series(target_series) # generate an artificial description file - new_series.set_description('clone of "%s"' % self.__name) + new_series.set_description('clone of "%s"' % self.get_name()) # clone self's entire series as unapplied patches try: @@ -648,15 +657,15 @@ class Series(StgitObject): new_series.forward_patches(applied) # Clone parent informations - value = config.get('branch.%s.remote' % self.__name) + value = config.get('branch.%s.remote' % self.get_name()) if value: config.set('branch.%s.remote' % target_series, value) - value = config.get('branch.%s.merge' % self.__name) + value = config.get('branch.%s.merge' % self.get_name()) if value: config.set('branch.%s.merge' % target_series, value) - value = config.get('branch.%s.stgit.parentbranch' % self.__name) + value = config.get('branch.%s.stgit.parentbranch' % self.get_name()) if value: config.set('branch.%s.stgit.parentbranch' % target_series, value) @@ -707,10 +716,10 @@ class Series(StgitObject): # Cleanup parent informations # FIXME: should one day make use of git-config --section-remove, # scheduled for 1.5.1 - config.unset('branch.%s.remote' % self.__name) - config.unset('branch.%s.merge' % self.__name) - config.unset('branch.%s.stgit.parentbranch' % self.__name) - config.unset('branch.%s.stgitformatversion' % self.__name) + config.unset('branch.%s.remote' % self.get_name()) + config.unset('branch.%s.merge' % self.get_name()) + config.unset('branch.%s.stgit.parentbranch' % self.get_name()) + config.unset(self.format_version_key()) def refresh_patch(self, files = None, message = None, edit = False, show_patch = False, @@ -1092,13 +1101,13 @@ class Series(StgitObject): patch = Patch(name, self.__patch_dir, self.__refs_dir) - if git.get_head_file() == self.get_branch(): + if git.get_head_file() == self.get_name(): if keep and not git.apply_diff(git.get_head(), patch.get_bottom()): raise StackException( 'Failed to pop patches while preserving the local changes') git.switch(patch.get_bottom(), keep) else: - git.set_branch(self.get_branch(), patch.get_bottom()) + git.set_branch(self.get_name(), patch.get_bottom()) # save the new applied list idx = applied.index(name) + 1