~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/test_commit.py

[merge] fix \t in commit messages

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.selftest 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('.')
 
51
        file('hello', 'w').write('hello world')
 
52
        b.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.get_revision(rh[0])
 
63
        eq(rev.message, 'add hello')
 
64
 
 
65
        tree1 = b.revision_tree(rh[0])
 
66
        text = tree1.get_file_text(file_id)
 
67
        eq(text, 'hello world')
 
68
 
 
69
        tree2 = b.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('.')
 
75
        file('hello', 'w').write('hello world')
 
76
        b.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.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('.')
 
88
        file('hello', 'w').write('hello')
 
89
        b.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('.')
 
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('.')
 
113
        file('hello', 'w').write('hello')
 
114
        file('buongia', 'w').write('buongia')
 
115
        b.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.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.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('.')
 
148
        self.build_tree(['hello'])
 
149
        b.add(['hello'], ['hello-id'])
 
150
        b.working_tree().commit(message='one', rev_id='test@rev-1', allow_pointless=False)
 
151
 
 
152
        b.rename_one('hello', 'fruity')
 
153
        b.working_tree().commit(message='renamed', rev_id='test@rev-2', allow_pointless=False)
 
154
 
 
155
        eq = self.assertEquals
 
156
        tree1 = b.revision_tree('test@rev-1')
 
157
        eq(tree1.id2path('hello-id'), 'hello')
 
158
        eq(tree1.get_file_text('hello-id'), 'contents of hello\n')
 
159
        self.assertFalse(tree1.has_filename('fruity'))
 
160
        self.check_inventory_shape(tree1.inventory, ['hello'])
 
161
        ie = tree1.inventory['hello-id']
 
162
        eq(ie.revision, 'test@rev-1')
 
163
 
 
164
        tree2 = b.revision_tree('test@rev-2')
 
165
        eq(tree2.id2path('hello-id'), 'fruity')
 
166
        eq(tree2.get_file_text('hello-id'), 'contents of hello\n')
 
167
        self.check_inventory_shape(tree2.inventory, ['fruity'])
 
168
        ie = tree2.inventory['hello-id']
 
169
        eq(ie.revision, 'test@rev-2')
 
170
 
 
171
 
 
172
    def test_reused_rev_id(self):
 
173
        """Test that a revision id cannot be reused in a branch"""
 
174
        b = Branch.initialize('.')
 
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
 
 
183
 
 
184
    def test_commit_move(self):
 
185
        """Test commit of revisions with moved files and directories"""
 
186
        eq = self.assertEquals
 
187
        b = Branch.initialize('.')
 
188
        r1 = 'test@rev-1'
 
189
        self.build_tree(['hello', 'a/', 'b/'])
 
190
        b.add(['hello', 'a', 'b'], ['hello-id', 'a-id', 'b-id'])
 
191
        b.working_tree().commit('initial', rev_id=r1, allow_pointless=False)
 
192
 
 
193
        b.move(['hello'], 'a')
 
194
        r2 = 'test@rev-2'
 
195
        b.working_tree().commit('two', rev_id=r2, allow_pointless=False)
 
196
        self.check_inventory_shape(b.working_tree().read_working_inventory(),
 
197
                                   ['a', 'a/hello', 'b'])
 
198
 
 
199
        b.move(['b'], 'a')
 
200
        r3 = 'test@rev-3'
 
201
        b.working_tree().commit('three', rev_id=r3, allow_pointless=False)
 
202
        self.check_inventory_shape(b.working_tree().read_working_inventory(),
 
203
                                   ['a', 'a/hello', 'a/b'])
 
204
        self.check_inventory_shape(b.get_revision_inventory(r3),
 
205
                                   ['a', 'a/hello', 'a/b'])
 
206
 
 
207
        b.move([os.sep.join(['a', 'hello'])],
 
208
               os.sep.join(['a', 'b']))
 
209
        r4 = 'test@rev-4'
 
210
        b.working_tree().commit('four', rev_id=r4, allow_pointless=False)
 
211
        self.check_inventory_shape(b.working_tree().read_working_inventory(),
 
212
                                   ['a', 'a/b/hello', 'a/b'])
 
213
 
 
214
        inv = b.get_revision_inventory(r4)
 
215
        eq(inv['hello-id'].revision, r4)
 
216
        eq(inv['a-id'].revision, r1)
 
217
        eq(inv['b-id'].revision, r3)
 
218
 
 
219
        
 
220
    def test_removed_commit(self):
 
221
        """Commit with a removed file"""
 
222
        b = Branch.initialize('.')
 
223
        wt = b.working_tree()
 
224
        file('hello', 'w').write('hello world')
 
225
        b.add(['hello'], ['hello-id'])
 
226
        b.working_tree().commit(message='add hello')
 
227
 
 
228
        wt = b.working_tree()  # FIXME: kludge for aliasing of working inventory
 
229
        wt.remove('hello')
 
230
        b.working_tree().commit('removed hello', rev_id='rev2')
 
231
 
 
232
        tree = b.revision_tree('rev2')
 
233
        self.assertFalse(tree.has_id('hello-id'))
 
234
 
 
235
 
 
236
    def test_committed_ancestry(self):
 
237
        """Test commit appends revisions to ancestry."""
 
238
        b = Branch.initialize('.')
 
239
        rev_ids = []
 
240
        for i in range(4):
 
241
            file('hello', 'w').write((str(i) * 4) + '\n')
 
242
            if i == 0:
 
243
                b.add(['hello'], ['hello-id'])
 
244
            rev_id = 'test@rev-%d' % (i+1)
 
245
            rev_ids.append(rev_id)
 
246
            b.working_tree().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.get_ancestry(rev_ids[i])
 
252
            eq(anc, [None] + rev_ids[:i+1])
 
253
 
 
254
    def test_commit_new_subdir_child_selective(self):
 
255
        b = Branch.initialize('.')
 
256
        self.build_tree(['dir/', 'dir/file1', 'dir/file2'])
 
257
        b.add(['dir', 'dir/file1', 'dir/file2'],
 
258
              ['dirid', 'file1id', 'file2id'])
 
259
        b.working_tree().commit('dir/file1', specific_files=['dir/file1'], rev_id='1')
 
260
        inv = b.get_inventory('1')
 
261
        self.assertEqual('1', inv['dirid'].revision)
 
262
        self.assertEqual('1', inv['file1id'].revision)
 
263
        # FIXME: This should raise a KeyError I think, rbc20051006
 
264
        self.assertRaises(BzrError, inv.__getitem__, 'file2id')
 
265
 
 
266
    def test_strict_commit(self):
 
267
        """Try and commit with unknown files and strict = True, should fail."""
 
268
        from bzrlib.errors import StrictCommitFailed
 
269
        b = Branch.initialize('.')
 
270
        file('hello', 'w').write('hello world')
 
271
        b.add('hello')
 
272
        file('goodbye', 'w').write('goodbye cruel world!')
 
273
        self.assertRaises(StrictCommitFailed, b.working_tree().commit,
 
274
            message='add hello but not goodbye', strict=True)
 
275
 
 
276
    def test_strict_commit_without_unknowns(self):
 
277
        """Try and commit with no unknown files and strict = True,
 
278
        should work."""
 
279
        from bzrlib.errors import StrictCommitFailed
 
280
        b = Branch.initialize('.')
 
281
        file('hello', 'w').write('hello world')
 
282
        b.add('hello')
 
283
        b.working_tree().commit(message='add hello', strict=True)
 
284
 
 
285
    def test_nonstrict_commit(self):
 
286
        """Try and commit with unknown files and strict = False, should work."""
 
287
        b = Branch.initialize('.')
 
288
        file('hello', 'w').write('hello world')
 
289
        b.add('hello')
 
290
        file('goodbye', 'w').write('goodbye cruel world!')
 
291
        b.working_tree().commit(message='add hello but not goodbye', strict=False)
 
292
 
 
293
    def test_nonstrict_commit_without_unknowns(self):
 
294
        """Try and commit with no unknown files and strict = False,
 
295
        should work."""
 
296
        b = Branch.initialize('.')
 
297
        file('hello', 'w').write('hello world')
 
298
        b.add('hello')
 
299
        b.working_tree().commit(message='add hello', strict=False)
 
300
 
 
301
    def test_signed_commit(self):
 
302
        import bzrlib.gpg
 
303
        import bzrlib.commit as commit
 
304
        oldstrategy = bzrlib.gpg.GPGStrategy
 
305
        branch = Branch.initialize('.')
 
306
        branch.working_tree().commit("base", allow_pointless=True, rev_id='A')
 
307
        self.failIf(branch.revision_store.has_id('A', 'sig'))
 
308
        try:
 
309
            from bzrlib.testament import Testament
 
310
            # monkey patch gpg signing mechanism
 
311
            bzrlib.gpg.GPGStrategy = bzrlib.gpg.LoopbackGPGStrategy
 
312
            commit.Commit(config=MustSignConfig(branch)).commit(branch, "base",
 
313
                                                      allow_pointless=True,
 
314
                                                      rev_id='B')
 
315
            self.assertEqual(Testament.from_revision(branch,'B').as_short_text(),
 
316
                             branch.revision_store.get('B', 'sig').read())
 
317
        finally:
 
318
            bzrlib.gpg.GPGStrategy = oldstrategy
 
319
 
 
320
    def test_commit_failed_signature(self):
 
321
        import bzrlib.gpg
 
322
        import bzrlib.commit as commit
 
323
        oldstrategy = bzrlib.gpg.GPGStrategy
 
324
        branch = Branch.initialize('.')
 
325
        branch.working_tree().commit("base", allow_pointless=True, rev_id='A')
 
326
        self.failIf(branch.revision_store.has_id('A', 'sig'))
 
327
        try:
 
328
            from bzrlib.testament import Testament
 
329
            # monkey patch gpg signing mechanism
 
330
            bzrlib.gpg.GPGStrategy = bzrlib.gpg.DisabledGPGStrategy
 
331
            config = MustSignConfig(branch)
 
332
            self.assertRaises(SigningFailed,
 
333
                              commit.Commit(config=config).commit,
 
334
                              branch, "base",
 
335
                              allow_pointless=True,
 
336
                              rev_id='B')
 
337
            branch = Branch.open('.')
 
338
            self.assertEqual(branch.revision_history(), ['A'])
 
339
            self.failIf(branch.revision_store.has_id('B'))
 
340
        finally:
 
341
            bzrlib.gpg.GPGStrategy = oldstrategy
 
342
 
 
343
    def test_commit_invokes_hooks(self):
 
344
        import bzrlib.commit as commit
 
345
        branch = Branch.initialize('.')
 
346
        calls = []
 
347
        def called(branch, rev_id):
 
348
            calls.append('called')
 
349
        bzrlib.ahook = called
 
350
        try:
 
351
            config = BranchWithHooks(branch)
 
352
            commit.Commit(config=config).commit(
 
353
                            branch, "base",
 
354
                            allow_pointless=True,
 
355
                            rev_id='A')
 
356
            self.assertEqual(['called', 'called'], calls)
 
357
        finally:
 
358
            del bzrlib.ahook