1
# Copyright (C) 2005 by Canonical Ltd
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.
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.
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
20
from bzrlib.selftest import TestCaseInTempDir
21
from bzrlib.branch import Branch
22
from bzrlib.workingtree import WorkingTree
23
from bzrlib.commit import Commit
24
from bzrlib.config import BranchConfig
25
from bzrlib.errors import PointlessCommit, BzrError, SigningFailed
28
# TODO: Test commit with some added, and added-but-missing files
30
class MustSignConfig(BranchConfig):
32
def signature_needed(self):
35
def gpg_signing_command(self):
39
class TestCommit(TestCaseInTempDir):
41
def test_simple_commit(self):
42
"""Commit and check two versions of a single file."""
43
b = Branch.initialize('.')
44
file('hello', 'w').write('hello world')
46
b.commit(message='add hello')
47
file_id = b.working_tree().path2id('hello')
49
file('hello', 'w').write('version 2')
50
b.commit(message='commit 2')
52
eq = self.assertEquals
54
rh = b.revision_history()
55
rev = b.get_revision(rh[0])
56
eq(rev.message, 'add hello')
58
tree1 = b.revision_tree(rh[0])
59
text = tree1.get_file_text(file_id)
60
eq(text, 'hello world')
62
tree2 = b.revision_tree(rh[1])
63
eq(tree2.get_file_text(file_id), 'version 2')
65
def test_delete_commit(self):
66
"""Test a commit with a deleted file"""
67
b = Branch.initialize('.')
68
file('hello', 'w').write('hello world')
69
b.add(['hello'], ['hello-id'])
70
b.commit(message='add hello')
73
b.commit('removed hello', rev_id='rev2')
75
tree = b.revision_tree('rev2')
76
self.assertFalse(tree.has_id('hello-id'))
79
def test_pointless_commit(self):
80
"""Commit refuses unless there are changes or it's forced."""
81
b = Branch.initialize('.')
82
file('hello', 'w').write('hello')
84
b.commit(message='add hello')
85
self.assertEquals(b.revno(), 1)
86
self.assertRaises(PointlessCommit,
89
allow_pointless=False)
90
self.assertEquals(b.revno(), 1)
94
def test_commit_empty(self):
95
"""Commiting an empty tree works."""
96
b = Branch.initialize('.')
97
b.commit(message='empty tree', allow_pointless=True)
98
self.assertRaises(PointlessCommit,
100
message='empty tree',
101
allow_pointless=False)
102
b.commit(message='empty tree', allow_pointless=True)
103
self.assertEquals(b.revno(), 2)
106
def test_selective_delete(self):
107
"""Selective commit in tree with deletions"""
108
b = Branch.initialize('.')
109
file('hello', 'w').write('hello')
110
file('buongia', 'w').write('buongia')
111
b.add(['hello', 'buongia'],
112
['hello-id', 'buongia-id'])
113
b.commit(message='add files',
117
file('buongia', 'w').write('new text')
118
b.commit(message='update text',
119
specific_files=['buongia'],
120
allow_pointless=False,
123
b.commit(message='remove hello',
124
specific_files=['hello'],
125
allow_pointless=False,
128
eq = self.assertEquals
131
tree2 = b.revision_tree('test@rev-2')
132
self.assertTrue(tree2.has_filename('hello'))
133
self.assertEquals(tree2.get_file_text('hello-id'), 'hello')
134
self.assertEquals(tree2.get_file_text('buongia-id'), 'new text')
136
tree3 = b.revision_tree('test@rev-3')
137
self.assertFalse(tree3.has_filename('hello'))
138
self.assertEquals(tree3.get_file_text('buongia-id'), 'new text')
141
def test_commit_rename(self):
142
"""Test commit of a revision where a file is renamed."""
143
b = Branch.initialize('.')
144
self.build_tree(['hello'])
145
b.add(['hello'], ['hello-id'])
146
b.commit(message='one', rev_id='test@rev-1', allow_pointless=False)
148
b.rename_one('hello', 'fruity')
149
b.commit(message='renamed', rev_id='test@rev-2', allow_pointless=False)
151
eq = self.assertEquals
152
tree1 = b.revision_tree('test@rev-1')
153
eq(tree1.id2path('hello-id'), 'hello')
154
eq(tree1.get_file_text('hello-id'), 'contents of hello\n')
155
self.assertFalse(tree1.has_filename('fruity'))
156
self.check_inventory_shape(tree1.inventory, ['hello'])
157
ie = tree1.inventory['hello-id']
158
eq(ie.revision, 'test@rev-1')
160
tree2 = b.revision_tree('test@rev-2')
161
eq(tree2.id2path('hello-id'), 'fruity')
162
eq(tree2.get_file_text('hello-id'), 'contents of hello\n')
163
self.check_inventory_shape(tree2.inventory, ['fruity'])
164
ie = tree2.inventory['hello-id']
165
eq(ie.revision, 'test@rev-2')
168
def test_reused_rev_id(self):
169
"""Test that a revision id cannot be reused in a branch"""
170
b = Branch.initialize('.')
171
b.commit('initial', rev_id='test@rev-1', allow_pointless=True)
172
self.assertRaises(Exception,
176
allow_pointless=True)
180
def test_commit_move(self):
181
"""Test commit of revisions with moved files and directories"""
182
eq = self.assertEquals
183
b = Branch.initialize('.')
185
self.build_tree(['hello', 'a/', 'b/'])
186
b.add(['hello', 'a', 'b'], ['hello-id', 'a-id', 'b-id'])
187
b.commit('initial', rev_id=r1, allow_pointless=False)
189
b.move(['hello'], 'a')
191
b.commit('two', rev_id=r2, allow_pointless=False)
192
self.check_inventory_shape(b.inventory,
193
['a', 'a/hello', 'b'])
197
b.commit('three', rev_id=r3, allow_pointless=False)
198
self.check_inventory_shape(b.inventory,
199
['a', 'a/hello', 'a/b'])
200
self.check_inventory_shape(b.get_revision_inventory(r3),
201
['a', 'a/hello', 'a/b'])
203
b.move([os.sep.join(['a', 'hello'])],
204
os.sep.join(['a', 'b']))
206
b.commit('four', rev_id=r4, allow_pointless=False)
207
self.check_inventory_shape(b.inventory,
208
['a', 'a/b/hello', 'a/b'])
210
inv = b.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)
216
def test_removed_commit(self):
217
"""Commit with a removed file"""
218
b = Branch.initialize('.')
219
wt = b.working_tree()
220
file('hello', 'w').write('hello world')
221
b.add(['hello'], ['hello-id'])
222
b.commit(message='add hello')
224
wt = b.working_tree() # FIXME: kludge for aliasing of working inventory
226
b.commit('removed hello', rev_id='rev2')
228
tree = b.revision_tree('rev2')
229
self.assertFalse(tree.has_id('hello-id'))
232
def test_committed_ancestry(self):
233
"""Test commit appends revisions to ancestry."""
234
b = Branch.initialize('.')
237
file('hello', 'w').write((str(i) * 4) + '\n')
239
b.add(['hello'], ['hello-id'])
240
rev_id = 'test@rev-%d' % (i+1)
241
rev_ids.append(rev_id)
242
b.commit(message='rev %d' % (i+1),
244
eq = self.assertEquals
245
eq(b.revision_history(), rev_ids)
247
anc = b.get_ancestry(rev_ids[i])
248
eq(anc, [None] + rev_ids[:i+1])
250
def test_commit_new_subdir_child_selective(self):
251
b = Branch.initialize('.')
252
self.build_tree(['dir/', 'dir/file1', 'dir/file2'])
253
b.add(['dir', 'dir/file1', 'dir/file2'],
254
['dirid', 'file1id', 'file2id'])
255
b.commit('dir/file1', specific_files=['dir/file1'], rev_id='1')
256
inv = b.get_inventory('1')
257
self.assertEqual('1', inv['dirid'].revision)
258
self.assertEqual('1', inv['file1id'].revision)
259
# FIXME: This should raise a KeyError I think, rbc20051006
260
self.assertRaises(BzrError, inv.__getitem__, 'file2id')
262
def test_strict_commit(self):
263
"""Try and commit with unknown files and strict = True, should fail."""
264
from bzrlib.errors import StrictCommitFailed
265
b = Branch.initialize('.')
266
file('hello', 'w').write('hello world')
268
file('goodbye', 'w').write('goodbye cruel world!')
269
self.assertRaises(StrictCommitFailed, b.commit,
270
message='add hello but not goodbye', strict=True)
272
def test_nonstrict_commit(self):
273
"""Try and commit with unknown files and strict = False, should work."""
274
b = Branch.initialize('.')
275
file('hello', 'w').write('hello world')
277
file('goodbye', 'w').write('goodbye cruel world!')
278
b.commit(message='add hello but not goodbye', strict=False)
280
def test_signed_commit(self):
282
import bzrlib.commit as commit
283
oldstrategy = bzrlib.gpg.GPGStrategy
284
branch = Branch.initialize('.')
285
branch.commit("base", allow_pointless=True, rev_id='A')
286
self.failIf(branch.revision_store.has_id('A', 'sig'))
288
from bzrlib.testament import Testament
289
# monkey patch gpg signing mechanism
290
bzrlib.gpg.GPGStrategy = bzrlib.gpg.LoopbackGPGStrategy
291
commit.Commit(config=MustSignConfig(branch)).commit(branch, "base",
292
allow_pointless=True,
294
self.assertEqual(Testament.from_revision(branch,'B').as_short_text(),
295
branch.revision_store.get('B', 'sig').read())
297
bzrlib.gpg.GPGStrategy = oldstrategy
299
def test_commit_failed_signature(self):
301
import bzrlib.commit as commit
302
oldstrategy = bzrlib.gpg.GPGStrategy
303
branch = Branch.initialize('.')
304
branch.commit("base", allow_pointless=True, rev_id='A')
305
self.failIf(branch.revision_store.has_id('A', 'sig'))
307
from bzrlib.testament import Testament
308
# monkey patch gpg signing mechanism
309
bzrlib.gpg.GPGStrategy = bzrlib.gpg.DisabledGPGStrategy
310
config = MustSignConfig(branch)
311
self.assertRaises(SigningFailed,
312
commit.Commit(config=config).commit,
314
allow_pointless=True,
316
branch = Branch.open('.')
317
self.assertEqual(branch.revision_history(), ['A'])
318
self.failIf(branch.revision_store.has_id('B'))
320
bzrlib.gpg.GPGStrategy = oldstrategy