34
from .decorators import needs_read_lock, needs_write_lock
35
32
from .sixish import (
41
def needs_tree_write_lock(unbound):
42
"""Decorate unbound to take out and release a tree_write lock."""
43
def tree_write_locked(self, *args, **kwargs):
44
self.lock_tree_write()
46
return unbound(self, *args, **kwargs)
49
tree_write_locked.__doc__ = unbound.__doc__
50
tree_write_locked.__name__ = unbound.__name__
51
return tree_write_locked
54
38
class MutableTree(tree.Tree):
55
39
"""A MutableTree is a specialisation of Tree which is able to be mutated.
131
114
kinds = [None] * len(files)
132
115
elif not len(kinds) == len(files):
133
116
raise AssertionError()
135
# generic constraint checks:
136
if self.is_control_filename(f):
137
raise errors.ForbiddenControlFileError(filename=f)
138
fp = osutils.splitpath(f)
139
# fill out file kinds for all files [not needed when we stop
140
# caring about the instantaneous file kind within a uncommmitted tree
142
self._gather_kinds(files, kinds)
143
self._add(files, ids, kinds)
117
with self.lock_tree_write():
119
# generic constraint checks:
120
if self.is_control_filename(f):
121
raise errors.ForbiddenControlFileError(filename=f)
122
fp = osutils.splitpath(f)
123
# fill out file kinds for all files [not needed when we stop
124
# caring about the instantaneous file kind within a uncommmitted tree
126
self._gather_kinds(files, kinds)
127
self._add(files, ids, kinds)
145
129
def add_reference(self, sub_tree):
146
130
"""Add a TreeReference to the tree, pointing at sub_tree"""
173
157
raise NotImplementedError(self._add)
176
159
def commit(self, message=None, revprops=None, *args, **kwargs):
177
160
# avoid circular imports
178
161
from breezy import commit
179
162
possible_master_transports=[]
180
revprops = commit.Commit.update_revprops(
183
kwargs.pop('authors', None),
184
kwargs.get('local', False),
185
possible_master_transports)
186
# args for wt.commit start at message from the Commit.commit method,
187
args = (message, ) + args
188
for hook in MutableTree.hooks['start_commit']:
190
committed_id = commit.Commit().commit(working_tree=self,
192
possible_master_transports=possible_master_transports,
194
post_hook_params = PostCommitHookParams(self)
195
for hook in MutableTree.hooks['post_commit']:
196
hook(post_hook_params)
163
with self.lock_write():
164
revprops = commit.Commit.update_revprops(
167
kwargs.pop('authors', None),
168
kwargs.get('local', False),
169
possible_master_transports)
170
# args for wt.commit start at message from the Commit.commit method,
171
args = (message, ) + args
172
for hook in MutableTree.hooks['start_commit']:
174
committed_id = commit.Commit().commit(working_tree=self,
176
possible_master_transports=possible_master_transports,
178
post_hook_params = PostCommitHookParams(self)
179
for hook in MutableTree.hooks['post_commit']:
180
hook(post_hook_params)
199
183
def _gather_kinds(self, files, kinds):
200
184
"""Helper function for add - sets the entries of kinds."""
201
185
raise NotImplementedError(self._gather_kinds)
204
187
def has_changes(self, _from_tree=None):
205
188
"""Quickly check that the tree contains at least one commitable change.
210
193
:return: True if a change is found. False otherwise
212
# Check pending merges
213
if len(self.get_parent_ids()) > 1:
215
if _from_tree is None:
216
_from_tree = self.basis_tree()
217
changes = self.iter_changes(_from_tree)
219
change = next(changes)
220
# Exclude root (talk about black magic... --vila 20090629)
221
if change[4] == (None, None):
195
with self.lock_read():
196
# Check pending merges
197
if len(self.get_parent_ids()) > 1:
199
if _from_tree is None:
200
_from_tree = self.basis_tree()
201
changes = self.iter_changes(_from_tree)
222
203
change = next(changes)
224
except StopIteration:
204
# Exclude root (talk about black magic... --vila 20090629)
205
if change[4] == (None, None):
206
change = next(changes)
208
except StopIteration:
229
212
def check_changed_or_out_of_date(self, strict, opt_name,
230
213
more_error, more_warning):
231
214
"""Check the tree for uncommitted changes and branch synchronization.
243
226
:param more_warning: Details about what is happening.
246
strict = self.branch.get_config_stack().get(opt_name)
247
if strict is not False:
249
if (self.has_changes()):
250
err_class = errors.UncommittedChanges
251
elif self.last_revision() != self.branch.last_revision():
252
# The tree has lost sync with its branch, there is little
253
# chance that the user is aware of it but he can still force
254
# the action with --no-strict
255
err_class = errors.OutOfDateTree
256
if err_class is not None:
258
err = err_class(self, more=more_warning)
259
# We don't want to interrupt the user if he expressed no
260
# preference about strict.
261
trace.warning('%s', err._format())
263
err = err_class(self, more=more_error)
228
with self.lock_read():
230
strict = self.branch.get_config_stack().get(opt_name)
231
if strict is not False:
233
if (self.has_changes()):
234
err_class = errors.UncommittedChanges
235
elif self.last_revision() != self.branch.last_revision():
236
# The tree has lost sync with its branch, there is little
237
# chance that the user is aware of it but he can still
238
# force the action with --no-strict
239
err_class = errors.OutOfDateTree
240
if err_class is not None:
242
err = err_class(self, more=more_warning)
243
# We don't want to interrupt the user if he expressed
244
# no preference about strict.
245
trace.warning('%s', err._format())
247
err = err_class(self, more=more_error)
267
250
def last_revision(self):
268
251
"""Return the revision id of the last commit performed in this tree.