~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commit.py

  • Committer: Martin Pool
  • Date: 2006-01-13 08:12:22 UTC
  • mfrom: (1185.63.5 bzr.patches)
  • Revision ID: mbp@sourcefrog.net-20060113081222-6b572004a2ade0cc
[merge] test_hashcache_raise from Denys

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
76
82
from bzrlib.errors import (BzrError, PointlessCommit,
77
83
                           HistoryMissing,
78
 
                           ConflictsInTree
 
84
                           ConflictsInTree,
 
85
                           StrictCommitFailed
79
86
                           )
 
87
import bzrlib.gpg as gpg
80
88
from bzrlib.revision import Revision
 
89
from bzrlib.testament import Testament
81
90
from bzrlib.trace import mutter, note, warning
82
91
from bzrlib.xml5 import serializer_v5
83
92
from bzrlib.inventory import Inventory, ROOT_ID
84
93
from bzrlib.weave import Weave
85
94
from bzrlib.weavefile import read_weave, write_weave_v5
86
 
from bzrlib.atomicfile import AtomicFile
 
95
from bzrlib.workingtree import WorkingTree
87
96
 
88
97
 
89
98
def commit(*args, **kwargs):
115
124
    def missing(self, path):
116
125
        pass
117
126
 
 
127
 
118
128
class ReportCommitToLog(NullCommitReporter):
119
129
 
120
130
    def snapshot_change(self, change, path):
132
142
    def missing(self, path):
133
143
        note('missing %s', path)
134
144
 
 
145
 
135
146
class Commit(object):
136
147
    """Task of committing a new revision.
137
148
 
145
156
            working inventory.
146
157
    """
147
158
    def __init__(self,
148
 
                 reporter=None):
 
159
                 reporter=None,
 
160
                 config=None):
149
161
        if reporter is not None:
150
162
            self.reporter = reporter
151
163
        else:
152
164
            self.reporter = NullCommitReporter()
153
 
 
 
165
        if config is not None:
 
166
            self.config = config
 
167
        else:
 
168
            self.config = None
154
169
        
155
170
    def commit(self,
156
171
               branch, message,
160
175
               specific_files=None,
161
176
               rev_id=None,
162
177
               allow_pointless=True,
 
178
               strict=False,
163
179
               verbose=False,
164
180
               revprops=None):
165
181
        """Commit working copy as a new revision.
178
194
        allow_pointless -- If true (default), commit even if nothing
179
195
            has changed and no merges are recorded.
180
196
 
 
197
        strict -- If true, don't allow a commit if the working tree
 
198
            contains unknown files.
 
199
 
181
200
        revprops -- Properties for new revision
182
201
        """
183
202
        mutter('preparing to commit')
187
206
        self.rev_id = rev_id
188
207
        self.specific_files = specific_files
189
208
        self.allow_pointless = allow_pointless
190
 
        self.revprops = revprops
 
209
        self.revprops = {'branch-nick': branch.nick}
 
210
        if revprops:
 
211
            self.revprops.update(revprops)
 
212
        self.work_tree = WorkingTree(branch.base, branch)
 
213
 
 
214
        if strict:
 
215
            # raise an exception as soon as we find a single unknown.
 
216
            for unknown in self.work_tree.unknowns():
 
217
                raise StrictCommitFailed()
191
218
 
192
219
        if timestamp is None:
193
220
            self.timestamp = time.time()
194
221
        else:
195
222
            self.timestamp = long(timestamp)
196
223
            
 
224
        if self.config is None:
 
225
            self.config = bzrlib.config.BranchConfig(self.branch)
 
226
 
197
227
        if rev_id is None:
198
 
            self.rev_id = _gen_revision_id(self.branch, self.timestamp)
 
228
            self.rev_id = _gen_revision_id(self.config, self.timestamp)
199
229
        else:
200
230
            self.rev_id = rev_id
201
231
 
202
232
        if committer is None:
203
 
            self.committer = bzrlib.config.username(self.branch)
 
233
            self.committer = self.config.username()
204
234
        else:
205
235
            assert isinstance(committer, basestring), type(committer)
206
236
            self.committer = committer
210
240
        else:
211
241
            self.timezone = int(timezone)
212
242
 
213
 
        assert isinstance(message, basestring), type(message)
 
243
        if isinstance(message, str):
 
244
            message = message.decode(bzrlib.user_encoding)
 
245
        assert isinstance(message, unicode), type(message)
214
246
        self.message = message
215
247
        self._escape_commit_message()
216
248
 
217
249
        self.branch.lock_write()
218
250
        try:
219
 
            self.work_tree = self.branch.working_tree()
220
251
            self.work_inv = self.work_tree.inventory
221
252
            self.basis_tree = self.branch.basis_tree()
222
253
            self.basis_inv = self.basis_tree.inventory
241
272
 
242
273
            self._record_inventory()
243
274
            self._make_revision()
 
275
            self.work_tree.set_pending_merges([])
 
276
            self.branch.append_revision(self.rev_id)
244
277
            self.reporter.completed(self.branch.revno()+1, self.rev_id)
245
 
            self.branch.append_revision(self.rev_id)
246
 
            self.branch.set_pending_merges([])
 
278
            if self.config.post_commit() is not None:
 
279
                hooks = self.config.post_commit().split(' ')
 
280
                # this would be nicer with twisted.python.reflect.namedAny
 
281
                for hook in hooks:
 
282
                    result = eval(hook + '(branch, rev_id)',
 
283
                                  {'branch':self.branch,
 
284
                                   'bzrlib':bzrlib,
 
285
                                   'rev_id':self.rev_id})
247
286
        finally:
248
287
            self.branch.unlock()
249
288
 
262
301
        # represented in well-formed XML; escape characters that
263
302
        # aren't listed in the XML specification
264
303
        # (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
304
        self.message, escape_count = re.subn(
272
 
            char_pattern,
 
305
            u'[^\x09\x0A\x0D\u0020-\uD7FF\uE000-\uFFFD]+',
273
306
            lambda match: match.group(0).encode('unicode_escape'),
274
307
            self.message)
275
308
        if escape_count:
277
310
 
278
311
    def _gather_parents(self):
279
312
        """Record the parents of a merge for merge detection."""
280
 
        pending_merges = self.branch.pending_merges()
 
313
        pending_merges = self.work_tree.pending_merges()
281
314
        self.parents = []
282
315
        self.parent_invs = []
283
316
        self.present_parents = []
313
346
        rev_tmp = StringIO()
314
347
        serializer_v5.write_revision(self.rev, rev_tmp)
315
348
        rev_tmp.seek(0)
 
349
        if self.config.signature_needed():
 
350
            plaintext = Testament(self.rev, self.new_inv).as_short_text()
 
351
            self.branch.store_revision_signature(gpg.GPGStrategy(self.config),
 
352
                                                 plaintext, self.rev_id)
316
353
        self.branch.revision_store.add(rev_tmp, self.rev_id)
317
354
        mutter('new revision_id is {%s}', self.rev_id)
318
355
 
339
376
            deleted_ids.sort(reverse=True)
340
377
            for path, file_id in deleted_ids:
341
378
                del self.work_inv[file_id]
342
 
            self.branch._write_inventory(self.work_inv)
 
379
            self.work_tree._write_inventory(self.work_inv)
343
380
 
344
381
    def _store_snapshot(self):
345
382
        """Pass over inventory and record a snapshot.
354
391
        for path, ie in self.new_inv.iter_entries():
355
392
            previous_entries = ie.find_previous_heads(
356
393
                self.parent_invs, 
357
 
                self.weave_store.get_weave_or_empty(ie.file_id,
 
394
                self.weave_store.get_weave_prelude_or_empty(ie.file_id,
358
395
                    self.branch.get_transaction()))
359
396
            if ie.revision is None:
360
397
                change = ie.snapshot(self.rev_id, path, previous_entries,
419
456
            if file_id not in self.new_inv:
420
457
                self.reporter.deleted(self.basis_inv.id2path(file_id))
421
458
 
422
 
def _gen_revision_id(branch, when):
 
459
def _gen_revision_id(config, when):
423
460
    """Return new revision-id."""
424
 
    s = '%s-%s-' % (bzrlib.config.user_email(branch), compact_date(when))
 
461
    s = '%s-%s-' % (config.user_email(), compact_date(when))
425
462
    s += hexlify(rand_bytes(8))
426
463
    return s