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,
81
81
import bzrlib.config
82
import bzrlib.errors as errors
82
83
from bzrlib.errors import (BzrError, PointlessCommit,
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
232
237
self.revprops.update(revprops)
235
# raise an exception as soon as we find a single unknown.
236
for unknown in self.work_tree.unknowns():
237
raise StrictCommitFailed()
239
if timestamp is None:
240
self.timestamp = time.time()
242
self.timestamp = long(timestamp)
244
if self.config is None:
245
self.config = bzrlib.config.BranchConfig(self.branch)
248
self.rev_id = _gen_revision_id(self.config, self.timestamp)
252
if committer is None:
253
self.committer = self.config.username()
255
assert isinstance(committer, basestring), type(committer)
256
self.committer = committer
259
self.timezone = local_time_offset()
261
self.timezone = int(timezone)
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()
269
self.branch.lock_write()
239
self.work_tree.lock_write()
241
# setup the bound branch variables as needed.
242
self._check_bound_branch()
244
# check for out of date working trees
245
# if we are bound, then self.branch is the master branch and this
246
# test is thus all we need.
247
if self.work_tree.last_revision() != self.master_branch.last_revision():
248
raise errors.OutOfDateTree(self.work_tree)
251
# raise an exception as soon as we find a single unknown.
252
for unknown in self.work_tree.unknowns():
253
raise StrictCommitFailed()
255
if timestamp is None:
256
self.timestamp = time.time()
258
self.timestamp = long(timestamp)
260
if self.config is None:
261
self.config = bzrlib.config.BranchConfig(self.branch)
264
self.rev_id = _gen_revision_id(self.config, self.timestamp)
268
if committer is None:
269
self.committer = self.config.username()
271
assert isinstance(committer, basestring), type(committer)
272
self.committer = committer
275
self.timezone = local_time_offset()
277
self.timezone = int(timezone)
279
if isinstance(message, str):
280
message = message.decode(bzrlib.user_encoding)
281
assert isinstance(message, unicode), type(message)
282
self.message = message
283
self._escape_commit_message()
271
285
self.work_inv = self.work_tree.inventory
272
self.basis_tree = self.branch.basis_tree()
286
self.basis_tree = self.work_tree.basis_tree()
273
287
self.basis_inv = self.basis_tree.inventory
275
289
self._gather_parents()
290
304
if len(list(self.work_tree.iter_conflicts()))>0:
291
305
raise ConflictsInTree
293
self._record_inventory()
307
self.inv_sha1 = self.branch.repository.add_inventory(
294
312
self._make_revision()
313
# revision data is in the local branch now.
315
# upload revision data to the master.
316
# this will propogate merged revisions too if needed.
317
if self.bound_branch:
318
self.master_branch.repository.fetch(self.branch.repository,
319
revision_id=self.rev_id)
320
# now the master has the revision data
321
# 'commit' to the master first so a timeout here causes the local
322
# branch to be out of date
323
self.master_branch.append_revision(self.rev_id)
325
# and now do the commit locally.
326
self.branch.append_revision(self.rev_id)
295
328
self.work_tree.set_pending_merges([])
296
self.branch.append_revision(self.rev_id)
297
329
if len(self.parents):
298
330
precursor = self.parents[0]
301
333
self.work_tree.set_last_revision(self.rev_id, precursor)
334
# now the work tree is up to date with the branch
302
336
self.reporter.completed(self.branch.revno()+1, self.rev_id)
303
337
if self.config.post_commit() is not None:
304
338
hooks = self.config.post_commit().split(' ')
310
344
'rev_id':self.rev_id})
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())
346
self._cleanup_bound_branch()
347
self.work_tree.unlock()
349
def _check_bound_branch(self):
350
"""Check to see if the local branch is bound.
352
If it is bound, then most of the commit will actually be
353
done using the remote branch as the target branch.
354
Only at the end will the local branch be updated.
356
if self.local and not self.branch.get_bound_location():
357
raise errors.LocalRequiresBoundBranch()
360
self.master_branch = self.branch.get_master_branch()
362
if not self.master_branch:
363
# make this branch the reference branch for out of date checks.
364
self.master_branch = self.branch
367
# If the master branch is bound, we must fail
368
master_bound_location = self.master_branch.get_bound_location()
369
if master_bound_location:
370
raise errors.CommitToDoubleBoundBranch(self.branch,
371
self.master_branch, master_bound_location)
373
# TODO: jam 20051230 We could automatically push local
374
# commits to the remote branch if they would fit.
375
# But for now, just require remote to be identical
378
# Make sure the local branch is identical to the master
379
master_rh = self.master_branch.revision_history()
380
local_rh = self.branch.revision_history()
381
if local_rh != master_rh:
382
raise errors.BoundBranchOutOfDate(self.branch,
385
# Now things are ready to change the master branch
387
self.bound_branch = self.branch
388
self.master_branch.lock_write()
390
#### # Check to see if we have any pending merges. If we do
391
#### # those need to be pushed into the master branch
392
#### pending_merges = self.work_tree.pending_merges()
393
#### if pending_merges:
394
#### for revision_id in pending_merges:
395
#### self.master_branch.repository.fetch(self.bound_branch.repository,
396
#### revision_id=revision_id)
398
def _cleanup_bound_branch(self):
399
"""Executed at the end of a try/finally to cleanup a bound branch.
401
If the branch wasn't bound, this is a no-op.
402
If it was, it resents self.branch to the local branch, instead
405
if not self.bound_branch:
407
self.master_branch.unlock()
323
409
def _escape_commit_message(self):
324
410
"""Replace xml-incompatible control characters."""
362
448
def _make_revision(self):
363
449
"""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
373
serializer_v5.write_revision(self.rev, rev_tmp)
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)
450
rev = Revision(timestamp=self.timestamp,
451
timezone=self.timezone,
452
committer=self.committer,
453
message=self.message,
454
inventory_sha1=self.inv_sha1,
455
revision_id=self.rev_id,
456
properties=self.revprops)
457
rev.parent_ids = self.parents
458
self.branch.repository.add_revision(self.rev_id, rev, self.new_inv, self.config)
382
460
def _remove_deleted(self):
383
461
"""Remove deleted files from the working inventories.