~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commit.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-06-06 01:58:59 UTC
  • mfrom: (2507.1.1 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20070606015859-rwlz810cte7l1row
(robertc) Create the top level changes-list from the London sprint for reference.

Show diffs side-by-side

added added

removed removed

Lines of Context:
106
106
 
107
107
class ReportCommitToLog(NullCommitReporter):
108
108
 
109
 
    def _note(self, format, *args):
110
 
        """Output a message.
111
 
 
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.
115
 
 
116
 
        Subclasses may choose to override this method but need to be aware
117
 
        of its potential impact on performance.
118
 
        """
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
121
113
 
122
114
    def snapshot_change(self, change, path):
123
115
        if change == 'unchanged':
124
116
            return
125
117
        if change == 'added' and path == '':
126
118
            return
127
 
        self._note("%s %s", change, path)
 
119
        note("%s %s", change, path)
128
120
 
129
121
    def completed(self, revno, rev_id):
130
 
        self._note('Committed revision %d.', revno)
 
122
        note('Committed revision %d.', revno)
131
123
    
132
124
    def deleted(self, file_id):
133
 
        self._note('deleted %s', file_id)
 
125
        note('deleted %s', file_id)
134
126
 
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)
137
129
 
138
130
    def missing(self, path):
139
 
        self._note('missing %s', path)
 
131
        note('missing %s', path)
140
132
 
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)
143
135
 
144
136
 
145
137
class Commit(object):
161
153
            self.reporter = reporter
162
154
        else:
163
155
            self.reporter = NullCommitReporter()
164
 
        self.config = config
 
156
        if config is not None:
 
157
            self.config = config
 
158
        else:
 
159
            self.config = None
165
160
        
166
161
    def commit(self,
167
162
               message=None,
182
177
               recursive='down'):
183
178
        """Commit working copy as a new revision.
184
179
 
185
 
        :param message: the commit message (it or message_callback is required)
186
 
 
187
 
        :param timestamp: if not None, seconds-since-epoch for a
188
 
            postdated/predated commit.
189
 
 
190
 
        :param specific_files: If true, commit only those files.
191
 
 
192
 
        :param rev_id: If set, use this as the new revision id.
 
180
        message -- the commit message (it or message_callback is required)
 
181
 
 
182
        timestamp -- if not None, seconds-since-epoch for a
 
183
             postdated/predated commit.
 
184
 
 
185
        specific_files -- If true, commit only those files.
 
186
 
 
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.
197
192
 
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.
200
195
 
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.
203
198
 
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.
240
235
        self.committer = committer
241
236
        self.strict = strict
242
237
        self.verbose = verbose
 
238
        self.local = local
243
239
 
244
240
        if reporter is None and self.reporter is None:
245
241
            self.reporter = NullCommitReporter()
252
248
        self.basis_tree.lock_read()
253
249
        try:
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
257
253
 
258
 
            # Setup the bound branch variables as needed.
 
254
            # setup the bound branch variables as needed.
259
255
            self._check_bound_branch()
260
256
 
261
 
            # Check that the working tree is up to date
262
 
            old_revno,new_revno = self._check_out_of_date_tree()
263
 
 
 
257
            # check for out of date working trees
 
258
            try:
 
259
                first_tree_parent = self.work_tree.get_parent_ids()[0]
 
260
            except IndexError:
 
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
 
272
            else:
 
273
                # ghost parents never appear in revision history.
 
274
                new_revno = 1
264
275
            if strict:
265
276
                # raise an exception as soon as we find a single unknown.
266
277
                for unknown in self.work_tree.unknowns():
278
289
                # cheaply?
279
290
                tree.find_ids_across_trees(specific_files,
280
291
                                           [self.basis_tree, self.work_tree])
281
 
 
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)
294
303
            
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)
 
306
            
298
307
            self._remove_deleted()
299
308
            self._populate_new_inv()
300
309
            self._report_deletes()
 
310
 
301
311
            self._check_pointless()
 
312
 
302
313
            self._emit_progress_update()
303
 
 
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()
315
325
 
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.
319
329
            
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,
329
339
                                                          self.rev_id)
330
340
 
331
341
            # and now do the commit locally.
332
342
            self.branch.set_last_revision_info(new_revno, self.rev_id)
333
343
 
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
 
347
            
337
348
            self.reporter.completed(new_revno, self.rev_id)
338
 
 
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
 
350
            # 0.15)
 
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
 
354
                for hook in hooks:
 
355
                    result = eval(hook + '(branch, rev_id)',
 
356
                                  {'branch':self.branch,
 
357
                                   'bzrlib':bzrlib,
 
358
                                   'rev_id':self.rev_id})
 
359
            # new style commit hooks:
 
360
            if not self.bound_branch:
 
361
                hook_master = self.branch
 
362
                hook_local = None
 
363
            else:
 
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
 
369
            if self.parents:
 
370
                old_revid = self.parents[0]
 
371
            else:
 
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,
 
375
                    self.rev_id)
341
376
            self._emit_progress_update()
342
377
        finally:
343
378
            self._cleanup()
440
475
        self.master_branch.lock_write()
441
476
        self.master_locked = True
442
477
 
443
 
    def _check_out_of_date_tree(self):
444
 
        """Check that the working tree is up to date.
445
 
 
446
 
        :return: old_revision_number,new_revision_number tuple
447
 
        """
448
 
        try:
449
 
            first_tree_parent = self.work_tree.get_parent_ids()[0]
450
 
        except IndexError:
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
462
 
        else:
463
 
            # ghost parents never appear in revision history.
464
 
            new_revno = 1
465
 
        return old_revno,new_revno
466
 
 
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
470
 
        # 0.15)
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
474
 
            for hook in hooks:
475
 
                result = eval(hook + '(branch, rev_id)',
476
 
                              {'branch':self.branch,
477
 
                               'bzrlib':bzrlib,
478
 
                               'rev_id':self.rev_id})
479
 
        # new style commit hooks:
480
 
        if not self.bound_branch:
481
 
            hook_master = self.branch
482
 
            hook_local = None
483
 
        else:
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
489
 
        if self.parents:
490
 
            old_revid = self.parents[0]
491
 
        else:
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,
495
 
                self.rev_id)
496
 
 
497
478
    def _cleanup(self):
498
479
        """Cleanup any open locks, progress bars etc."""
499
480
        cleanups = [self._cleanup_bound_branch,