~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commit.py

  • Committer: John Arbash Meinel
  • Date: 2006-03-08 14:31:23 UTC
  • mfrom: (1598 +trunk)
  • mto: (1685.1.1 bzr-encoding)
  • mto: This revision was merged to the branch mainline in revision 1752.
  • Revision ID: john@arbash-meinel.com-20060308143123-448308b0db4de410
[merge] bzr.dev 1573, lots of updates

Show diffs side-by-side

added added

removed removed

Lines of Context:
76
76
from bzrlib.osutils import (local_time_offset,
77
77
                            rand_bytes, compact_date,
78
78
                            kind_marker, is_inside_any, quotefn,
79
 
                            sha_string, sha_strings, sha_file, isdir, isfile,
 
79
                            sha_file, isdir, isfile,
80
80
                            split_lines)
81
81
import bzrlib.config
 
82
import bzrlib.errors as errors
82
83
from bzrlib.errors import (BzrError, PointlessCommit,
83
84
                           HistoryMissing,
84
85
                           ConflictsInTree,
85
86
                           StrictCommitFailed
86
87
                           )
87
 
import bzrlib.gpg as gpg
88
88
from bzrlib.revision import Revision
89
89
from bzrlib.testament import Testament
90
90
from bzrlib.trace import mutter, note, warning
91
91
from bzrlib.xml5 import serializer_v5
92
92
from bzrlib.inventory import Inventory, ROOT_ID
93
93
from bzrlib.symbol_versioning import *
94
 
from bzrlib.weave import Weave
95
 
from bzrlib.weavefile import read_weave, write_weave_v5
96
94
from bzrlib.workingtree import WorkingTree
97
95
 
98
96
 
180
178
               strict=False,
181
179
               verbose=False,
182
180
               revprops=None,
183
 
               working_tree=None):
 
181
               working_tree=None,
 
182
               local=False):
184
183
        """Commit working copy as a new revision.
185
184
 
186
185
        branch -- the deprecated branch to commit to. New callers should pass in 
206
205
            contains unknown files.
207
206
 
208
207
        revprops -- Properties for new revision
 
208
        :param local: Perform a local only commit.
209
209
        """
210
210
        mutter('preparing to commit')
211
211
 
214
214
                 "deprecated as of bzr 0.8. Please use working_tree= instead.",
215
215
                 DeprecationWarning, stacklevel=2)
216
216
            self.branch = branch
217
 
            self.work_tree = WorkingTree(branch.base, branch)
 
217
            self.work_tree = self.branch.bzrdir.open_workingtree()
218
218
        elif working_tree is None:
219
219
            raise BzrError("One of branch and working_tree must be passed into commit().")
220
220
        else:
224
224
            raise BzrError("The message keyword parameter is required for commit().")
225
225
 
226
226
        self.weave_store = self.branch.repository.weave_store
 
227
        self.bound_branch = None
 
228
        self.local = local
 
229
        self.master_branch = None
227
230
        self.rev_id = rev_id
228
231
        self.specific_files = specific_files
229
232
        self.allow_pointless = allow_pointless
230
 
        self.revprops = {'branch-nick': self.branch.nick}
231
 
        if revprops:
 
233
        self.revprops = {}
 
234
        if revprops is not None:
232
235
            self.revprops.update(revprops)
233
236
 
234
 
        if strict:
235
 
            # raise an exception as soon as we find a single unknown.
236
 
            for unknown in self.work_tree.unknowns():
237
 
                raise StrictCommitFailed()
238
 
 
239
 
        if timestamp is None:
240
 
            self.timestamp = time.time()
241
 
        else:
242
 
            self.timestamp = long(timestamp)
243
 
            
244
 
        if self.config is None:
245
 
            self.config = bzrlib.config.BranchConfig(self.branch)
246
 
 
247
 
        if rev_id is None:
248
 
            self.rev_id = _gen_revision_id(self.config, self.timestamp)
249
 
        else:
250
 
            self.rev_id = rev_id
251
 
 
252
 
        if committer is None:
253
 
            self.committer = self.config.username()
254
 
        else:
255
 
            assert isinstance(committer, basestring), type(committer)
256
 
            self.committer = committer
257
 
 
258
 
        if timezone is None:
259
 
            self.timezone = local_time_offset()
260
 
        else:
261
 
            self.timezone = int(timezone)
262
 
 
263
 
        if isinstance(message, str):
264
 
            message = message.decode(bzrlib.user_encoding)
265
 
        assert isinstance(message, unicode), type(message)
266
 
        self.message = message
267
 
        self._escape_commit_message()
268
 
 
269
 
        self.branch.lock_write()
 
237
        self.work_tree.lock_write()
270
238
        try:
 
239
            # setup the bound branch variables as needed.
 
240
            self._check_bound_branch()
 
241
 
 
242
            # check for out of date working trees
 
243
            # if we are bound, then self.branch is the master branch and this
 
244
            # test is thus all we need.
 
245
            if self.work_tree.last_revision() != self.master_branch.last_revision():
 
246
                raise errors.OutOfDateTree(self.work_tree)
 
247
    
 
248
            if strict:
 
249
                # raise an exception as soon as we find a single unknown.
 
250
                for unknown in self.work_tree.unknowns():
 
251
                    raise StrictCommitFailed()
 
252
    
 
253
            if timestamp is None:
 
254
                self.timestamp = time.time()
 
255
            else:
 
256
                self.timestamp = long(timestamp)
 
257
                
 
258
            if self.config is None:
 
259
                self.config = bzrlib.config.BranchConfig(self.branch)
 
260
    
 
261
            if rev_id is None:
 
262
                self.rev_id = _gen_revision_id(self.config, self.timestamp)
 
263
            else:
 
264
                self.rev_id = rev_id
 
265
    
 
266
            if committer is None:
 
267
                self.committer = self.config.username()
 
268
            else:
 
269
                assert isinstance(committer, basestring), type(committer)
 
270
                self.committer = committer
 
271
    
 
272
            if timezone is None:
 
273
                self.timezone = local_time_offset()
 
274
            else:
 
275
                self.timezone = int(timezone)
 
276
    
 
277
            if isinstance(message, str):
 
278
                message = message.decode(bzrlib.user_encoding)
 
279
            assert isinstance(message, unicode), type(message)
 
280
            self.message = message
 
281
            self._escape_commit_message()
 
282
 
271
283
            self.work_inv = self.work_tree.inventory
272
 
            self.basis_tree = self.branch.basis_tree()
 
284
            self.basis_tree = self.work_tree.basis_tree()
273
285
            self.basis_inv = self.basis_tree.inventory
274
286
 
275
287
            self._gather_parents()
290
302
            if len(list(self.work_tree.iter_conflicts()))>0:
291
303
                raise ConflictsInTree
292
304
 
293
 
            self._record_inventory()
 
305
            self.inv_sha1 = self.branch.repository.add_inventory(
 
306
                self.rev_id,
 
307
                self.new_inv,
 
308
                self.present_parents
 
309
                )
294
310
            self._make_revision()
 
311
            # revision data is in the local branch now.
 
312
            
 
313
            # upload revision data to the master.
 
314
            # this will propogate merged revisions too if needed.
 
315
            if self.bound_branch:
 
316
                self.master_branch.repository.fetch(self.branch.repository,
 
317
                                                    revision_id=self.rev_id)
 
318
                # now the master has the revision data
 
319
                # 'commit' to the master first so a timeout here causes the local
 
320
                # branch to be out of date
 
321
                self.master_branch.append_revision(self.rev_id)
 
322
 
 
323
            # and now do the commit locally.
 
324
            self.branch.append_revision(self.rev_id)
 
325
 
295
326
            self.work_tree.set_pending_merges([])
296
 
            self.branch.append_revision(self.rev_id)
297
327
            if len(self.parents):
298
328
                precursor = self.parents[0]
299
329
            else:
300
330
                precursor = None
301
331
            self.work_tree.set_last_revision(self.rev_id, precursor)
 
332
            # now the work tree is up to date with the branch
 
333
            
302
334
            self.reporter.completed(self.branch.revno()+1, self.rev_id)
303
335
            if self.config.post_commit() is not None:
304
336
                hooks = self.config.post_commit().split(' ')
309
341
                                   'bzrlib':bzrlib,
310
342
                                   'rev_id':self.rev_id})
311
343
        finally:
312
 
            self.branch.unlock()
313
 
 
314
 
    def _record_inventory(self):
315
 
        """Store the inventory for the new revision."""
316
 
        inv_text = serializer_v5.write_inventory_to_string(self.new_inv)
317
 
        self.inv_sha1 = sha_string(inv_text)
318
 
        s = self.branch.repository.control_weaves
319
 
        s.add_text('inventory', self.rev_id,
320
 
                   split_lines(inv_text), self.present_parents,
321
 
                   self.branch.get_transaction())
 
344
            self._cleanup_bound_branch()
 
345
            self.work_tree.unlock()
 
346
 
 
347
    def _check_bound_branch(self):
 
348
        """Check to see if the local branch is bound.
 
349
 
 
350
        If it is bound, then most of the commit will actually be
 
351
        done using the remote branch as the target branch.
 
352
        Only at the end will the local branch be updated.
 
353
        """
 
354
        if self.local and not self.branch.get_bound_location():
 
355
            raise errors.LocalRequiresBoundBranch()
 
356
 
 
357
        if not self.local:
 
358
            self.master_branch = self.branch.get_master_branch()
 
359
 
 
360
        if not self.master_branch:
 
361
            # make this branch the reference branch for out of date checks.
 
362
            self.master_branch = self.branch
 
363
            return
 
364
 
 
365
        # If the master branch is bound, we must fail
 
366
        master_bound_location = self.master_branch.get_bound_location()
 
367
        if master_bound_location:
 
368
            raise errors.CommitToDoubleBoundBranch(self.branch,
 
369
                    self.master_branch, master_bound_location)
 
370
 
 
371
        # TODO: jam 20051230 We could automatically push local
 
372
        #       commits to the remote branch if they would fit.
 
373
        #       But for now, just require remote to be identical
 
374
        #       to local.
 
375
        
 
376
        # Make sure the local branch is identical to the master
 
377
        master_rh = self.master_branch.revision_history()
 
378
        local_rh = self.branch.revision_history()
 
379
        if local_rh != master_rh:
 
380
            raise errors.BoundBranchOutOfDate(self.branch,
 
381
                    self.master_branch)
 
382
 
 
383
        # Now things are ready to change the master branch
 
384
        # so grab the lock
 
385
        self.bound_branch = self.branch
 
386
        self.master_branch.lock_write()
 
387
####        
 
388
####        # Check to see if we have any pending merges. If we do
 
389
####        # those need to be pushed into the master branch
 
390
####        pending_merges = self.work_tree.pending_merges()
 
391
####        if pending_merges:
 
392
####            for revision_id in pending_merges:
 
393
####                self.master_branch.repository.fetch(self.bound_branch.repository,
 
394
####                                                    revision_id=revision_id)
 
395
 
 
396
    def _cleanup_bound_branch(self):
 
397
        """Executed at the end of a try/finally to cleanup a bound branch.
 
398
 
 
399
        If the branch wasn't bound, this is a no-op.
 
400
        If it was, it resents self.branch to the local branch, instead
 
401
        of being the master.
 
402
        """
 
403
        if not self.bound_branch:
 
404
            return
 
405
        self.master_branch.unlock()
322
406
 
323
407
    def _escape_commit_message(self):
324
408
        """Replace xml-incompatible control characters."""
361
445
            
362
446
    def _make_revision(self):
363
447
        """Record a new revision object for this commit."""
364
 
        self.rev = Revision(timestamp=self.timestamp,
365
 
                            timezone=self.timezone,
366
 
                            committer=self.committer,
367
 
                            message=self.message,
368
 
                            inventory_sha1=self.inv_sha1,
369
 
                            revision_id=self.rev_id,
370
 
                            properties=self.revprops)
371
 
        self.rev.parent_ids = self.parents
372
 
        rev_tmp = StringIO()
373
 
        serializer_v5.write_revision(self.rev, rev_tmp)
374
 
        rev_tmp.seek(0)
375
 
        if self.config.signature_needed():
376
 
            plaintext = Testament(self.rev, self.new_inv).as_short_text()
377
 
            self.branch.repository.store_revision_signature(
378
 
                gpg.GPGStrategy(self.config), plaintext, self.rev_id)
379
 
        self.branch.repository.revision_store.add(rev_tmp, self.rev_id)
380
 
        mutter('new revision_id is {%s}', self.rev_id)
 
448
        rev = Revision(timestamp=self.timestamp,
 
449
                       timezone=self.timezone,
 
450
                       committer=self.committer,
 
451
                       message=self.message,
 
452
                       inventory_sha1=self.inv_sha1,
 
453
                       revision_id=self.rev_id,
 
454
                       properties=self.revprops)
 
455
        rev.parent_ids = self.parents
 
456
        self.branch.repository.add_revision(self.rev_id, rev, self.new_inv, self.config)
381
457
 
382
458
    def _remove_deleted(self):
383
459
        """Remove deleted files from the working inventories.
417
493
        for path, ie in self.new_inv.iter_entries():
418
494
            previous_entries = ie.find_previous_heads(
419
495
                self.parent_invs, 
420
 
                self.weave_store.get_weave_prelude_or_empty(ie.file_id,
 
496
                self.weave_store.get_weave_or_empty(ie.file_id,
421
497
                    self.branch.get_transaction()))
422
498
            if ie.revision is None:
423
499
                change = ie.snapshot(self.rev_id, path, previous_entries,