+"""A Python class hierarchy wrapping the StGit on-disk metadata."""
+
import os.path
from stgit import exception, utils
from stgit.lib import git, stackupgrade
+from stgit.config import config
+
+class StackException(exception.StgException):
+ """Exception raised by L{stack} objects."""
class Patch(object):
+ """Represents an StGit patch. This class is mainly concerned with
+ reading and writing the on-disk representation of a patch."""
def __init__(self, stack, name):
self.__stack = stack
self.__name = name
old_log = [self.__stack.repository.refs.get(self.__log_ref)]
except KeyError:
old_log = []
- cd = git.Commitdata(tree = new_commit.data.tree, parents = old_log,
+ cd = git.CommitData(tree = new_commit.data.tree, parents = old_log,
message = '%s\t%s' % (msg, new_commit.sha1))
c = self.__stack.repository.commit(cd)
self.__stack.repository.refs.set(self.__log_ref, c, msg)
class PatchOrder(object):
"""Keeps track of patch order, and which patches are applied.
Works with patch names, not actual patches."""
- __list_order = [ 'applied', 'unapplied' ]
def __init__(self, stack):
self.__stack = stack
self.__lists = {}
lambda self, val: self.__set_list('applied', val))
unapplied = property(lambda self: self.__get_list('unapplied'),
lambda self, val: self.__set_list('unapplied', val))
+ hidden = property(lambda self: self.__get_list('hidden'),
+ lambda self, val: self.__set_list('hidden', val))
+ all = property(lambda self: self.applied + self.unapplied + self.hidden)
+ all_visible = property(lambda self: self.applied + self.unapplied)
+
+ @staticmethod
+ def create(stackdir):
+ """Create the PatchOrder specific files
+ """
+ utils.create_empty_file(os.path.join(stackdir, 'applied'))
+ utils.create_empty_file(os.path.join(stackdir, 'unapplied'))
+ utils.create_empty_file(os.path.join(stackdir, 'hidden'))
class Patches(object):
- """Creates Patch objects."""
+ """Creates L{Patch} objects. Makes sure there is only one such object
+ per patch."""
def __init__(self, stack):
self.__stack = stack
def create_patch(name):
self.__patches[name] = p
return p
-class Stack(object):
+class Stack(git.Branch):
+ """Represents an StGit stack (that is, a git branch with some extra
+ metadata)."""
+ __repo_subdir = 'patches'
+
def __init__(self, repository, name):
- self.__repository = repository
- self.__name = name
- try:
- self.head
- except KeyError:
- raise exception.StgException('%s: no such branch' % name)
+ git.Branch.__init__(self, repository, name)
self.__patchorder = PatchOrder(self)
self.__patches = Patches(self)
- stackupgrade.update_to_current_format_version(repository, name)
- name = property(lambda self: self.__name)
- repository = property(lambda self: self.__repository)
+ if not stackupgrade.update_to_current_format_version(repository, name):
+ raise StackException('%s: branch not initialized' % name)
patchorder = property(lambda self: self.__patchorder)
patches = property(lambda self: self.__patches)
@property
def directory(self):
- return os.path.join(self.__repository.directory, 'patches', self.__name)
- def __ref(self):
- return 'refs/heads/%s' % self.__name
- @property
- def head(self):
- return self.__repository.refs.get(self.__ref())
- def set_head(self, commit, msg):
- self.__repository.refs.set(self.__ref(), commit, msg)
+ return os.path.join(self.repository.directory, self.__repo_subdir, self.name)
@property
def base(self):
if self.patchorder.applied:
return True
return self.head == self.patches.get(self.patchorder.applied[-1]).commit
+ def set_parents(self, remote, branch):
+ if remote:
+ self.set_parent_remote(remote)
+ if branch:
+ self.set_parent_branch(branch)
+
+ @classmethod
+ def initialise(cls, repository, name = None):
+ """Initialise a Git branch to handle patch series.
+
+ @param repository: The L{Repository} where the L{Stack} will be created
+ @param name: The name of the L{Stack}
+ """
+ if not name:
+ name = repository.current_branch_name
+ # make sure that the corresponding Git branch exists
+ git.Branch(repository, name)
+
+ dir = os.path.join(repository.directory, cls.__repo_subdir, name)
+ compat_dir = os.path.join(dir, 'patches')
+ if os.path.exists(dir):
+ raise StackException('%s: branch already initialized' % name)
+
+ # create the stack directory and files
+ utils.create_dirs(dir)
+ utils.create_dirs(compat_dir)
+ PatchOrder.create(dir)
+ config.set(stackupgrade.format_version_key(name),
+ str(stackupgrade.FORMAT_VERSION))
+
+ return repository.get_stack(name)
+
+ @classmethod
+ def create(cls, repository, name,
+ create_at = None, parent_remote = None, parent_branch = None):
+ """Create and initialise a Git branch returning the L{Stack} object.
+
+ @param repository: The L{Repository} where the L{Stack} will be created
+ @param name: The name of the L{Stack}
+ @param create_at: The Git id used as the base for the newly created
+ Git branch
+ @param parent_remote: The name of the remote Git branch
+ @param parent_branch: The name of the parent Git branch
+ """
+ git.Branch.create(repository, name, create_at = create_at)
+ stack = cls.initialise(repository, name)
+ stack.set_parents(parent_remote, parent_branch)
+ return stack
+
class Repository(git.Repository):
+ """A git L{Repository<git.Repository>} with some added StGit-specific
+ operations."""
def __init__(self, *args, **kwargs):
git.Repository.__init__(self, *args, **kwargs)
self.__stacks = {} # name -> Stack
@property
- def current_branch(self):
- return utils.strip_leading('refs/heads/', self.head)
- @property
def current_stack(self):
- return self.get_stack(self.current_branch)
- def get_stack(self, name):
+ return self.get_stack()
+ def get_stack(self, name = None):
+ if not name:
+ name = self.current_branch_name
if not name in self.__stacks:
self.__stacks[name] = Stack(self, name)
return self.__stacks[name]