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
with self.lock_tree_write():
45
return unbound(self, *args, **kwargs)
46
tree_write_locked.__doc__ = unbound.__doc__
47
tree_write_locked.__name__ = unbound.__name__
48
return tree_write_locked
51
38
class MutableTree(tree.Tree):
52
39
"""A MutableTree is a specialisation of Tree which is able to be mutated.
128
114
kinds = [None] * len(files)
129
115
elif not len(kinds) == len(files):
130
116
raise AssertionError()
132
# generic constraint checks:
133
if self.is_control_filename(f):
134
raise errors.ForbiddenControlFileError(filename=f)
135
fp = osutils.splitpath(f)
136
# fill out file kinds for all files [not needed when we stop
137
# caring about the instantaneous file kind within a uncommmitted tree
139
self._gather_kinds(files, kinds)
140
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)
142
129
def add_reference(self, sub_tree):
143
130
"""Add a TreeReference to the tree, pointing at sub_tree"""
180
167
raise NotImplementedError(self.apply_inventory_delta)
183
169
def commit(self, message=None, revprops=None, *args, **kwargs):
184
170
# avoid circular imports
185
171
from breezy import commit
186
172
possible_master_transports=[]
187
revprops = commit.Commit.update_revprops(
190
kwargs.pop('authors', None),
191
kwargs.get('local', False),
192
possible_master_transports)
193
# args for wt.commit start at message from the Commit.commit method,
194
args = (message, ) + args
195
for hook in MutableTree.hooks['start_commit']:
197
committed_id = commit.Commit().commit(working_tree=self,
199
possible_master_transports=possible_master_transports,
201
post_hook_params = PostCommitHookParams(self)
202
for hook in MutableTree.hooks['post_commit']:
203
hook(post_hook_params)
173
with self.lock_write():
174
revprops = commit.Commit.update_revprops(
177
kwargs.pop('authors', None),
178
kwargs.get('local', False),
179
possible_master_transports)
180
# args for wt.commit start at message from the Commit.commit method,
181
args = (message, ) + args
182
for hook in MutableTree.hooks['start_commit']:
184
committed_id = commit.Commit().commit(working_tree=self,
186
possible_master_transports=possible_master_transports,
188
post_hook_params = PostCommitHookParams(self)
189
for hook in MutableTree.hooks['post_commit']:
190
hook(post_hook_params)
206
193
def _gather_kinds(self, files, kinds):
207
194
"""Helper function for add - sets the entries of kinds."""
208
195
raise NotImplementedError(self._gather_kinds)
211
197
def has_changes(self, _from_tree=None):
212
198
"""Quickly check that the tree contains at least one commitable change.
217
203
:return: True if a change is found. False otherwise
219
# Check pending merges
220
if len(self.get_parent_ids()) > 1:
222
if _from_tree is None:
223
_from_tree = self.basis_tree()
224
changes = self.iter_changes(_from_tree)
226
change = next(changes)
227
# Exclude root (talk about black magic... --vila 20090629)
228
if change[4] == (None, None):
205
with self.lock_read():
206
# Check pending merges
207
if len(self.get_parent_ids()) > 1:
209
if _from_tree is None:
210
_from_tree = self.basis_tree()
211
changes = self.iter_changes(_from_tree)
229
213
change = next(changes)
231
except StopIteration:
214
# Exclude root (talk about black magic... --vila 20090629)
215
if change[4] == (None, None):
216
change = next(changes)
218
except StopIteration:
236
222
def check_changed_or_out_of_date(self, strict, opt_name,
237
223
more_error, more_warning):
238
224
"""Check the tree for uncommitted changes and branch synchronization.
250
236
:param more_warning: Details about what is happening.
253
strict = self.branch.get_config_stack().get(opt_name)
254
if strict is not False:
256
if (self.has_changes()):
257
err_class = errors.UncommittedChanges
258
elif self.last_revision() != self.branch.last_revision():
259
# The tree has lost sync with its branch, there is little
260
# chance that the user is aware of it but he can still force
261
# the action with --no-strict
262
err_class = errors.OutOfDateTree
263
if err_class is not None:
265
err = err_class(self, more=more_warning)
266
# We don't want to interrupt the user if he expressed no
267
# preference about strict.
268
trace.warning('%s', err._format())
270
err = err_class(self, more=more_error)
238
with self.lock_read():
240
strict = self.branch.get_config_stack().get(opt_name)
241
if strict is not False:
243
if (self.has_changes()):
244
err_class = errors.UncommittedChanges
245
elif self.last_revision() != self.branch.last_revision():
246
# The tree has lost sync with its branch, there is little
247
# chance that the user is aware of it but he can still
248
# force the action with --no-strict
249
err_class = errors.OutOfDateTree
250
if err_class is not None:
252
err = err_class(self, more=more_warning)
253
# We don't want to interrupt the user if he expressed
254
# no preference about strict.
255
trace.warning('%s', err._format())
257
err = err_class(self, more=more_error)
274
260
def last_revision(self):
275
261
"""Return the revision id of the last commit performed in this tree.