~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_commit.py

add a clean target

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 TestCaseInTempDir
22
 
from bzrlib.branch import Branch
23
 
from bzrlib.workingtree import WorkingTree
24
 
from bzrlib.commit import Commit
25
 
from bzrlib.config import BranchConfig
26
 
from bzrlib.errors import PointlessCommit, BzrError, SigningFailed
27
 
 
28
 
 
29
 
# TODO: Test commit with some added, and added-but-missing files
30
 
 
31
 
class MustSignConfig(BranchConfig):
32
 
 
33
 
    def signature_needed(self):
34
 
        return True
35
 
 
36
 
    def gpg_signing_command(self):
37
 
        return ['cat', '-']
38
 
 
39
 
 
40
 
class BranchWithHooks(BranchConfig):
41
 
 
42
 
    def post_commit(self):
43
 
        return "bzrlib.ahook bzrlib.ahook"
44
 
 
45
 
 
46
 
class TestCommit(TestCaseInTempDir):
47
 
 
48
 
    def test_simple_commit(self):
49
 
        """Commit and check two versions of a single file."""
50
 
        b = Branch.initialize(u'.')
51
 
        file('hello', 'w').write('hello world')
52
 
        b.working_tree().add('hello')
53
 
        b.working_tree().commit(message='add hello')
54
 
        file_id = b.working_tree().path2id('hello')
55
 
 
56
 
        file('hello', 'w').write('version 2')
57
 
        b.working_tree().commit(message='commit 2')
58
 
 
59
 
        eq = self.assertEquals
60
 
        eq(b.revno(), 2)
61
 
        rh = b.revision_history()
62
 
        rev = b.repository.get_revision(rh[0])
63
 
        eq(rev.message, 'add hello')
64
 
 
65
 
        tree1 = b.repository.revision_tree(rh[0])
66
 
        text = tree1.get_file_text(file_id)
67
 
        eq(text, 'hello world')
68
 
 
69
 
        tree2 = b.repository.revision_tree(rh[1])
70
 
        eq(tree2.get_file_text(file_id), 'version 2')
71
 
 
72
 
    def test_delete_commit(self):
73
 
        """Test a commit with a deleted file"""
74
 
        b = Branch.initialize(u'.')
75
 
        file('hello', 'w').write('hello world')
76
 
        b.working_tree().add(['hello'], ['hello-id'])
77
 
        b.working_tree().commit(message='add hello')
78
 
 
79
 
        os.remove('hello')
80
 
        b.working_tree().commit('removed hello', rev_id='rev2')
81
 
 
82
 
        tree = b.repository.revision_tree('rev2')
83
 
        self.assertFalse(tree.has_id('hello-id'))
84
 
 
85
 
    def test_pointless_commit(self):
86
 
        """Commit refuses unless there are changes or it's forced."""
87
 
        b = Branch.initialize(u'.')
88
 
        file('hello', 'w').write('hello')
89
 
        b.working_tree().add(['hello'])
90
 
        b.working_tree().commit(message='add hello')
91
 
        self.assertEquals(b.revno(), 1)
92
 
        self.assertRaises(PointlessCommit,
93
 
                          b.working_tree().commit,
94
 
                          message='fails',
95
 
                          allow_pointless=False)
96
 
        self.assertEquals(b.revno(), 1)
97
 
        
98
 
    def test_commit_empty(self):
99
 
        """Commiting an empty tree works."""
100
 
        b = Branch.initialize(u'.')
101
 
        b.working_tree().commit(message='empty tree', allow_pointless=True)
102
 
        self.assertRaises(PointlessCommit,
103
 
                          b.working_tree().commit,
104
 
                          message='empty tree',
105
 
                          allow_pointless=False)
106
 
        b.working_tree().commit(message='empty tree', allow_pointless=True)
107
 
        self.assertEquals(b.revno(), 2)
108
 
 
109
 
 
110
 
    def test_selective_delete(self):
111
 
        """Selective commit in tree with deletions"""
112
 
        b = Branch.initialize(u'.')
113
 
        file('hello', 'w').write('hello')
114
 
        file('buongia', 'w').write('buongia')
115
 
        b.working_tree().add(['hello', 'buongia'],
116
 
              ['hello-id', 'buongia-id'])
117
 
        b.working_tree().commit(message='add files',
118
 
                 rev_id='test@rev-1')
119
 
        
120
 
        os.remove('hello')
121
 
        file('buongia', 'w').write('new text')
122
 
        b.working_tree().commit(message='update text',
123
 
                 specific_files=['buongia'],
124
 
                 allow_pointless=False,
125
 
                 rev_id='test@rev-2')
126
 
 
127
 
        b.working_tree().commit(message='remove hello',
128
 
                 specific_files=['hello'],
129
 
                 allow_pointless=False,
130
 
                 rev_id='test@rev-3')
131
 
 
132
 
        eq = self.assertEquals
133
 
        eq(b.revno(), 3)
134
 
 
135
 
        tree2 = b.repository.revision_tree('test@rev-2')
136
 
        self.assertTrue(tree2.has_filename('hello'))
137
 
        self.assertEquals(tree2.get_file_text('hello-id'), 'hello')
138
 
        self.assertEquals(tree2.get_file_text('buongia-id'), 'new text')
139
 
        
140
 
        tree3 = b.repository.revision_tree('test@rev-3')
141
 
        self.assertFalse(tree3.has_filename('hello'))
142
 
        self.assertEquals(tree3.get_file_text('buongia-id'), 'new text')
143
 
 
144
 
 
145
 
    def test_commit_rename(self):
146
 
        """Test commit of a revision where a file is renamed."""
147
 
        b = Branch.initialize(u'.')
148
 
        tree = WorkingTree(u'.', b)
149
 
        self.build_tree(['hello'], line_endings='binary')
150
 
        tree.add(['hello'], ['hello-id'])
151
 
        tree.commit(message='one', rev_id='test@rev-1', allow_pointless=False)
152
 
 
153
 
        tree.rename_one('hello', 'fruity')
154
 
        tree.commit(message='renamed', rev_id='test@rev-2', allow_pointless=False)
155
 
 
156
 
        eq = self.assertEquals
157
 
        tree1 = b.repository.revision_tree('test@rev-1')
158
 
        eq(tree1.id2path('hello-id'), 'hello')
159
 
        eq(tree1.get_file_text('hello-id'), 'contents of hello\n')
160
 
        self.assertFalse(tree1.has_filename('fruity'))
161
 
        self.check_inventory_shape(tree1.inventory, ['hello'])
162
 
        ie = tree1.inventory['hello-id']
163
 
        eq(ie.revision, 'test@rev-1')
164
 
 
165
 
        tree2 = b.repository.revision_tree('test@rev-2')
166
 
        eq(tree2.id2path('hello-id'), 'fruity')
167
 
        eq(tree2.get_file_text('hello-id'), 'contents of hello\n')
168
 
        self.check_inventory_shape(tree2.inventory, ['fruity'])
169
 
        ie = tree2.inventory['hello-id']
170
 
        eq(ie.revision, 'test@rev-2')
171
 
 
172
 
    def test_reused_rev_id(self):
173
 
        """Test that a revision id cannot be reused in a branch"""
174
 
        b = Branch.initialize(u'.')
175
 
        b.working_tree().commit('initial', rev_id='test@rev-1', allow_pointless=True)
176
 
        self.assertRaises(Exception,
177
 
                          b.working_tree().commit,
178
 
                          message='reused id',
179
 
                          rev_id='test@rev-1',
180
 
                          allow_pointless=True)
181
 
 
182
 
    def test_commit_move(self):
183
 
        """Test commit of revisions with moved files and directories"""
184
 
        eq = self.assertEquals
185
 
        b = Branch.initialize(u'.')
186
 
        r1 = 'test@rev-1'
187
 
        self.build_tree(['hello', 'a/', 'b/'])
188
 
        b.working_tree().add(['hello', 'a', 'b'], ['hello-id', 'a-id', 'b-id'])
189
 
        b.working_tree().commit('initial', rev_id=r1, allow_pointless=False)
190
 
        b.working_tree().move(['hello'], 'a')
191
 
        r2 = 'test@rev-2'
192
 
        b.working_tree().commit('two', rev_id=r2, allow_pointless=False)
193
 
        self.check_inventory_shape(b.working_tree().read_working_inventory(),
194
 
                                   ['a', 'a/hello', 'b'])
195
 
 
196
 
        b.working_tree().move(['b'], 'a')
197
 
        r3 = 'test@rev-3'
198
 
        b.working_tree().commit('three', rev_id=r3, allow_pointless=False)
199
 
        self.check_inventory_shape(b.working_tree().read_working_inventory(),
200
 
                                   ['a', 'a/hello', 'a/b'])
201
 
        self.check_inventory_shape(b.repository.get_revision_inventory(r3),
202
 
                                   ['a', 'a/hello', 'a/b'])
203
 
 
204
 
        b.working_tree().move(['a/hello'], 'a/b')
205
 
        r4 = 'test@rev-4'
206
 
        b.working_tree().commit('four', rev_id=r4, allow_pointless=False)
207
 
        self.check_inventory_shape(b.working_tree().read_working_inventory(),
208
 
                                   ['a', 'a/b/hello', 'a/b'])
209
 
 
210
 
        inv = b.repository.get_revision_inventory(r4)
211
 
        eq(inv['hello-id'].revision, r4)
212
 
        eq(inv['a-id'].revision, r1)
213
 
        eq(inv['b-id'].revision, r3)
214
 
        
215
 
    def test_removed_commit(self):
216
 
        """Commit with a removed file"""
217
 
        b = Branch.initialize(u'.')
218
 
        wt = b.working_tree()
219
 
        file('hello', 'w').write('hello world')
220
 
        b.working_tree().add(['hello'], ['hello-id'])
221
 
        b.working_tree().commit(message='add hello')
222
 
 
223
 
        wt = b.working_tree()  # FIXME: kludge for aliasing of working inventory
224
 
        wt.remove('hello')
225
 
        b.working_tree().commit('removed hello', rev_id='rev2')
226
 
 
227
 
        tree = b.repository.revision_tree('rev2')
228
 
        self.assertFalse(tree.has_id('hello-id'))
229
 
 
230
 
 
231
 
    def test_committed_ancestry(self):
232
 
        """Test commit appends revisions to ancestry."""
233
 
        b = Branch.initialize(u'.')
234
 
        rev_ids = []
235
 
        for i in range(4):
236
 
            file('hello', 'w').write((str(i) * 4) + '\n')
237
 
            if i == 0:
238
 
                b.working_tree().add(['hello'], ['hello-id'])
239
 
            rev_id = 'test@rev-%d' % (i+1)
240
 
            rev_ids.append(rev_id)
241
 
            b.working_tree().commit(message='rev %d' % (i+1),
242
 
                     rev_id=rev_id)
243
 
        eq = self.assertEquals
244
 
        eq(b.revision_history(), rev_ids)
245
 
        for i in range(4):
246
 
            anc = b.repository.get_ancestry(rev_ids[i])
247
 
            eq(anc, [None] + rev_ids[:i+1])
248
 
 
249
 
    def test_commit_new_subdir_child_selective(self):
250
 
        b = Branch.initialize(u'.')
251
 
        self.build_tree(['dir/', 'dir/file1', 'dir/file2'])
252
 
        b.working_tree().add(['dir', 'dir/file1', 'dir/file2'],
253
 
              ['dirid', 'file1id', 'file2id'])
254
 
        b.working_tree().commit('dir/file1', specific_files=['dir/file1'], rev_id='1')
255
 
        inv = b.repository.get_inventory('1')
256
 
        self.assertEqual('1', inv['dirid'].revision)
257
 
        self.assertEqual('1', inv['file1id'].revision)
258
 
        # FIXME: This should raise a KeyError I think, rbc20051006
259
 
        self.assertRaises(BzrError, inv.__getitem__, 'file2id')
260
 
 
261
 
    def test_strict_commit(self):
262
 
        """Try and commit with unknown files and strict = True, should fail."""
263
 
        from bzrlib.errors import StrictCommitFailed
264
 
        b = Branch.initialize(u'.')
265
 
        file('hello', 'w').write('hello world')
266
 
        b.working_tree().add('hello')
267
 
        file('goodbye', 'w').write('goodbye cruel world!')
268
 
        self.assertRaises(StrictCommitFailed, b.working_tree().commit,
269
 
            message='add hello but not goodbye', strict=True)
270
 
 
271
 
    def test_strict_commit_without_unknowns(self):
272
 
        """Try and commit with no unknown files and strict = True,
273
 
        should work."""
274
 
        from bzrlib.errors import StrictCommitFailed
275
 
        b = Branch.initialize(u'.')
276
 
        file('hello', 'w').write('hello world')
277
 
        b.working_tree().add('hello')
278
 
        b.working_tree().commit(message='add hello', strict=True)
279
 
 
280
 
    def test_nonstrict_commit(self):
281
 
        """Try and commit with unknown files and strict = False, should work."""
282
 
        b = Branch.initialize(u'.')
283
 
        file('hello', 'w').write('hello world')
284
 
        b.working_tree().add('hello')
285
 
        file('goodbye', 'w').write('goodbye cruel world!')
286
 
        b.working_tree().commit(message='add hello but not goodbye', strict=False)
287
 
 
288
 
    def test_nonstrict_commit_without_unknowns(self):
289
 
        """Try and commit with no unknown files and strict = False,
290
 
        should work."""
291
 
        b = Branch.initialize(u'.')
292
 
        file('hello', 'w').write('hello world')
293
 
        b.working_tree().add('hello')
294
 
        b.working_tree().commit(message='add hello', strict=False)
295
 
 
296
 
    def test_signed_commit(self):
297
 
        import bzrlib.gpg
298
 
        import bzrlib.commit as commit
299
 
        oldstrategy = bzrlib.gpg.GPGStrategy
300
 
        branch = Branch.initialize(u'.')
301
 
        branch.working_tree().commit("base", allow_pointless=True, rev_id='A')
302
 
        self.failIf(branch.repository.revision_store.has_id('A', 'sig'))
303
 
        try:
304
 
            from bzrlib.testament import Testament
305
 
            # monkey patch gpg signing mechanism
306
 
            bzrlib.gpg.GPGStrategy = bzrlib.gpg.LoopbackGPGStrategy
307
 
            commit.Commit(config=MustSignConfig(branch)).commit(branch, "base",
308
 
                                                      allow_pointless=True,
309
 
                                                      rev_id='B')
310
 
            self.assertEqual(Testament.from_revision(branch.repository,
311
 
                             'B').as_short_text(),
312
 
                             branch.repository.revision_store.get('B', 
313
 
                                                               'sig').read())
314
 
        finally:
315
 
            bzrlib.gpg.GPGStrategy = oldstrategy
316
 
 
317
 
    def test_commit_failed_signature(self):
318
 
        import bzrlib.gpg
319
 
        import bzrlib.commit as commit
320
 
        oldstrategy = bzrlib.gpg.GPGStrategy
321
 
        branch = Branch.initialize(u'.')
322
 
        branch.working_tree().commit("base", allow_pointless=True, rev_id='A')
323
 
        self.failIf(branch.repository.revision_store.has_id('A', 'sig'))
324
 
        try:
325
 
            from bzrlib.testament import Testament
326
 
            # monkey patch gpg signing mechanism
327
 
            bzrlib.gpg.GPGStrategy = bzrlib.gpg.DisabledGPGStrategy
328
 
            config = MustSignConfig(branch)
329
 
            self.assertRaises(SigningFailed,
330
 
                              commit.Commit(config=config).commit,
331
 
                              branch, "base",
332
 
                              allow_pointless=True,
333
 
                              rev_id='B')
334
 
            branch = Branch.open(u'.')
335
 
            self.assertEqual(branch.revision_history(), ['A'])
336
 
            self.failIf(branch.repository.revision_store.has_id('B'))
337
 
        finally:
338
 
            bzrlib.gpg.GPGStrategy = oldstrategy
339
 
 
340
 
    def test_commit_invokes_hooks(self):
341
 
        import bzrlib.commit as commit
342
 
        branch = Branch.initialize(u'.')
343
 
        calls = []
344
 
        def called(branch, rev_id):
345
 
            calls.append('called')
346
 
        bzrlib.ahook = called
347
 
        try:
348
 
            config = BranchWithHooks(branch)
349
 
            commit.Commit(config=config).commit(
350
 
                            branch, "base",
351
 
                            allow_pointless=True,
352
 
                            rev_id='A')
353
 
            self.assertEqual(['called', 'called'], calls)
354
 
        finally:
355
 
            del bzrlib.ahook