X-Git-Url: https://git.distorted.org.uk/~mdw/stgit/blobdiff_plain/fe25b999a6259b475a2c377ded94e20abbb12034..6dd8fafabb5b8e266a85f13c8851ca8a66a1a405:/stgit/commands/common.py diff --git a/stgit/commands/common.py b/stgit/commands/common.py index 8ed47ca..27ef465 100644 --- a/stgit/commands/common.py +++ b/stgit/commands/common.py @@ -21,8 +21,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import sys, os, os.path, re from optparse import OptionParser, make_option +from stgit.exception import * from stgit.utils import * from stgit.out import * +from stgit.run import * from stgit import stack, git, basedir from stgit.config import config, file_extensions @@ -30,12 +32,11 @@ crt_series = None # Command exception class -class CmdException(Exception): +class CmdException(StgException): pass - # Utility functions -class RevParseException(Exception): +class RevParseException(StgException): """Revision spec parse error.""" pass @@ -114,9 +115,9 @@ def check_local_changes(): def check_head_top_equal(): if not crt_series.head_top_equal(): raise CmdException( - 'HEAD and top are not the same. You probably committed\n' - ' changes to the tree outside of StGIT. To bring them\n' - ' into StGIT, use the "assimilate" command') +"""HEAD and top are not the same. This can happen if you + modify a branch with git. The "assimilate" command can + fix this situation.""") def check_conflicts(): if os.path.exists(os.path.join(basedir.get(), 'conflicts')): @@ -318,14 +319,7 @@ def address_or_alias(addr_str): for addr in addr_str.split(',')] return ', '.join([addr for addr in addr_list if addr]) -def prepare_rebase(force=None): - if not force: - # Be sure we won't loose results of stg-(un)commit by error. - # Do not require an existing orig-base for compatibility with 0.12 and earlier. - origbase = crt_series._get_field('orig-base') - if origbase and crt_series.get_base() != origbase: - raise CmdException, 'Rebasing would possibly lose data' - +def prepare_rebase(): # pop all patches applied = crt_series.get_applied() if len(applied) > 0: @@ -335,11 +329,20 @@ def prepare_rebase(force=None): return applied def rebase(target): - if target == git.get_head(): + try: + tree_id = git_id(target) + except: + # it might be that we use a custom rebase command with its own + # target type + tree_id = target + if tree_id == git.get_head(): out.info('Already at "%s", no need for rebasing.' % target) return - out.start('Rebasing to "%s"' % target) - git.reset(tree_id = git_id(target)) + if target: + out.start('Rebasing to "%s"' % target) + else: + out.start('Rebasing to the default target') + git.rebase(tree_id = tree_id) out.done() def post_rebase(applied, nopush, merged): @@ -478,3 +481,70 @@ def parse_patch(fobj): # we don't yet have an agreed place for the creation date. # Just return None return (descr, authname, authemail, authdate, diff) + +def readonly_constant_property(f): + """Decorator that converts a function that computes a value to an + attribute that returns the value. The value is computed only once, + the first time it is accessed.""" + def new_f(self): + n = '__' + f.__name__ + if not hasattr(self, n): + setattr(self, n, f(self)) + return getattr(self, n) + return property(new_f) + +class DirectoryException(StgException): + pass + +class _Directory(object): + @readonly_constant_property + def git_dir(self): + try: + return Run('git-rev-parse', '--git-dir' + ).discard_stderr().output_one_line() + except RunException: + raise DirectoryException('No git repository found') + @readonly_constant_property + def __topdir_path(self): + try: + lines = Run('git-rev-parse', '--show-cdup' + ).discard_stderr().output_lines() + if len(lines) == 0: + return '.' + elif len(lines) == 1: + return lines[0] + else: + raise RunException('Too much output') + except RunException: + raise DirectoryException('No git repository found') + @readonly_constant_property + def is_inside_git_dir(self): + return { 'true': True, 'false': False + }[Run('git-rev-parse', '--is-inside-git-dir' + ).output_one_line()] + @readonly_constant_property + def is_inside_worktree(self): + return { 'true': True, 'false': False + }[Run('git-rev-parse', '--is-inside-work-tree' + ).output_one_line()] + def cd_to_topdir(self): + os.chdir(self.__topdir_path) + +class DirectoryAnywhere(_Directory): + def setup(self): + pass + +class DirectoryHasRepository(_Directory): + def setup(self): + self.git_dir # might throw an exception + +class DirectoryInWorktree(DirectoryHasRepository): + def setup(self): + DirectoryHasRepository.setup(self) + if not self.is_inside_worktree: + raise DirectoryException('Not inside a git worktree') + +class DirectoryGotoToplevel(DirectoryInWorktree): + def setup(self): + DirectoryInWorktree.setup(self) + self.cd_to_topdir()