107
107
class ReportCommitToLog(NullCommitReporter):
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)
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
122
114
def snapshot_change(self, change, path):
123
115
if change == 'unchanged':
125
117
if change == 'added' and path == '':
127
self._note("%s %s", change, path)
119
note("%s %s", change, path)
129
121
def completed(self, revno, rev_id):
130
self._note('Committed revision %d.', revno)
122
note('Committed revision %d.', revno)
132
124
def deleted(self, file_id):
133
self._note('deleted %s', file_id)
125
note('deleted %s', file_id)
135
127
def escaped(self, escape_count, message):
136
self._note("replaced %d control characters in message", escape_count)
128
note("replaced %d control characters in message", escape_count)
138
130
def missing(self, path):
139
self._note('missing %s', path)
131
note('missing %s', path)
141
133
def renamed(self, change, old_path, new_path):
142
self._note('%s %s => %s', change, old_path, new_path)
134
note('%s %s => %s', change, old_path, new_path)
145
137
class Commit(object):
182
177
recursive='down'):
183
178
"""Commit working copy as a new revision.
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.
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.
193
188
Useful for test or import commands that need to tightly
194
189
control what revisions are assigned. If you duplicate
195
190
a revision id that exists elsewhere it is your own fault.
196
191
If null (default), a time/random revision id is generated.
198
:param allow_pointless: If true (default), commit even if nothing
193
allow_pointless -- If true (default), commit even if nothing
199
194
has changed and no merges are recorded.
201
:param strict: If true, don't allow a commit if the working tree
196
strict -- If true, don't allow a commit if the working tree
202
197
contains unknown files.
204
:param revprops: Properties for new revision
199
revprops -- Properties for new revision
205
200
:param local: Perform a local only commit.
206
201
:param recursive: If set to 'down', commit in any subtrees that have
207
202
pending changes of any sort during this commit.
252
248
self.basis_tree.lock_read()
254
250
# Cannot commit with conflicts present.
255
if len(self.work_tree.conflicts()) > 0:
251
if len(self.work_tree.conflicts())>0:
256
252
raise ConflictsInTree
258
# Setup the bound branch variables as needed.
254
# setup the bound branch variables as needed.
259
255
self._check_bound_branch()
261
# Check that the working tree is up to date
262
old_revno,new_revno = self._check_out_of_date_tree()
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.
265
276
# raise an exception as soon as we find a single unknown.
266
277
for unknown in self.work_tree.unknowns():
279
290
tree.find_ids_across_trees(specific_files,
280
291
[self.basis_tree, self.work_tree])
282
# Setup the progress bar ...
283
292
# one to finish, one for rev and inventory, and one for each
284
293
# inventory entry, and the same for the new inventory.
285
294
# note that this estimate is too long when we do a partial tree
292
301
if len(self.parents) > 1 and self.specific_files:
293
302
raise errors.CannotCommitSelectedFileMerge(self.specific_files)
295
# Build the new inventory
296
304
self.builder = self.branch.get_commit_builder(self.parents,
297
305
self.config, timestamp, timezone, committer, revprops, rev_id)
298
307
self._remove_deleted()
299
308
self._populate_new_inv()
300
309
self._report_deletes()
301
311
self._check_pointless()
302
313
self._emit_progress_update()
304
314
# TODO: Now the new inventory is known, check for conflicts and
305
315
# prompt the user for a commit message.
306
316
# ADHB 2006-08-08: If this is done, populate_new_inv should not add
313
323
self.message = message
314
324
self._escape_commit_message()
316
# Add revision data to the local branch
317
326
self.rev_id = self.builder.commit(self.message)
318
327
self._emit_progress_update()
328
# revision data is in the local branch now.
320
330
# upload revision data to the master.
321
331
# this will propagate merged revisions too if needed.
323
333
self.master_branch.repository.fetch(self.branch.repository,
324
334
revision_id=self.rev_id)
325
335
# now the master has the revision data
326
# 'commit' to the master first so a timeout here causes the
327
# local branch to be out of date
336
# 'commit' to the master first so a timeout here causes the local
337
# branch to be out of date
328
338
self.master_branch.set_last_revision_info(new_revno,
331
341
# and now do the commit locally.
332
342
self.branch.set_last_revision_info(new_revno, self.rev_id)
334
# Make the working tree up to date with the branch
335
344
rev_tree = self.builder.revision_tree()
336
345
self.work_tree.set_parent_trees([(self.rev_id, rev_tree)])
346
# now the work tree is up to date with the branch
337
348
self.reporter.completed(new_revno, self.rev_id)
339
# Process the post commit hooks, if any
340
self._process_hooks(old_revno, new_revno)
349
# old style commit hooks - should be deprecated ? (obsoleted in
351
if self.config.post_commit() is not None:
352
hooks = self.config.post_commit().split(' ')
353
# this would be nicer with twisted.python.reflect.namedAny
355
result = eval(hook + '(branch, rev_id)',
356
{'branch':self.branch,
358
'rev_id':self.rev_id})
359
# new style commit hooks:
360
if not self.bound_branch:
361
hook_master = self.branch
364
hook_master = self.master_branch
365
hook_local = self.branch
366
# With bound branches, when the master is behind the local branch,
367
# the 'old_revno' and old_revid values here are incorrect.
368
# XXX: FIXME ^. RBC 20060206
370
old_revid = self.parents[0]
372
old_revid = bzrlib.revision.NULL_REVISION
373
for hook in Branch.hooks['post_commit']:
374
hook(hook_local, hook_master, old_revno, old_revid, new_revno,
341
376
self._emit_progress_update()
440
475
self.master_branch.lock_write()
441
476
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,
497
478
def _cleanup(self):
498
479
"""Cleanup any open locks, progress bars etc."""
499
480
cleanups = [self._cleanup_bound_branch,