~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: 2006-02-21 17:41:02 UTC
  • mfrom: (1185.50.85 bzr-jam-integration)
  • Revision ID: pqm@pqm.ubuntu.com-20060221174102-aa6bd4464296c614
Mac OSX raises EPERM when you try to unlink a directory

Show diffs side-by-side

added added

removed removed

Lines of Context:
56
56
# merges from, then it should still be reported as newly added
57
57
# relative to the basis revision.
58
58
 
 
59
# TODO: Do checks that the tree can be committed *before* running the 
 
60
# editor; this should include checks for a pointless commit and for 
 
61
# unknown or missing files.
 
62
 
 
63
# TODO: If commit fails, leave the message in a file somewhere.
 
64
 
59
65
 
60
66
import os
61
67
import re
66
72
from binascii import hexlify
67
73
from cStringIO import StringIO
68
74
 
 
75
from bzrlib.atomicfile import AtomicFile
69
76
from bzrlib.osutils import (local_time_offset,
70
77
                            rand_bytes, compact_date,
71
78
                            kind_marker, is_inside_any, quotefn,
72
79
                            sha_string, sha_strings, sha_file, isdir, isfile,
73
80
                            split_lines)
74
 
from bzrlib.branch import gen_file_id
75
81
import bzrlib.config
 
82
import bzrlib.errors as errors
76
83
from bzrlib.errors import (BzrError, PointlessCommit,
77
84
                           HistoryMissing,
78
 
                           ConflictsInTree
 
85
                           ConflictsInTree,
 
86
                           StrictCommitFailed
79
87
                           )
 
88
import bzrlib.gpg as gpg
80
89
from bzrlib.revision import Revision
 
90
from bzrlib.testament import Testament
81
91
from bzrlib.trace import mutter, note, warning
82
92
from bzrlib.xml5 import serializer_v5
83
93
from bzrlib.inventory import Inventory, ROOT_ID
 
94
from bzrlib.symbol_versioning import *
84
95
from bzrlib.weave import Weave
85
96
from bzrlib.weavefile import read_weave, write_weave_v5
86
 
from bzrlib.atomicfile import AtomicFile
87
 
 
88
 
 
 
97
from bzrlib.workingtree import WorkingTree
 
98
 
 
99
 
 
100
@deprecated_function(zero_seven)
89
101
def commit(*args, **kwargs):
90
102
    """Commit a new revision to a branch.
91
103
 
115
127
    def missing(self, path):
116
128
        pass
117
129
 
 
130
 
118
131
class ReportCommitToLog(NullCommitReporter):
119
132
 
120
133
    def snapshot_change(self, change, path):
132
145
    def missing(self, path):
133
146
        note('missing %s', path)
134
147
 
 
148
 
135
149
class Commit(object):
136
150
    """Task of committing a new revision.
137
151
 
145
159
            working inventory.
146
160
    """
147
161
    def __init__(self,
148
 
                 reporter=None):
 
162
                 reporter=None,
 
163
                 config=None):
149
164
        if reporter is not None:
150
165
            self.reporter = reporter
151
166
        else:
152
167
            self.reporter = NullCommitReporter()
153
 
 
 
168
        if config is not None:
 
169
            self.config = config
 
170
        else:
 
171
            self.config = None
154
172
        
155
173
    def commit(self,
156
 
               branch, message,
 
174
               branch=DEPRECATED_PARAMETER, message=None,
157
175
               timestamp=None,
158
176
               timezone=None,
159
177
               committer=None,
160
178
               specific_files=None,
161
179
               rev_id=None,
162
180
               allow_pointless=True,
 
181
               strict=False,
163
182
               verbose=False,
164
 
               revprops=None):
 
183
               revprops=None,
 
184
               working_tree=None):
165
185
        """Commit working copy as a new revision.
166
186
 
 
187
        branch -- the deprecated branch to commit to. New callers should pass in 
 
188
                  working_tree instead
 
189
 
 
190
        message -- the commit message, a mandatory parameter
 
191
 
167
192
        timestamp -- if not None, seconds-since-epoch for a
168
193
             postdated/predated commit.
169
194
 
178
203
        allow_pointless -- If true (default), commit even if nothing
179
204
            has changed and no merges are recorded.
180
205
 
 
206
        strict -- If true, don't allow a commit if the working tree
 
207
            contains unknown files.
 
208
 
181
209
        revprops -- Properties for new revision
182
210
        """
183
211
        mutter('preparing to commit')
184
212
 
185
 
        self.branch = branch
186
 
        self.weave_store = branch.weave_store
 
213
        if deprecated_passed(branch):
 
214
            warn("Commit.commit (branch, ...): The branch parameter is "
 
215
                 "deprecated as of bzr 0.8. Please use working_tree= instead.",
 
216
                 DeprecationWarning, stacklevel=2)
 
217
            self.branch = branch
 
218
            self.work_tree = self.branch.bzrdir.open_workingtree()
 
219
        elif working_tree is None:
 
220
            raise BzrError("One of branch and working_tree must be passed into commit().")
 
221
        else:
 
222
            self.work_tree = working_tree
 
223
            self.branch = self.work_tree.branch
 
224
        if message is None:
 
225
            raise BzrError("The message keyword parameter is required for commit().")
 
226
 
 
227
        self.weave_store = self.branch.repository.weave_store
187
228
        self.rev_id = rev_id
188
229
        self.specific_files = specific_files
189
230
        self.allow_pointless = allow_pointless
190
 
        self.revprops = revprops
 
231
        self.revprops = {'branch-nick': self.branch.nick}
 
232
        if revprops:
 
233
            self.revprops.update(revprops)
 
234
 
 
235
        # check for out of date working trees
 
236
        if self.work_tree.last_revision() != self.branch.last_revision():
 
237
            raise errors.OutOfDateTree(self.work_tree)
 
238
 
 
239
        if strict:
 
240
            # raise an exception as soon as we find a single unknown.
 
241
            for unknown in self.work_tree.unknowns():
 
242
                raise StrictCommitFailed()
191
243
 
192
244
        if timestamp is None:
193
245
            self.timestamp = time.time()
194
246
        else:
195
247
            self.timestamp = long(timestamp)
196
248
            
 
249
        if self.config is None:
 
250
            self.config = bzrlib.config.BranchConfig(self.branch)
 
251
 
197
252
        if rev_id is None:
198
 
            self.rev_id = _gen_revision_id(self.branch, self.timestamp)
 
253
            self.rev_id = _gen_revision_id(self.config, self.timestamp)
199
254
        else:
200
255
            self.rev_id = rev_id
201
256
 
202
257
        if committer is None:
203
 
            self.committer = bzrlib.config.username(self.branch)
 
258
            self.committer = self.config.username()
204
259
        else:
205
260
            assert isinstance(committer, basestring), type(committer)
206
261
            self.committer = committer
210
265
        else:
211
266
            self.timezone = int(timezone)
212
267
 
213
 
        assert isinstance(message, basestring), type(message)
 
268
        if isinstance(message, str):
 
269
            message = message.decode(bzrlib.user_encoding)
 
270
        assert isinstance(message, unicode), type(message)
214
271
        self.message = message
215
272
        self._escape_commit_message()
216
273
 
217
274
        self.branch.lock_write()
218
275
        try:
219
 
            self.work_tree = self.branch.working_tree()
220
276
            self.work_inv = self.work_tree.inventory
221
 
            self.basis_tree = self.branch.basis_tree()
 
277
            self.basis_tree = self.work_tree.basis_tree()
222
278
            self.basis_inv = self.basis_tree.inventory
223
279
 
224
280
            self._gather_parents()
241
297
 
242
298
            self._record_inventory()
243
299
            self._make_revision()
 
300
            self.work_tree.set_pending_merges([])
 
301
            self.branch.append_revision(self.rev_id)
 
302
            if len(self.parents):
 
303
                precursor = self.parents[0]
 
304
            else:
 
305
                precursor = None
 
306
            self.work_tree.set_last_revision(self.rev_id, precursor)
244
307
            self.reporter.completed(self.branch.revno()+1, self.rev_id)
245
 
            self.branch.append_revision(self.rev_id)
246
 
            self.branch.set_pending_merges([])
 
308
            if self.config.post_commit() is not None:
 
309
                hooks = self.config.post_commit().split(' ')
 
310
                # this would be nicer with twisted.python.reflect.namedAny
 
311
                for hook in hooks:
 
312
                    result = eval(hook + '(branch, rev_id)',
 
313
                                  {'branch':self.branch,
 
314
                                   'bzrlib':bzrlib,
 
315
                                   'rev_id':self.rev_id})
247
316
        finally:
248
317
            self.branch.unlock()
249
318
 
251
320
        """Store the inventory for the new revision."""
252
321
        inv_text = serializer_v5.write_inventory_to_string(self.new_inv)
253
322
        self.inv_sha1 = sha_string(inv_text)
254
 
        s = self.branch.control_weaves
 
323
        s = self.branch.repository.control_weaves
255
324
        s.add_text('inventory', self.rev_id,
256
325
                   split_lines(inv_text), self.present_parents,
257
326
                   self.branch.get_transaction())
262
331
        # represented in well-formed XML; escape characters that
263
332
        # aren't listed in the XML specification
264
333
        # (http://www.w3.org/TR/REC-xml/#NT-Char).
265
 
        if isinstance(self.message, unicode):
266
 
            char_pattern = u'[^\x09\x0A\x0D\u0020-\uD7FF\uE000-\uFFFD]'
267
 
        else:
268
 
            # Use a regular 'str' as pattern to avoid having re.subn
269
 
            # return 'unicode' results.
270
 
            char_pattern = '[^x09\x0A\x0D\x20-\xFF]'
271
334
        self.message, escape_count = re.subn(
272
 
            char_pattern,
 
335
            u'[^\x09\x0A\x0D\u0020-\uD7FF\uE000-\uFFFD]+',
273
336
            lambda match: match.group(0).encode('unicode_escape'),
274
337
            self.message)
275
338
        if escape_count:
277
340
 
278
341
    def _gather_parents(self):
279
342
        """Record the parents of a merge for merge detection."""
280
 
        pending_merges = self.branch.pending_merges()
 
343
        pending_merges = self.work_tree.pending_merges()
281
344
        self.parents = []
282
345
        self.parent_invs = []
283
346
        self.present_parents = []
286
349
            self.parents.append(precursor_id)
287
350
        self.parents += pending_merges
288
351
        for revision in self.parents:
289
 
            if self.branch.has_revision(revision):
290
 
                self.parent_invs.append(self.branch.get_inventory(revision))
 
352
            if self.branch.repository.has_revision(revision):
 
353
                inventory = self.branch.repository.get_inventory(revision)
 
354
                self.parent_invs.append(inventory)
291
355
                self.present_parents.append(revision)
292
356
 
293
357
    def _check_parents_present(self):
294
358
        for parent_id in self.parents:
295
359
            mutter('commit parent revision {%s}', parent_id)
296
 
            if not self.branch.has_revision(parent_id):
 
360
            if not self.branch.repository.has_revision(parent_id):
297
361
                if parent_id == self.branch.last_revision():
298
362
                    warning("parent is missing %r", parent_id)
299
363
                    raise HistoryMissing(self.branch, 'revision', parent_id)
313
377
        rev_tmp = StringIO()
314
378
        serializer_v5.write_revision(self.rev, rev_tmp)
315
379
        rev_tmp.seek(0)
316
 
        self.branch.revision_store.add(rev_tmp, self.rev_id)
 
380
        if self.config.signature_needed():
 
381
            plaintext = Testament(self.rev, self.new_inv).as_short_text()
 
382
            self.branch.repository.store_revision_signature(
 
383
                gpg.GPGStrategy(self.config), plaintext, self.rev_id)
 
384
        self.branch.repository.revision_store.add(rev_tmp, self.rev_id)
317
385
        mutter('new revision_id is {%s}', self.rev_id)
318
386
 
319
387
    def _remove_deleted(self):
339
407
            deleted_ids.sort(reverse=True)
340
408
            for path, file_id in deleted_ids:
341
409
                del self.work_inv[file_id]
342
 
            self.branch._write_inventory(self.work_inv)
 
410
            self.work_tree._write_inventory(self.work_inv)
343
411
 
344
412
    def _store_snapshot(self):
345
413
        """Pass over inventory and record a snapshot.
354
422
        for path, ie in self.new_inv.iter_entries():
355
423
            previous_entries = ie.find_previous_heads(
356
424
                self.parent_invs, 
357
 
                self.weave_store.get_weave_or_empty(ie.file_id,
 
425
                self.weave_store.get_weave_prelude_or_empty(ie.file_id,
358
426
                    self.branch.get_transaction()))
359
427
            if ie.revision is None:
360
428
                change = ie.snapshot(self.rev_id, path, previous_entries,
419
487
            if file_id not in self.new_inv:
420
488
                self.reporter.deleted(self.basis_inv.id2path(file_id))
421
489
 
422
 
def _gen_revision_id(branch, when):
 
490
def _gen_revision_id(config, when):
423
491
    """Return new revision-id."""
424
 
    s = '%s-%s-' % (bzrlib.config.user_email(branch), compact_date(when))
 
492
    s = '%s-%s-' % (config.user_email(), compact_date(when))
425
493
    s += hexlify(rand_bytes(8))
426
494
    return s