~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commit.py

  • Committer: Robert Collins
  • Date: 2005-12-24 02:20:45 UTC
  • mto: (1185.50.57 bzr-jam-integration)
  • mto: This revision was merged to the branch mainline in revision 1550.
  • Revision ID: robertc@robertcollins.net-20051224022045-14efc8dfa0e1a4e9
Start tests for api usage.

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
                           )
80
87
import bzrlib.gpg as gpg
81
88
from bzrlib.revision import Revision
 
89
from bzrlib.testament import Testament
82
90
from bzrlib.trace import mutter, note, warning
83
91
from bzrlib.xml5 import serializer_v5
84
92
from bzrlib.inventory import Inventory, ROOT_ID
85
93
from bzrlib.weave import Weave
86
94
from bzrlib.weavefile import read_weave, write_weave_v5
87
 
from bzrlib.atomicfile import AtomicFile
 
95
from bzrlib.workingtree import WorkingTree
88
96
 
89
97
 
90
98
def commit(*args, **kwargs):
116
124
    def missing(self, path):
117
125
        pass
118
126
 
 
127
 
119
128
class ReportCommitToLog(NullCommitReporter):
120
129
 
121
130
    def snapshot_change(self, change, path):
133
142
    def missing(self, path):
134
143
        note('missing %s', path)
135
144
 
 
145
 
136
146
class Commit(object):
137
147
    """Task of committing a new revision.
138
148
 
165
175
               specific_files=None,
166
176
               rev_id=None,
167
177
               allow_pointless=True,
 
178
               strict=False,
168
179
               verbose=False,
169
180
               revprops=None):
170
181
        """Commit working copy as a new revision.
183
194
        allow_pointless -- If true (default), commit even if nothing
184
195
            has changed and no merges are recorded.
185
196
 
 
197
        strict -- If true, don't allow a commit if the working tree
 
198
            contains unknown files.
 
199
 
186
200
        revprops -- Properties for new revision
187
201
        """
188
202
        mutter('preparing to commit')
192
206
        self.rev_id = rev_id
193
207
        self.specific_files = specific_files
194
208
        self.allow_pointless = allow_pointless
195
 
        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()
196
218
 
197
219
        if timestamp is None:
198
220
            self.timestamp = time.time()
218
240
        else:
219
241
            self.timezone = int(timezone)
220
242
 
221
 
        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)
222
246
        self.message = message
223
247
        self._escape_commit_message()
224
248
 
225
249
        self.branch.lock_write()
226
250
        try:
227
 
            self.work_tree = self.branch.working_tree()
228
251
            self.work_inv = self.work_tree.inventory
229
252
            self.basis_tree = self.branch.basis_tree()
230
253
            self.basis_inv = self.basis_tree.inventory
249
272
 
250
273
            self._record_inventory()
251
274
            self._make_revision()
 
275
            self.work_tree.set_pending_merges([])
 
276
            self.branch.append_revision(self.rev_id)
252
277
            self.reporter.completed(self.branch.revno()+1, self.rev_id)
253
 
            self.branch.append_revision(self.rev_id)
254
 
            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})
255
286
        finally:
256
287
            self.branch.unlock()
257
288
 
270
301
        # represented in well-formed XML; escape characters that
271
302
        # aren't listed in the XML specification
272
303
        # (http://www.w3.org/TR/REC-xml/#NT-Char).
273
 
        if isinstance(self.message, unicode):
274
 
            char_pattern = u'[^\x09\x0A\x0D\u0020-\uD7FF\uE000-\uFFFD]'
275
 
        else:
276
 
            # Use a regular 'str' as pattern to avoid having re.subn
277
 
            # return 'unicode' results.
278
 
            char_pattern = '[^x09\x0A\x0D\x20-\xFF]'
279
304
        self.message, escape_count = re.subn(
280
 
            char_pattern,
 
305
            u'[^\x09\x0A\x0D\u0020-\uD7FF\uE000-\uFFFD]+',
281
306
            lambda match: match.group(0).encode('unicode_escape'),
282
307
            self.message)
283
308
        if escape_count:
285
310
 
286
311
    def _gather_parents(self):
287
312
        """Record the parents of a merge for merge detection."""
288
 
        pending_merges = self.branch.pending_merges()
 
313
        pending_merges = self.work_tree.pending_merges()
289
314
        self.parents = []
290
315
        self.parent_invs = []
291
316
        self.present_parents = []
321
346
        rev_tmp = StringIO()
322
347
        serializer_v5.write_revision(self.rev, rev_tmp)
323
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)
324
353
        self.branch.revision_store.add(rev_tmp, self.rev_id)
325
 
        if self.config.signature_needed():
326
 
            self.branch.sign_revision(self.rev_id, gpg.GPGStrategy(self.config))
327
354
        mutter('new revision_id is {%s}', self.rev_id)
328
355
 
329
356
    def _remove_deleted(self):
349
376
            deleted_ids.sort(reverse=True)
350
377
            for path, file_id in deleted_ids:
351
378
                del self.work_inv[file_id]
352
 
            self.branch._write_inventory(self.work_inv)
 
379
            self.work_tree._write_inventory(self.work_inv)
353
380
 
354
381
    def _store_snapshot(self):
355
382
        """Pass over inventory and record a snapshot.
364
391
        for path, ie in self.new_inv.iter_entries():
365
392
            previous_entries = ie.find_previous_heads(
366
393
                self.parent_invs, 
367
 
                self.weave_store.get_weave_or_empty(ie.file_id,
 
394
                self.weave_store.get_weave_prelude_or_empty(ie.file_id,
368
395
                    self.branch.get_transaction()))
369
396
            if ie.revision is None:
370
397
                change = ie.snapshot(self.rev_id, path, previous_entries,