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.
248
252
self.basis_tree.lock_read()
250
254
# Cannot commit with conflicts present.
251
if len(self.work_tree.conflicts())>0:
255
if len(self.work_tree.conflicts()) > 0:
252
256
raise ConflictsInTree
254
# setup the bound branch variables as needed.
258
# Setup the bound branch variables as needed.
255
259
self._check_bound_branch()
257
# check for out of date working trees
259
first_tree_parent = self.work_tree.get_parent_ids()[0]
261
# if there are no parents, treat our parent as 'None'
262
# this is so that we still consier the master branch
263
# - in a checkout scenario the tree may have no
264
# parents but the branch may do.
265
first_tree_parent = bzrlib.revision.NULL_REVISION
266
old_revno, master_last = self.master_branch.last_revision_info()
267
if master_last != first_tree_parent:
268
if master_last != bzrlib.revision.NULL_REVISION:
269
raise errors.OutOfDateTree(self.work_tree)
270
if self.branch.repository.has_revision(first_tree_parent):
271
new_revno = old_revno + 1
273
# 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()
276
265
# raise an exception as soon as we find a single unknown.
277
266
for unknown in self.work_tree.unknowns():
290
279
tree.find_ids_across_trees(specific_files,
291
280
[self.basis_tree, self.work_tree])
282
# Setup the progress bar ...
292
283
# one to finish, one for rev and inventory, and one for each
293
284
# inventory entry, and the same for the new inventory.
294
285
# note that this estimate is too long when we do a partial tree
300
291
self._gather_parents()
301
292
if len(self.parents) > 1 and self.specific_files:
302
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
305
296
self.builder = self.branch.get_commit_builder(self.parents,
306
297
self.config, timestamp, timezone, committer, revprops, rev_id)
308
298
self._remove_deleted()
309
299
self._populate_new_inv()
310
300
self._report_deletes()
312
301
self._check_pointless()
302
self._emit_progress_update()
314
self._emit_progress_update()
315
304
# TODO: Now the new inventory is known, check for conflicts and
316
305
# prompt the user for a commit message.
317
306
# ADHB 2006-08-08: If this is done, populate_new_inv should not add
324
313
self.message = message
325
314
self._escape_commit_message()
316
# Add revision data to the local branch
327
317
self.rev_id = self.builder.commit(self.message)
328
318
self._emit_progress_update()
329
# revision data is in the local branch now.
331
320
# upload revision data to the master.
332
321
# this will propagate merged revisions too if needed.
334
323
self.master_branch.repository.fetch(self.branch.repository,
335
324
revision_id=self.rev_id)
336
325
# now the master has the revision data
337
# 'commit' to the master first so a timeout here causes the local
338
# 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
339
328
self.master_branch.set_last_revision_info(new_revno,
342
331
# and now do the commit locally.
343
332
self.branch.set_last_revision_info(new_revno, self.rev_id)
334
# Make the working tree up to date with the branch
345
335
rev_tree = self.builder.revision_tree()
346
336
self.work_tree.set_parent_trees([(self.rev_id, rev_tree)])
347
# now the work tree is up to date with the branch
349
337
self.reporter.completed(new_revno, self.rev_id)
350
# old style commit hooks - should be deprecated ? (obsoleted in
352
if self.config.post_commit() is not None:
353
hooks = self.config.post_commit().split(' ')
354
# this would be nicer with twisted.python.reflect.namedAny
356
result = eval(hook + '(branch, rev_id)',
357
{'branch':self.branch,
359
'rev_id':self.rev_id})
360
# new style commit hooks:
361
if not self.bound_branch:
362
hook_master = self.branch
365
hook_master = self.master_branch
366
hook_local = self.branch
367
# With bound branches, when the master is behind the local branch,
368
# the 'old_revno' and old_revid values here are incorrect.
369
# XXX: FIXME ^. RBC 20060206
371
old_revid = self.parents[0]
373
old_revid = bzrlib.revision.NULL_REVISION
374
for hook in Branch.hooks['post_commit']:
375
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)
377
341
self._emit_progress_update()
476
440
self.master_branch.lock_write()
477
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,
479
497
def _cleanup(self):
480
498
"""Cleanup any open locks, progress bars etc."""
481
499
cleanups = [self._cleanup_bound_branch,