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
# Command exception class
-class CmdException(Exception):
+class CmdException(StgException):
pass
-
# Utility functions
-class RevParseException(Exception):
+class RevParseException(StgException):
"""Revision spec parse error."""
pass
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')):
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:
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):
# 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()