X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/stgit/blobdiff_plain/ca2160163b2a8209e5b04e155a8f4dafbe9334c8..6972fd6b0cc82580fb94b3388e4d4879f69b09a8:/stgit/commands/common.py diff --git a/stgit/commands/common.py b/stgit/commands/common.py index 0fc157a..2a80e8c 100644 --- a/stgit/commands/common.py +++ b/stgit/commands/common.py @@ -21,20 +21,20 @@ 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 -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 @@ -72,7 +72,7 @@ def parse_rev(rev): # No, we can't parse that. raise RevParseException -def git_id(rev): +def git_id(crt_series, rev): """Return the GIT id """ if not rev: @@ -110,7 +110,7 @@ def check_local_changes(): raise CmdException, \ 'local changes in the tree. Use "refresh" or "status --reset"' -def check_head_top_equal(): +def check_head_top_equal(crt_series): if not crt_series.head_top_equal(): raise CmdException( """HEAD and top are not the same. This can happen if you @@ -123,7 +123,7 @@ def check_conflicts(): 'Unsolved conflicts. Please resolve them first or\n' \ ' revert the changes with "status --reset"' -def print_crt_patch(branch = None): +def print_crt_patch(crt_series, branch = None): if not branch: patch = crt_series.get_current() else: @@ -156,7 +156,7 @@ def resolved_all(reset = None): resolved(filename, reset) os.remove(os.path.join(basedir.get(), 'conflicts')) -def push_patches(patches, check_merged = False): +def push_patches(crt_series, patches, check_merged = False): """Push multiple patches onto the stack. This function is shared between the push and pull commands """ @@ -195,7 +195,7 @@ def push_patches(patches, check_merged = False): else: out.done() -def pop_patches(patches, keep = False): +def pop_patches(crt_series, patches, keep = False): """Pop the patches in the list from the stack. It is assumed that the patches are listed in the stack reverse order. """ @@ -317,14 +317,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(crt_series): # pop all patches applied = crt_series.get_applied() if len(applied) > 0: @@ -333,9 +326,9 @@ def prepare_rebase(force=None): out.done() return applied -def rebase(target): +def rebase(crt_series, target): try: - tree_id = git_id(target) + tree_id = git_id(crt_series, target) except: # it might be that we use a custom rebase command with its own # target type @@ -350,12 +343,12 @@ def rebase(target): git.rebase(tree_id = tree_id) out.done() -def post_rebase(applied, nopush, merged): +def post_rebase(crt_series, applied, nopush, merged): # memorize that we rebased to here crt_series._set_field('orig-base', git.get_head()) # push the patches back if not nopush: - push_patches(applied, merged) + push_patches(crt_series, applied, merged) # # Patch description/e-mail/diff parsing @@ -486,3 +479,72 @@ 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): + def __init__(self, needs_current_series = True): + self.needs_current_series = needs_current_series + @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()