~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_commit.py

  • Committer: Martin Pool
  • Date: 2005-05-03 07:48:54 UTC
  • Revision ID: mbp@sourcefrog.net-20050503074854-adb6f9d6382e27a9
- sketchy experiments in bash and zsh completion

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 by Canonical Ltd
2
 
 
3
 
# This program is free software; you can redistribute it and/or modify
4
 
# it under the terms of the GNU General Public License as published by
5
 
# the Free Software Foundation; either version 2 of the License, or
6
 
# (at your option) any later version.
7
 
 
8
 
# This program is distributed in the hope that it will be useful,
9
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
 
# GNU General Public License for more details.
12
 
 
13
 
# You should have received a copy of the GNU General Public License
14
 
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
 
 
17
 
 
18
 
import os
19
 
 
20
 
import bzrlib
21
 
from bzrlib.tests import TestCaseWithTransport
22
 
from bzrlib.branch import Branch
23
 
from bzrlib.bzrdir import BzrDir, BzrDirMetaFormat1
24
 
from bzrlib.workingtree import WorkingTree
25
 
from bzrlib.commit import Commit
26
 
from bzrlib.config import BranchConfig
27
 
from bzrlib.errors import (PointlessCommit, BzrError, SigningFailed, 
28
 
                           LockContention)
29
 
 
30
 
 
31
 
# TODO: Test commit with some added, and added-but-missing files
32
 
 
33
 
class MustSignConfig(BranchConfig):
34
 
 
35
 
    def signature_needed(self):
36
 
        return True
37
 
 
38
 
    def gpg_signing_command(self):
39
 
        return ['cat', '-']
40
 
 
41
 
 
42
 
class BranchWithHooks(BranchConfig):
43
 
 
44
 
    def post_commit(self):
45
 
        return "bzrlib.ahook bzrlib.ahook"
46
 
 
47
 
 
48
 
class TestCommit(TestCaseWithTransport):
49
 
 
50
 
    def test_simple_commit(self):
51
 
        """Commit and check two versions of a single file."""
52
 
        wt = self.make_branch_and_tree('.')
53
 
        b = wt.branch
54
 
        file('hello', 'w').write('hello world')
55
 
        wt.add('hello')
56
 
        wt.commit(message='add hello')
57
 
        file_id = wt.path2id('hello')
58
 
 
59
 
        file('hello', 'w').write('version 2')
60
 
        wt.commit(message='commit 2')
61
 
 
62
 
        eq = self.assertEquals
63
 
        eq(b.revno(), 2)
64
 
        rh = b.revision_history()
65
 
        rev = b.repository.get_revision(rh[0])
66
 
        eq(rev.message, 'add hello')
67
 
 
68
 
        tree1 = b.repository.revision_tree(rh[0])
69
 
        text = tree1.get_file_text(file_id)
70
 
        eq(text, 'hello world')
71
 
 
72
 
        tree2 = b.repository.revision_tree(rh[1])
73
 
        eq(tree2.get_file_text(file_id), 'version 2')
74
 
 
75
 
    def test_delete_commit(self):
76
 
        """Test a commit with a deleted file"""
77
 
        wt = self.make_branch_and_tree('.')
78
 
        b = wt.branch
79
 
        file('hello', 'w').write('hello world')
80
 
        wt.add(['hello'], ['hello-id'])
81
 
        wt.commit(message='add hello')
82
 
 
83
 
        os.remove('hello')
84
 
        wt.commit('removed hello', rev_id='rev2')
85
 
 
86
 
        tree = b.repository.revision_tree('rev2')
87
 
        self.assertFalse(tree.has_id('hello-id'))
88
 
 
89
 
    def test_pointless_commit(self):
90
 
        """Commit refuses unless there are changes or it's forced."""
91
 
        wt = self.make_branch_and_tree('.')
92
 
        b = wt.branch
93
 
        file('hello', 'w').write('hello')
94
 
        wt.add(['hello'])
95
 
        wt.commit(message='add hello')
96
 
        self.assertEquals(b.revno(), 1)
97
 
        self.assertRaises(PointlessCommit,
98
 
                          wt.commit,
99
 
                          message='fails',
100
 
                          allow_pointless=False)
101
 
        self.assertEquals(b.revno(), 1)
102
 
        
103
 
    def test_commit_empty(self):
104
 
        """Commiting an empty tree works."""
105
 
        wt = self.make_branch_and_tree('.')
106
 
        b = wt.branch
107
 
        wt.commit(message='empty tree', allow_pointless=True)
108
 
        self.assertRaises(PointlessCommit,
109
 
                          wt.commit,
110
 
                          message='empty tree',
111
 
                          allow_pointless=False)
112
 
        wt.commit(message='empty tree', allow_pointless=True)
113
 
        self.assertEquals(b.revno(), 2)
114
 
 
115
 
    def test_selective_delete(self):
116
 
        """Selective commit in tree with deletions"""
117
 
        wt = self.make_branch_and_tree('.')
118
 
        b = wt.branch
119
 
        file('hello', 'w').write('hello')
120
 
        file('buongia', 'w').write('buongia')
121
 
        wt.add(['hello', 'buongia'],
122
 
              ['hello-id', 'buongia-id'])
123
 
        wt.commit(message='add files',
124
 
                 rev_id='test@rev-1')
125
 
        
126
 
        os.remove('hello')
127
 
        file('buongia', 'w').write('new text')
128
 
        wt.commit(message='update text',
129
 
                 specific_files=['buongia'],
130
 
                 allow_pointless=False,
131
 
                 rev_id='test@rev-2')
132
 
 
133
 
        wt.commit(message='remove hello',
134
 
                 specific_files=['hello'],
135
 
                 allow_pointless=False,
136
 
                 rev_id='test@rev-3')
137
 
 
138
 
        eq = self.assertEquals
139
 
        eq(b.revno(), 3)
140
 
 
141
 
        tree2 = b.repository.revision_tree('test@rev-2')
142
 
        self.assertTrue(tree2.has_filename('hello'))
143
 
        self.assertEquals(tree2.get_file_text('hello-id'), 'hello')
144
 
        self.assertEquals(tree2.get_file_text('buongia-id'), 'new text')
145
 
        
146
 
        tree3 = b.repository.revision_tree('test@rev-3')
147
 
        self.assertFalse(tree3.has_filename('hello'))
148
 
        self.assertEquals(tree3.get_file_text('buongia-id'), 'new text')
149
 
 
150
 
    def test_commit_rename(self):
151
 
        """Test commit of a revision where a file is renamed."""
152
 
        tree = self.make_branch_and_tree('.')
153
 
        b = tree.branch
154
 
        self.build_tree(['hello'], line_endings='binary')
155
 
        tree.add(['hello'], ['hello-id'])
156
 
        tree.commit(message='one', rev_id='test@rev-1', allow_pointless=False)
157
 
 
158
 
        tree.rename_one('hello', 'fruity')
159
 
        tree.commit(message='renamed', rev_id='test@rev-2', allow_pointless=False)
160
 
 
161
 
        eq = self.assertEquals
162
 
        tree1 = b.repository.revision_tree('test@rev-1')
163
 
        eq(tree1.id2path('hello-id'), 'hello')
164
 
        eq(tree1.get_file_text('hello-id'), 'contents of hello\n')
165
 
        self.assertFalse(tree1.has_filename('fruity'))
166
 
        self.check_inventory_shape(tree1.inventory, ['hello'])
167
 
        ie = tree1.inventory['hello-id']
168
 
        eq(ie.revision, 'test@rev-1')
169
 
 
170
 
        tree2 = b.repository.revision_tree('test@rev-2')
171
 
        eq(tree2.id2path('hello-id'), 'fruity')
172
 
        eq(tree2.get_file_text('hello-id'), 'contents of hello\n')
173
 
        self.check_inventory_shape(tree2.inventory, ['fruity'])
174
 
        ie = tree2.inventory['hello-id']
175
 
        eq(ie.revision, 'test@rev-2')
176
 
 
177
 
    def test_reused_rev_id(self):
178
 
        """Test that a revision id cannot be reused in a branch"""
179
 
        wt = self.make_branch_and_tree('.')
180
 
        b = wt.branch
181
 
        wt.commit('initial', rev_id='test@rev-1', allow_pointless=True)
182
 
        self.assertRaises(Exception,
183
 
                          wt.commit,
184
 
                          message='reused id',
185
 
                          rev_id='test@rev-1',
186
 
                          allow_pointless=True)
187
 
 
188
 
    def test_commit_move(self):
189
 
        """Test commit of revisions with moved files and directories"""
190
 
        eq = self.assertEquals
191
 
        wt = self.make_branch_and_tree('.')
192
 
        b = wt.branch
193
 
        r1 = 'test@rev-1'
194
 
        self.build_tree(['hello', 'a/', 'b/'])
195
 
        wt.add(['hello', 'a', 'b'], ['hello-id', 'a-id', 'b-id'])
196
 
        wt.commit('initial', rev_id=r1, allow_pointless=False)
197
 
        wt.move(['hello'], 'a')
198
 
        r2 = 'test@rev-2'
199
 
        wt.commit('two', rev_id=r2, allow_pointless=False)
200
 
        self.check_inventory_shape(wt.read_working_inventory(),
201
 
                                   ['a', 'a/hello', 'b'])
202
 
 
203
 
        wt.move(['b'], 'a')
204
 
        r3 = 'test@rev-3'
205
 
        wt.commit('three', rev_id=r3, allow_pointless=False)
206
 
        self.check_inventory_shape(wt.read_working_inventory(),
207
 
                                   ['a', 'a/hello', 'a/b'])
208
 
        self.check_inventory_shape(b.repository.get_revision_inventory(r3),
209
 
                                   ['a', 'a/hello', 'a/b'])
210
 
 
211
 
        wt.move(['a/hello'], 'a/b')
212
 
        r4 = 'test@rev-4'
213
 
        wt.commit('four', rev_id=r4, allow_pointless=False)
214
 
        self.check_inventory_shape(wt.read_working_inventory(),
215
 
                                   ['a', 'a/b/hello', 'a/b'])
216
 
 
217
 
        inv = b.repository.get_revision_inventory(r4)
218
 
        eq(inv['hello-id'].revision, r4)
219
 
        eq(inv['a-id'].revision, r1)
220
 
        eq(inv['b-id'].revision, r3)
221
 
        
222
 
    def test_removed_commit(self):
223
 
        """Commit with a removed file"""
224
 
        wt = self.make_branch_and_tree('.')
225
 
        b = wt.branch
226
 
        file('hello', 'w').write('hello world')
227
 
        wt.add(['hello'], ['hello-id'])
228
 
        wt.commit(message='add hello')
229
 
        wt.remove('hello')
230
 
        wt.commit('removed hello', rev_id='rev2')
231
 
 
232
 
        tree = b.repository.revision_tree('rev2')
233
 
        self.assertFalse(tree.has_id('hello-id'))
234
 
 
235
 
    def test_committed_ancestry(self):
236
 
        """Test commit appends revisions to ancestry."""
237
 
        wt = self.make_branch_and_tree('.')
238
 
        b = wt.branch
239
 
        rev_ids = []
240
 
        for i in range(4):
241
 
            file('hello', 'w').write((str(i) * 4) + '\n')
242
 
            if i == 0:
243
 
                wt.add(['hello'], ['hello-id'])
244
 
            rev_id = 'test@rev-%d' % (i+1)
245
 
            rev_ids.append(rev_id)
246
 
            wt.commit(message='rev %d' % (i+1),
247
 
                     rev_id=rev_id)
248
 
        eq = self.assertEquals
249
 
        eq(b.revision_history(), rev_ids)
250
 
        for i in range(4):
251
 
            anc = b.repository.get_ancestry(rev_ids[i])
252
 
            eq(anc, [None] + rev_ids[:i+1])
253
 
 
254
 
    def test_commit_new_subdir_child_selective(self):
255
 
        wt = self.make_branch_and_tree('.')
256
 
        b = wt.branch
257
 
        self.build_tree(['dir/', 'dir/file1', 'dir/file2'])
258
 
        wt.add(['dir', 'dir/file1', 'dir/file2'],
259
 
              ['dirid', 'file1id', 'file2id'])
260
 
        wt.commit('dir/file1', specific_files=['dir/file1'], rev_id='1')
261
 
        inv = b.repository.get_inventory('1')
262
 
        self.assertEqual('1', inv['dirid'].revision)
263
 
        self.assertEqual('1', inv['file1id'].revision)
264
 
        # FIXME: This should raise a KeyError I think, rbc20051006
265
 
        self.assertRaises(BzrError, inv.__getitem__, 'file2id')
266
 
 
267
 
    def test_strict_commit(self):
268
 
        """Try and commit with unknown files and strict = True, should fail."""
269
 
        from bzrlib.errors import StrictCommitFailed
270
 
        wt = self.make_branch_and_tree('.')
271
 
        b = wt.branch
272
 
        file('hello', 'w').write('hello world')
273
 
        wt.add('hello')
274
 
        file('goodbye', 'w').write('goodbye cruel world!')
275
 
        self.assertRaises(StrictCommitFailed, wt.commit,
276
 
            message='add hello but not goodbye', strict=True)
277
 
 
278
 
    def test_strict_commit_without_unknowns(self):
279
 
        """Try and commit with no unknown files and strict = True,
280
 
        should work."""
281
 
        from bzrlib.errors import StrictCommitFailed
282
 
        wt = self.make_branch_and_tree('.')
283
 
        b = wt.branch
284
 
        file('hello', 'w').write('hello world')
285
 
        wt.add('hello')
286
 
        wt.commit(message='add hello', strict=True)
287
 
 
288
 
    def test_nonstrict_commit(self):
289
 
        """Try and commit with unknown files and strict = False, should work."""
290
 
        wt = self.make_branch_and_tree('.')
291
 
        b = wt.branch
292
 
        file('hello', 'w').write('hello world')
293
 
        wt.add('hello')
294
 
        file('goodbye', 'w').write('goodbye cruel world!')
295
 
        wt.commit(message='add hello but not goodbye', strict=False)
296
 
 
297
 
    def test_nonstrict_commit_without_unknowns(self):
298
 
        """Try and commit with no unknown files and strict = False,
299
 
        should work."""
300
 
        wt = self.make_branch_and_tree('.')
301
 
        b = wt.branch
302
 
        file('hello', 'w').write('hello world')
303
 
        wt.add('hello')
304
 
        wt.commit(message='add hello', strict=False)
305
 
 
306
 
    def test_signed_commit(self):
307
 
        import bzrlib.gpg
308
 
        import bzrlib.commit as commit
309
 
        oldstrategy = bzrlib.gpg.GPGStrategy
310
 
        wt = self.make_branch_and_tree('.')
311
 
        branch = wt.branch
312
 
        wt.commit("base", allow_pointless=True, rev_id='A')
313
 
        self.failIf(branch.repository.has_signature_for_revision_id('A'))
314
 
        try:
315
 
            from bzrlib.testament import Testament
316
 
            # monkey patch gpg signing mechanism
317
 
            bzrlib.gpg.GPGStrategy = bzrlib.gpg.LoopbackGPGStrategy
318
 
            commit.Commit(config=MustSignConfig(branch)).commit(message="base",
319
 
                                                      allow_pointless=True,
320
 
                                                      rev_id='B',
321
 
                                                      working_tree=wt)
322
 
            self.assertEqual(Testament.from_revision(branch.repository,
323
 
                             'B').as_short_text(),
324
 
                             branch.repository.get_signature_text('B'))
325
 
        finally:
326
 
            bzrlib.gpg.GPGStrategy = oldstrategy
327
 
 
328
 
    def test_commit_failed_signature(self):
329
 
        import bzrlib.gpg
330
 
        import bzrlib.commit as commit
331
 
        oldstrategy = bzrlib.gpg.GPGStrategy
332
 
        wt = self.make_branch_and_tree('.')
333
 
        branch = wt.branch
334
 
        wt.commit("base", allow_pointless=True, rev_id='A')
335
 
        self.failIf(branch.repository.has_signature_for_revision_id('A'))
336
 
        try:
337
 
            from bzrlib.testament import Testament
338
 
            # monkey patch gpg signing mechanism
339
 
            bzrlib.gpg.GPGStrategy = bzrlib.gpg.DisabledGPGStrategy
340
 
            config = MustSignConfig(branch)
341
 
            self.assertRaises(SigningFailed,
342
 
                              commit.Commit(config=config).commit,
343
 
                              message="base",
344
 
                              allow_pointless=True,
345
 
                              rev_id='B',
346
 
                              working_tree=wt)
347
 
            branch = Branch.open(self.get_url('.'))
348
 
            self.assertEqual(branch.revision_history(), ['A'])
349
 
            self.failIf(branch.repository.has_revision('B'))
350
 
        finally:
351
 
            bzrlib.gpg.GPGStrategy = oldstrategy
352
 
 
353
 
    def test_commit_invokes_hooks(self):
354
 
        import bzrlib.commit as commit
355
 
        wt = self.make_branch_and_tree('.')
356
 
        branch = wt.branch
357
 
        calls = []
358
 
        def called(branch, rev_id):
359
 
            calls.append('called')
360
 
        bzrlib.ahook = called
361
 
        try:
362
 
            config = BranchWithHooks(branch)
363
 
            commit.Commit(config=config).commit(
364
 
                            message = "base",
365
 
                            allow_pointless=True,
366
 
                            rev_id='A', working_tree = wt)
367
 
            self.assertEqual(['called', 'called'], calls)
368
 
        finally:
369
 
            del bzrlib.ahook
370
 
 
371
 
    def test_commit_object_doesnt_set_nick(self):
372
 
        # using the Commit object directly does not set the branch nick.
373
 
        wt = self.make_branch_and_tree('.')
374
 
        c = Commit()
375
 
        c.commit(working_tree=wt, message='empty tree', allow_pointless=True)
376
 
        self.assertEquals(wt.branch.revno(), 1)
377
 
        self.assertEqual({},
378
 
                         wt.branch.repository.get_revision(
379
 
                            wt.branch.last_revision()).properties)
380
 
 
381
 
    def test_safe_master_lock(self):
382
 
        os.mkdir('master')
383
 
        master = BzrDirMetaFormat1().initialize('master')
384
 
        master.create_repository()
385
 
        master_branch = master.create_branch()
386
 
        master.create_workingtree()
387
 
        bound = master.sprout('bound')
388
 
        wt = bound.open_workingtree()
389
 
        wt.branch.set_bound_location(os.path.realpath('master'))
390
 
        master_branch.lock_write()
391
 
        try:
392
 
            self.assertRaises(LockContention, wt.commit, 'silly')
393
 
        finally:
394
 
            master_branch.unlock()