1
# Copyright (C) 2006-2010 Canonical Ltd
1
# Copyright (C) 2006, 2007 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
28
28
from bzrlib import (
34
from bzrlib.osutils import dirname
35
from bzrlib.revisiontree import RevisionTree
36
from bzrlib.trace import mutter, warning
42
44
from bzrlib.decorators import needs_read_lock, needs_write_lock
45
from bzrlib.osutils import splitpath
45
48
def needs_tree_write_lock(unbound):
127
130
# generic constraint checks:
128
131
if self.is_control_filename(f):
129
132
raise errors.ForbiddenControlFileError(filename=f)
130
fp = osutils.splitpath(f)
131
134
# fill out file kinds for all files [not needed when we stop
132
135
# caring about the instantaneous file kind within a uncommmitted tree
184
187
# avoid circular imports
185
188
from bzrlib import commit
186
191
possible_master_transports=[]
187
revprops = commit.Commit.update_revprops(
190
kwargs.pop('authors', None),
191
kwargs.pop('author', None),
192
if not 'branch-nick' in revprops:
193
revprops['branch-nick'] = self.branch._get_nick(
192
194
kwargs.get('local', False),
193
195
possible_master_transports)
196
authors = kwargs.pop('authors', None)
197
author = kwargs.pop('author', None)
198
if authors is not None:
199
if author is not None:
200
raise AssertionError('Specifying both author and authors '
201
'is not allowed. Specify just authors instead')
202
if 'author' in revprops or 'authors' in revprops:
203
# XXX: maybe we should just accept one of them?
204
raise AssertionError('author property given twice')
206
for individual in authors:
207
if '\n' in individual:
208
raise AssertionError('\\n is not a valid character '
209
'in an author identity')
210
revprops['authors'] = '\n'.join(authors)
211
if author is not None:
212
symbol_versioning.warn('The parameter author was deprecated'
213
' in version 1.13. Use authors instead',
215
if 'author' in revprops or 'authors' in revprops:
216
# XXX: maybe we should just accept one of them?
217
raise AssertionError('author property given twice')
219
raise AssertionError('\\n is not a valid character '
220
'in an author identity')
221
revprops['authors'] = author
194
222
# args for wt.commit start at message from the Commit.commit method,
195
223
args = (message, ) + args
196
224
for hook in MutableTree.hooks['start_commit']:
209
237
raise NotImplementedError(self._gather_kinds)
212
def has_changes(self, _from_tree=None):
213
"""Quickly check that the tree contains at least one commitable change.
215
:param _from_tree: tree to compare against to find changes (default to
216
the basis tree and is intended to be used by tests).
240
def has_changes(self, from_tree):
241
"""Quickly check that the tree contains at least one change.
218
243
:return: True if a change is found. False otherwise
220
# Check pending merges
221
if len(self.get_parent_ids()) > 1:
223
if _from_tree is None:
224
_from_tree = self.basis_tree()
225
changes = self.iter_changes(_from_tree)
245
changes = self.iter_changes(from_tree)
227
247
change = changes.next()
228
248
# Exclude root (talk about black magic... --vila 20090629)
237
def check_changed_or_out_of_date(self, strict, opt_name,
238
more_error, more_warning):
239
"""Check the tree for uncommitted changes and branch synchronization.
241
If strict is None and not set in the config files, a warning is issued.
242
If strict is True, an error is raised.
243
If strict is False, no checks are done and no warning is issued.
245
:param strict: True, False or None, searched in branch config if None.
247
:param opt_name: strict option name to search in config file.
249
:param more_error: Details about how to avoid the check.
251
:param more_warning: Details about what is happening.
254
strict = self.branch.get_config().get_user_option_as_bool(opt_name)
255
if strict is not False:
257
if (self.has_changes()):
258
err_class = errors.UncommittedChanges
259
elif self.last_revision() != self.branch.last_revision():
260
# The tree has lost sync with its branch, there is little
261
# chance that the user is aware of it but he can still force
262
# the action with --no-strict
263
err_class = errors.OutOfDateTree
264
if err_class is not None:
266
err = err_class(self, more=more_warning)
267
# We don't want to interrupt the user if he expressed no
268
# preference about strict.
269
trace.warning('%s', err._format())
271
err = err_class(self, more=more_error)
275
257
def last_revision(self):
276
258
"""Return the revision id of the last commit performed in this tree.
376
358
This is designed more towards DWIM for humans than API clarity.
377
359
For the specific behaviour see the help for cmd_add().
379
:param file_list: List of zero or more paths. *NB: these are
380
interpreted relative to the process cwd, not relative to the
381
tree.* (Add and most other tree methods use tree-relative
383
361
:param action: A reporter to be called with the inventory, parent_ie,
384
362
path and kind of the path being added. It may return a file_id if
385
363
a specific one should be used.
409
385
user_dirs = set()
410
conflicts_related = set()
411
# Not all mutable trees can have conflicts
412
if getattr(self, 'conflicts', None) is not None:
413
# Collect all related files without checking whether they exist or
414
# are versioned. It's cheaper to do that once for all conflicts
415
# than trying to find the relevant conflict for each added file.
416
for c in self.conflicts():
417
conflicts_related.update(c.associated_filenames())
419
# expand any symlinks in the directory part, while leaving the
421
file_list = map(osutils.normalizepath, file_list)
423
387
# validate user file paths and convert all paths to tree
424
388
# relative : it's cheaper to make a tree relative path an abspath
426
390
# perform the canonicalization in bulk.
427
391
for filepath in osutils.canonical_relpaths(self.basedir, file_list):
428
392
rf = _FastPath(filepath)
429
# validate user parameters. Our recursive code avoids adding new
430
# files that need such validation
393
# validate user parameters. Our recursive code avoids adding new files
394
# that need such validation
431
395
if self.is_control_filename(rf.raw_path):
432
396
raise errors.ForbiddenControlFileError(filename=rf.raw_path)
440
404
if not InventoryEntry.versionable_kind(kind):
441
405
raise errors.BadFileKindError(filename=abspath, kind=kind)
442
# ensure the named path is added, so that ignore rules in the later
443
# directory walk dont skip it.
444
# we dont have a parent ie known yet.: use the relatively slower
445
# inventory probing method
406
# ensure the named path is added, so that ignore rules in the later directory
408
# we dont have a parent ie known yet.: use the relatively slower inventory
446
410
versioned = inv.has_filename(rf.raw_path)
479
443
kind = osutils.file_kind(abspath)
481
445
if not InventoryEntry.versionable_kind(kind):
482
trace.warning("skipping %s (can't add file of kind '%s')",
446
warning("skipping %s (can't add file of kind '%s')", abspath, kind)
485
448
if illegalpath_re.search(directory.raw_path):
486
trace.warning("skipping %r (contains \\n or \\r)" % abspath)
488
if directory.raw_path in conflicts_related:
489
# If the file looks like one generated for a conflict, don't
492
'skipping %s (generated to help resolve conflicts)',
449
warning("skipping %r (contains \\n or \\r)" % abspath)
496
452
if parent_ie is not None:
520
476
# mutter("%r is already versioned", abspath)
522
# XXX: This is wrong; people *might* reasonably be trying to
523
# add subtrees as subtrees. This should probably only be done
524
# in formats which can represent subtrees, and even then
525
# perhaps only when the user asked to add subtrees. At the
526
# moment you can add them specially through 'join --reference',
527
# which is perhaps reasonable: adding a new reference is a
528
# special operation and can have a special behaviour. mbp
530
trace.mutter("%r is a nested bzr tree", abspath)
478
# XXX: This is wrong; people *might* reasonably be trying to add
479
# subtrees as subtrees. This should probably only be done in formats
480
# which can represent subtrees, and even then perhaps only when
481
# the user asked to add subtrees. At the moment you can add them
482
# specially through 'join --reference', which is perhaps
483
# reasonable: adding a new reference is a special operation and
484
# can have a special behaviour. mbp 20070306
485
mutter("%r is a nested bzr tree", abspath)
532
487
_add_one(self, inv, parent_ie, directory, kind, action)
533
488
added.append(directory.raw_path)
555
510
# faster - its impossible for a non root dir to have a
557
512
if self.is_control_filename(subp):
558
trace.mutter("skip control directory %r", subp)
513
mutter("skip control directory %r", subp)
559
514
elif subf in this_ie.children:
560
515
# recurse into this already versioned subdir.
561
516
dirs_to_add.append((_FastPath(subp, subf), this_ie))
617
572
inventory = basis.inventory._get_mutable_inventory()
619
574
inventory.apply_delta(delta)
620
rev_tree = revisiontree.RevisionTree(self.branch.repository,
621
inventory, new_revid)
575
rev_tree = RevisionTree(self.branch.repository, inventory, new_revid)
622
576
self.set_parent_trees([(new_revid, rev_tree)])
707
661
# there are a limited number of dirs we can be nested under, it should
708
662
# generally find it very fast and not recurse after that.
709
663
added = _add_one_and_parent(tree, inv, None,
710
_FastPath(osutils.dirname(path.raw_path)), 'directory', action)
711
parent_id = inv.path2id(osutils.dirname(path.raw_path))
664
_FastPath(dirname(path.raw_path)), 'directory', action)
665
parent_id = inv.path2id(dirname(path.raw_path))
712
666
parent_ie = inv[parent_id]
713
667
_add_one(tree, inv, parent_ie, path, kind, action)
714
668
return added + [path.raw_path]
724
678
file_id or None to generate a new file id
727
# if the parent exists, but isn't a directory, we have to do the
728
# kind change now -- really the inventory shouldn't pretend to know
729
# the kind of wt files, but it does.
730
if parent_ie.kind != 'directory':
731
# nb: this relies on someone else checking that the path we're using
732
# doesn't contain symlinks.
733
new_parent_ie = inventory.make_entry('directory', parent_ie.name,
734
parent_ie.parent_id, parent_ie.file_id)
735
del inv[parent_ie.file_id]
736
inv.add(new_parent_ie)
737
parent_ie = new_parent_ie
738
681
file_id = file_id_callback(inv, parent_ie, path, kind)
739
682
entry = inv.make_entry(kind, path.base_path, parent_ie.file_id,