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