107
107
class ReportCommitToLog(NullCommitReporter):
109
# this may be more useful if 'note' was replaced by an overridable
110
# method on self, which would allow more trivial subclassing.
111
# alternative, a callable could be passed in, allowing really trivial
112
# reuse for some uis. RBC 20060511
109
def _note(self, format, *args):
112
Messages are output by writing directly to stderr instead of
113
using bzrlib.trace.note(). The latter constantly updates the
114
log file as we go causing an unnecessary performance hit.
116
Subclasses may choose to override this method but need to be aware
117
of its potential impact on performance.
119
bzrlib.ui.ui_factory.clear_term()
120
sys.stderr.write((format + "\n") % args)
114
122
def snapshot_change(self, change, path):
115
123
if change == 'unchanged':
117
125
if change == 'added' and path == '':
119
note("%s %s", change, path)
127
self._note("%s %s", change, path)
121
129
def completed(self, revno, rev_id):
122
note('Committed revision %d.', revno)
130
self._note('Committed revision %d.', revno)
124
132
def deleted(self, file_id):
125
note('deleted %s', file_id)
133
self._note('deleted %s', file_id)
127
135
def escaped(self, escape_count, message):
128
note("replaced %d control characters in message", escape_count)
136
self._note("replaced %d control characters in message", escape_count)
130
138
def missing(self, path):
131
note('missing %s', path)
139
self._note('missing %s', path)
133
141
def renamed(self, change, old_path, new_path):
134
note('%s %s => %s', change, old_path, new_path)
142
self._note('%s %s => %s', change, old_path, new_path)
137
145
class Commit(object):
177
182
recursive='down'):
178
183
"""Commit working copy as a new revision.
180
message -- the commit message (it or message_callback is required)
182
timestamp -- if not None, seconds-since-epoch for a
183
postdated/predated commit.
185
specific_files -- If true, commit only those files.
187
rev_id -- If set, use this as the new revision id.
185
:param message: the commit message (it or message_callback is required)
187
:param timestamp: if not None, seconds-since-epoch for a
188
postdated/predated commit.
190
:param specific_files: If true, commit only those files.
192
:param rev_id: If set, use this as the new revision id.
188
193
Useful for test or import commands that need to tightly
189
194
control what revisions are assigned. If you duplicate
190
195
a revision id that exists elsewhere it is your own fault.
191
196
If null (default), a time/random revision id is generated.
193
allow_pointless -- If true (default), commit even if nothing
198
:param allow_pointless: If true (default), commit even if nothing
194
199
has changed and no merges are recorded.
196
strict -- If true, don't allow a commit if the working tree
201
:param strict: If true, don't allow a commit if the working tree
197
202
contains unknown files.
199
revprops -- Properties for new revision
204
:param revprops: Properties for new revision
200
205
:param local: Perform a local only commit.
201
206
:param recursive: If set to 'down', commit in any subtrees that have
202
207
pending changes of any sort during this commit.
249
252
self.basis_tree.lock_read()
251
254
# Cannot commit with conflicts present.
252
if len(self.work_tree.conflicts())>0:
255
if len(self.work_tree.conflicts()) > 0:
253
256
raise ConflictsInTree
255
# setup the bound branch variables as needed.
258
# Setup the bound branch variables as needed.
256
259
self._check_bound_branch()
258
# check for out of date working trees
260
first_tree_parent = self.work_tree.get_parent_ids()[0]
262
# if there are no parents, treat our parent as 'None'
263
# this is so that we still consier the master branch
264
# - in a checkout scenario the tree may have no
265
# parents but the branch may do.
266
first_tree_parent = bzrlib.revision.NULL_REVISION
267
old_revno, master_last = self.master_branch.last_revision_info()
268
if master_last != first_tree_parent:
269
if master_last != bzrlib.revision.NULL_REVISION:
270
raise errors.OutOfDateTree(self.work_tree)
271
if self.branch.repository.has_revision(first_tree_parent):
272
new_revno = old_revno + 1
274
# ghost parents never appear in revision history.
261
# Check that the working tree is up to date
262
old_revno,new_revno = self._check_out_of_date_tree()
277
265
# raise an exception as soon as we find a single unknown.
278
266
for unknown in self.work_tree.unknowns():
291
279
tree.find_ids_across_trees(specific_files,
292
280
[self.basis_tree, self.work_tree])
282
# Setup the progress bar ...
293
283
# one to finish, one for rev and inventory, and one for each
294
284
# inventory entry, and the same for the new inventory.
295
285
# note that this estimate is too long when we do a partial tree
301
291
self._gather_parents()
302
292
if len(self.parents) > 1 and self.specific_files:
303
raise NotImplementedError('selected-file commit of merges is not supported yet: files %r',
293
raise errors.CannotCommitSelectedFileMerge(self.specific_files)
295
# Build the new inventory
306
296
self.builder = self.branch.get_commit_builder(self.parents,
307
297
self.config, timestamp, timezone, committer, revprops, rev_id)
309
298
self._remove_deleted()
310
299
self._populate_new_inv()
311
300
self._report_deletes()
313
301
self._check_pointless()
302
self._emit_progress_update()
315
self._emit_progress_update()
316
304
# TODO: Now the new inventory is known, check for conflicts and
317
305
# prompt the user for a commit message.
318
306
# ADHB 2006-08-08: If this is done, populate_new_inv should not add
325
313
self.message = message
326
314
self._escape_commit_message()
316
# Add revision data to the local branch
328
317
self.rev_id = self.builder.commit(self.message)
329
318
self._emit_progress_update()
330
# revision data is in the local branch now.
332
320
# upload revision data to the master.
333
321
# this will propagate merged revisions too if needed.
335
323
self.master_branch.repository.fetch(self.branch.repository,
336
324
revision_id=self.rev_id)
337
325
# now the master has the revision data
338
# 'commit' to the master first so a timeout here causes the local
339
# branch to be out of date
326
# 'commit' to the master first so a timeout here causes the
327
# local branch to be out of date
340
328
self.master_branch.set_last_revision_info(new_revno,
343
331
# and now do the commit locally.
344
332
self.branch.set_last_revision_info(new_revno, self.rev_id)
334
# Make the working tree up to date with the branch
346
335
rev_tree = self.builder.revision_tree()
347
336
self.work_tree.set_parent_trees([(self.rev_id, rev_tree)])
348
# now the work tree is up to date with the branch
350
337
self.reporter.completed(new_revno, self.rev_id)
351
# old style commit hooks - should be deprecated ? (obsoleted in
353
if self.config.post_commit() is not None:
354
hooks = self.config.post_commit().split(' ')
355
# this would be nicer with twisted.python.reflect.namedAny
357
result = eval(hook + '(branch, rev_id)',
358
{'branch':self.branch,
360
'rev_id':self.rev_id})
361
# new style commit hooks:
362
if not self.bound_branch:
363
hook_master = self.branch
366
hook_master = self.master_branch
367
hook_local = self.branch
368
# With bound branches, when the master is behind the local branch,
369
# the 'old_revno' and old_revid values here are incorrect.
370
# XXX: FIXME ^. RBC 20060206
372
old_revid = self.parents[0]
374
old_revid = bzrlib.revision.NULL_REVISION
375
for hook in Branch.hooks['post_commit']:
376
hook(hook_local, hook_master, old_revno, old_revid, new_revno,
339
# Process the post commit hooks, if any
340
self._process_hooks(old_revno, new_revno)
378
341
self._emit_progress_update()
477
440
self.master_branch.lock_write()
478
441
self.master_locked = True
443
def _check_out_of_date_tree(self):
444
"""Check that the working tree is up to date.
446
:return: old_revision_number,new_revision_number tuple
449
first_tree_parent = self.work_tree.get_parent_ids()[0]
451
# if there are no parents, treat our parent as 'None'
452
# this is so that we still consider the master branch
453
# - in a checkout scenario the tree may have no
454
# parents but the branch may do.
455
first_tree_parent = bzrlib.revision.NULL_REVISION
456
old_revno, master_last = self.master_branch.last_revision_info()
457
if master_last != first_tree_parent:
458
if master_last != bzrlib.revision.NULL_REVISION:
459
raise errors.OutOfDateTree(self.work_tree)
460
if self.branch.repository.has_revision(first_tree_parent):
461
new_revno = old_revno + 1
463
# ghost parents never appear in revision history.
465
return old_revno,new_revno
467
def _process_hooks(self, old_revno, new_revno):
468
"""Process any registered commit hooks."""
469
# old style commit hooks - should be deprecated ? (obsoleted in
471
if self.config.post_commit() is not None:
472
hooks = self.config.post_commit().split(' ')
473
# this would be nicer with twisted.python.reflect.namedAny
475
result = eval(hook + '(branch, rev_id)',
476
{'branch':self.branch,
478
'rev_id':self.rev_id})
479
# new style commit hooks:
480
if not self.bound_branch:
481
hook_master = self.branch
484
hook_master = self.master_branch
485
hook_local = self.branch
486
# With bound branches, when the master is behind the local branch,
487
# the 'old_revno' and old_revid values here are incorrect.
488
# XXX: FIXME ^. RBC 20060206
490
old_revid = self.parents[0]
492
old_revid = bzrlib.revision.NULL_REVISION
493
for hook in Branch.hooks['post_commit']:
494
hook(hook_local, hook_master, old_revno, old_revid, new_revno,
480
497
def _cleanup(self):
481
498
"""Cleanup any open locks, progress bars etc."""
482
499
cleanups = [self._cleanup_bound_branch,