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