chiark / gitweb /
Create a git.Branch class as ancestor of stack.Stack
[stgit] / stgit / lib / stack.py
CommitLineData
652b2e67
KH
1"""A Python class hierarchy wrapping the StGit on-disk metadata."""
2
cbe4567e
KH
3import os.path
4from stgit import exception, utils
f5c820a8 5from stgit.lib import git, stackupgrade
cbe4567e
KH
6
7class Patch(object):
652b2e67
KH
8 """Represents an StGit patch. This class is mainly concerned with
9 reading and writing the on-disk representation of a patch."""
cbe4567e
KH
10 def __init__(self, stack, name):
11 self.__stack = stack
12 self.__name = name
13 name = property(lambda self: self.__name)
5a8e991e 14 @property
cbe4567e
KH
15 def __ref(self):
16 return 'refs/patches/%s/%s' % (self.__stack.name, self.__name)
17 @property
5a8e991e
KH
18 def __log_ref(self):
19 return self.__ref + '.log'
20 @property
cbe4567e 21 def commit(self):
5a8e991e
KH
22 return self.__stack.repository.refs.get(self.__ref)
23 @property
24 def __compat_dir(self):
25 return os.path.join(self.__stack.directory, 'patches', self.__name)
26 def __write_compat_files(self, new_commit, msg):
27 """Write files used by the old infrastructure."""
28 def write(name, val, multiline = False):
29 fn = os.path.join(self.__compat_dir, name)
30 if val:
31 utils.write_string(fn, val, multiline)
32 elif os.path.isfile(fn):
33 os.remove(fn)
34 def write_patchlog():
35 try:
36 old_log = [self.__stack.repository.refs.get(self.__log_ref)]
37 except KeyError:
38 old_log = []
f5f22afe 39 cd = git.CommitData(tree = new_commit.data.tree, parents = old_log,
5a8e991e
KH
40 message = '%s\t%s' % (msg, new_commit.sha1))
41 c = self.__stack.repository.commit(cd)
42 self.__stack.repository.refs.set(self.__log_ref, c, msg)
43 return c
44 d = new_commit.data
45 write('authname', d.author.name)
46 write('authemail', d.author.email)
47 write('authdate', d.author.date)
48 write('commname', d.committer.name)
49 write('commemail', d.committer.email)
50 write('description', d.message)
51 write('log', write_patchlog().sha1)
52 write('top', new_commit.sha1)
53 write('bottom', d.parent.sha1)
54 try:
55 old_top_sha1 = self.commit.sha1
56 old_bottom_sha1 = self.commit.data.parent.sha1
57 except KeyError:
58 old_top_sha1 = None
59 old_bottom_sha1 = None
60 write('top.old', old_top_sha1)
61 write('bottom.old', old_bottom_sha1)
62 def __delete_compat_files(self):
63 if os.path.isdir(self.__compat_dir):
64 for f in os.listdir(self.__compat_dir):
65 os.remove(os.path.join(self.__compat_dir, f))
66 os.rmdir(self.__compat_dir)
67 self.__stack.repository.refs.delete(self.__log_ref)
cbe4567e 68 def set_commit(self, commit, msg):
5a8e991e
KH
69 self.__write_compat_files(commit, msg)
70 self.__stack.repository.refs.set(self.__ref, commit, msg)
cbe4567e 71 def delete(self):
5a8e991e
KH
72 self.__delete_compat_files()
73 self.__stack.repository.refs.delete(self.__ref)
cbe4567e
KH
74 def is_applied(self):
75 return self.name in self.__stack.patchorder.applied
76 def is_empty(self):
dcd32afa 77 return self.commit.data.is_nochange()
cbe4567e
KH
78
79class PatchOrder(object):
80 """Keeps track of patch order, and which patches are applied.
81 Works with patch names, not actual patches."""
cbe4567e
KH
82 def __init__(self, stack):
83 self.__stack = stack
84 self.__lists = {}
85 def __read_file(self, fn):
86 return tuple(utils.read_strings(
87 os.path.join(self.__stack.directory, fn)))
88 def __write_file(self, fn, val):
89 utils.write_strings(os.path.join(self.__stack.directory, fn), val)
90 def __get_list(self, name):
91 if not name in self.__lists:
92 self.__lists[name] = self.__read_file(name)
93 return self.__lists[name]
94 def __set_list(self, name, val):
95 val = tuple(val)
96 if val != self.__lists.get(name, None):
97 self.__lists[name] = val
98 self.__write_file(name, val)
99 applied = property(lambda self: self.__get_list('applied'),
100 lambda self, val: self.__set_list('applied', val))
101 unapplied = property(lambda self: self.__get_list('unapplied'),
102 lambda self, val: self.__set_list('unapplied', val))
448e5d9d
CM
103 hidden = property(lambda self: self.__get_list('hidden'),
104 lambda self, val: self.__set_list('hidden', val))
d454cc06
CM
105 all = property(lambda self: self.applied + self.unapplied + self.hidden)
106 all_visible = property(lambda self: self.applied + self.unapplied)
cbe4567e
KH
107
108class Patches(object):
652b2e67
KH
109 """Creates L{Patch} objects. Makes sure there is only one such object
110 per patch."""
cbe4567e
KH
111 def __init__(self, stack):
112 self.__stack = stack
113 def create_patch(name):
114 p = Patch(self.__stack, name)
115 p.commit # raise exception if the patch doesn't exist
116 return p
117 self.__patches = git.ObjectCache(create_patch) # name -> Patch
118 def exists(self, name):
119 try:
120 self.get(name)
121 return True
122 except KeyError:
123 return False
124 def get(self, name):
125 return self.__patches[name]
126 def new(self, name, commit, msg):
127 assert not name in self.__patches
128 p = Patch(self.__stack, name)
129 p.set_commit(commit, msg)
130 self.__patches[name] = p
131 return p
132
04c77bc5 133class Stack(git.Branch):
652b2e67
KH
134 """Represents an StGit stack (that is, a git branch with some extra
135 metadata)."""
04c77bc5
CM
136 __repo_subdir = 'patches'
137
cbe4567e 138 def __init__(self, repository, name):
04c77bc5 139 git.Branch.__init__(self, repository, name)
cbe4567e
KH
140 self.__patchorder = PatchOrder(self)
141 self.__patches = Patches(self)
317b386f
KH
142 if not stackupgrade.update_to_current_format_version(repository, name):
143 raise exception.StgException('%s: branch not initialized' % name)
cbe4567e
KH
144 patchorder = property(lambda self: self.__patchorder)
145 patches = property(lambda self: self.__patches)
146 @property
147 def directory(self):
04c77bc5 148 return os.path.join(self.repository.directory, self.__repo_subdir, self.name)
cbe4567e
KH
149 @property
150 def base(self):
151 if self.patchorder.applied:
152 return self.patches.get(self.patchorder.applied[0]
153 ).commit.data.parent
154 else:
155 return self.head
dcd32afa
KH
156 def head_top_equal(self):
157 if not self.patchorder.applied:
158 return True
159 return self.head == self.patches.get(self.patchorder.applied[-1]).commit
cbe4567e
KH
160
161class Repository(git.Repository):
652b2e67
KH
162 """A git L{Repository<git.Repository>} with some added StGit-specific
163 operations."""
cbe4567e
KH
164 def __init__(self, *args, **kwargs):
165 git.Repository.__init__(self, *args, **kwargs)
166 self.__stacks = {} # name -> Stack
167 @property
cbe4567e 168 def current_stack(self):
13b26f1a
CM
169 return self.get_stack()
170 def get_stack(self, name = None):
171 if not name:
04c77bc5 172 name = self.current_branch_name
cbe4567e
KH
173 if not name in self.__stacks:
174 self.__stacks[name] = Stack(self, name)
175 return self.__stacks[name]