13
13
# You should have received a copy of the GNU General Public License
14
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
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
21
from bzrlib import (
28
26
from bzrlib.branch import Branch
29
from bzrlib.bzrdir import BzrDir, BzrDirMetaFormat1
27
from bzrlib.bzrdir import BzrDirMetaFormat1
30
28
from bzrlib.commit import Commit, NullCommitReporter
31
from bzrlib.config import BranchConfig
32
from bzrlib.errors import (PointlessCommit, BzrError, SigningFailed,
34
from bzrlib.tests import SymlinkFeature, TestCaseWithTransport
35
from bzrlib.workingtree import WorkingTree
29
from bzrlib.errors import (
35
from bzrlib.tests import (
36
TestCaseWithTransport,
39
from bzrlib.tests.features import (
42
from bzrlib.tests.matchers import MatchesAncestry
38
45
# TODO: Test commit with some added, and added-but-missing files
40
class MustSignConfig(BranchConfig):
42
def signature_needed(self):
45
def gpg_signing_command(self):
49
class BranchWithHooks(BranchConfig):
51
def post_commit(self):
52
return "bzrlib.ahook bzrlib.ahook"
47
class MustSignConfig(config.MemoryStack):
50
super(MustSignConfig, self).__init__('''
51
gpg_signing_command=cat -
52
create_signatures=always
55
56
class CapturingReporter(NullCommitReporter):
81
82
"""Commit and check two versions of a single file."""
82
83
wt = self.make_branch_and_tree('.')
84
file('hello', 'w').write('hello world')
85
with file('hello', 'w') as f: f.write('hello world')
86
wt.commit(message='add hello')
87
rev1 = wt.commit(message='add hello')
87
88
file_id = wt.path2id('hello')
89
file('hello', 'w').write('version 2')
90
wt.commit(message='commit 2')
90
with file('hello', 'w') as f: f.write('version 2')
91
rev2 = wt.commit(message='commit 2')
92
93
eq = self.assertEquals
94
rh = b.revision_history()
95
rev = b.repository.get_revision(rh[0])
95
rev = b.repository.get_revision(rev1)
96
96
eq(rev.message, 'add hello')
98
tree1 = b.repository.revision_tree(rh[0])
98
tree1 = b.repository.revision_tree(rev1)
100
100
text = tree1.get_file_text(file_id)
102
102
self.assertEqual('hello world', text)
104
tree2 = b.repository.revision_tree(rh[1])
104
tree2 = b.repository.revision_tree(rev2)
105
105
tree2.lock_read()
106
106
text = tree2.get_file_text(file_id)
108
108
self.assertEqual('version 2', text)
110
def test_delete_commit(self):
111
"""Test a commit with a deleted file"""
112
wt = self.make_branch_and_tree('.')
114
file('hello', 'w').write('hello world')
110
def test_commit_lossy_native(self):
111
"""Attempt a lossy commit to a native branch."""
112
wt = self.make_branch_and_tree('.')
114
with file('hello', 'w') as f: f.write('hello world')
116
revid = wt.commit(message='add hello', rev_id='revid', lossy=True)
117
self.assertEquals('revid', revid)
119
def test_commit_lossy_foreign(self):
120
"""Attempt a lossy commit to a foreign branch."""
121
test_foreign.register_dummy_foreign_for_test(self)
122
wt = self.make_branch_and_tree('.',
123
format=test_foreign.DummyForeignVcsDirFormat())
125
with file('hello', 'w') as f: f.write('hello world')
127
revid = wt.commit(message='add hello', lossy=True,
128
timestamp=1302659388, timezone=0)
129
self.assertEquals('dummy-v1:1302659388.0-0-UNKNOWN', revid)
131
def test_commit_bound_lossy_foreign(self):
132
"""Attempt a lossy commit to a bzr branch bound to a foreign branch."""
133
test_foreign.register_dummy_foreign_for_test(self)
134
foreign_branch = self.make_branch('foreign',
135
format=test_foreign.DummyForeignVcsDirFormat())
136
wt = foreign_branch.create_checkout("local")
138
with file('local/hello', 'w') as f: f.write('hello world')
140
revid = wt.commit(message='add hello', lossy=True,
141
timestamp=1302659388, timezone=0)
142
self.assertEquals('dummy-v1:1302659388.0-0-0', revid)
143
self.assertEquals('dummy-v1:1302659388.0-0-0',
144
foreign_branch.last_revision())
145
self.assertEquals('dummy-v1:1302659388.0-0-0',
146
wt.branch.last_revision())
148
def test_missing_commit(self):
149
"""Test a commit with a missing file"""
150
wt = self.make_branch_and_tree('.')
152
with file('hello', 'w') as f: f.write('hello world')
115
153
wt.add(['hello'], ['hello-id'])
116
154
wt.commit(message='add hello')
118
156
os.remove('hello')
119
wt.commit('removed hello', rev_id='rev2')
157
reporter = CapturingReporter()
158
wt.commit('removed hello', rev_id='rev2', reporter=reporter)
160
[('missing', u'hello'), ('deleted', u'hello')],
121
163
tree = b.repository.revision_tree('rev2')
122
164
self.assertFalse(tree.has_id('hello-id'))
171
213
"""Selective commit in tree with deletions"""
172
214
wt = self.make_branch_and_tree('.')
174
file('hello', 'w').write('hello')
175
file('buongia', 'w').write('buongia')
216
with file('hello', 'w') as f: f.write('hello')
217
with file('buongia', 'w') as f: f.write('buongia')
176
218
wt.add(['hello', 'buongia'],
177
219
['hello-id', 'buongia-id'])
178
220
wt.commit(message='add files',
179
221
rev_id='test@rev-1')
181
223
os.remove('hello')
182
file('buongia', 'w').write('new text')
224
with file('buongia', 'w') as f: f.write('new text')
183
225
wt.commit(message='update text',
184
226
specific_files=['buongia'],
185
227
allow_pointless=False,
224
266
eq(tree1.id2path('hello-id'), 'hello')
225
267
eq(tree1.get_file_text('hello-id'), 'contents of hello\n')
226
268
self.assertFalse(tree1.has_filename('fruity'))
227
self.check_inventory_shape(tree1.inventory, ['hello'])
228
ie = tree1.inventory['hello-id']
229
eq(ie.revision, 'test@rev-1')
269
self.check_tree_shape(tree1, ['hello'])
270
eq(tree1.get_file_revision('hello-id'), 'test@rev-1')
231
272
tree2 = b.repository.revision_tree('test@rev-2')
232
273
tree2.lock_read()
233
274
self.addCleanup(tree2.unlock)
234
275
eq(tree2.id2path('hello-id'), 'fruity')
235
276
eq(tree2.get_file_text('hello-id'), 'contents of hello\n')
236
self.check_inventory_shape(tree2.inventory, ['fruity'])
237
ie = tree2.inventory['hello-id']
238
eq(ie.revision, 'test@rev-2')
277
self.check_tree_shape(tree2, ['fruity'])
278
eq(tree2.get_file_revision('hello-id'), 'test@rev-2')
240
280
def test_reused_rev_id(self):
241
281
"""Test that a revision id cannot be reused in a branch"""
284
323
wt.commit('four', rev_id=r4, allow_pointless=False)
287
self.check_inventory_shape(wt.read_working_inventory(),
288
['a/', 'a/b/hello', 'a/b/'])
326
self.check_tree_shape(wt, ['a/', 'a/b/hello', 'a/b/'])
292
inv = b.repository.get_revision_inventory(r4)
330
inv = b.repository.get_inventory(r4)
293
331
eq(inv['hello-id'].revision, r4)
294
332
eq(inv['a-id'].revision, r1)
295
333
eq(inv['b-id'].revision, r3)
315
353
for i in range(4):
316
file('hello', 'w').write((str(i) * 4) + '\n')
354
with file('hello', 'w') as f: f.write((str(i) * 4) + '\n')
318
356
wt.add(['hello'], ['hello-id'])
319
357
rev_id = 'test@rev-%d' % (i+1)
320
358
rev_ids.append(rev_id)
321
359
wt.commit(message='rev %d' % (i+1),
323
eq = self.assertEquals
324
eq(b.revision_history(), rev_ids)
325
361
for i in range(4):
326
anc = b.repository.get_ancestry(rev_ids[i])
327
eq(anc, [None] + rev_ids[:i+1])
362
self.assertThat(rev_ids[:i+1],
363
MatchesAncestry(b.repository, rev_ids[i]))
329
365
def test_commit_new_subdir_child_selective(self):
330
366
wt = self.make_branch_and_tree('.')
344
380
from bzrlib.errors import StrictCommitFailed
345
381
wt = self.make_branch_and_tree('.')
347
file('hello', 'w').write('hello world')
383
with file('hello', 'w') as f: f.write('hello world')
349
file('goodbye', 'w').write('goodbye cruel world!')
385
with file('goodbye', 'w') as f: f.write('goodbye cruel world!')
350
386
self.assertRaises(StrictCommitFailed, wt.commit,
351
387
message='add hello but not goodbye', strict=True)
353
389
def test_strict_commit_without_unknowns(self):
354
390
"""Try and commit with no unknown files and strict = True,
356
from bzrlib.errors import StrictCommitFailed
357
392
wt = self.make_branch_and_tree('.')
359
file('hello', 'w').write('hello world')
394
with file('hello', 'w') as f: f.write('hello world')
361
396
wt.commit(message='add hello', strict=True)
385
420
wt = self.make_branch_and_tree('.')
386
421
branch = wt.branch
387
422
wt.commit("base", allow_pointless=True, rev_id='A')
388
self.failIf(branch.repository.has_signature_for_revision_id('A'))
423
self.assertFalse(branch.repository.has_signature_for_revision_id('A'))
390
425
from bzrlib.testament import Testament
391
426
# monkey patch gpg signing mechanism
392
427
bzrlib.gpg.GPGStrategy = bzrlib.gpg.LoopbackGPGStrategy
393
commit.Commit(config=MustSignConfig(branch)).commit(message="base",
394
allow_pointless=True,
428
conf = config.MemoryStack('''
429
gpg_signing_command=cat -
430
create_signatures=always
432
commit.Commit(config_stack=conf).commit(
433
message="base", allow_pointless=True, rev_id='B',
398
436
return bzrlib.gpg.LoopbackGPGStrategy(None).sign(text)
399
437
self.assertEqual(sign(Testament.from_revision(branch.repository,
400
'B').as_short_text()),
438
'B').as_short_text()),
401
439
branch.repository.get_signature_text('B'))
403
441
bzrlib.gpg.GPGStrategy = oldstrategy
409
447
wt = self.make_branch_and_tree('.')
410
448
branch = wt.branch
411
449
wt.commit("base", allow_pointless=True, rev_id='A')
412
self.failIf(branch.repository.has_signature_for_revision_id('A'))
450
self.assertFalse(branch.repository.has_signature_for_revision_id('A'))
414
from bzrlib.testament import Testament
415
452
# monkey patch gpg signing mechanism
416
453
bzrlib.gpg.GPGStrategy = bzrlib.gpg.DisabledGPGStrategy
417
config = MustSignConfig(branch)
454
conf = config.MemoryStack('''
455
gpg_signing_command=cat -
456
create_signatures=always
418
458
self.assertRaises(SigningFailed,
419
commit.Commit(config=config).commit,
459
commit.Commit(config_stack=conf).commit,
421
461
allow_pointless=True,
424
464
branch = Branch.open(self.get_url('.'))
425
self.assertEqual(branch.revision_history(), ['A'])
426
self.failIf(branch.repository.has_revision('B'))
465
self.assertEqual(branch.last_revision(), 'A')
466
self.assertFalse(branch.repository.has_revision('B'))
428
468
bzrlib.gpg.GPGStrategy = oldstrategy
550
589
this_tree.merge_from_branch(other_tree.branch)
551
590
reporter = CapturingReporter()
552
591
this_tree.commit('do the commit', reporter=reporter)
554
('change', 'unchanged', ''),
555
('change', 'unchanged', 'dirtoleave'),
556
('change', 'unchanged', 'filetoleave'),
557
593
('change', 'modified', 'filetomodify'),
558
594
('change', 'added', 'newdir'),
559
595
('change', 'added', 'newfile'),
708
747
cb = self.Callback(u'commit 2', self)
709
748
repository = tree.branch.repository
710
749
# simulate network failure
711
def raise_(self, arg, arg2):
750
def raise_(self, arg, arg2, arg3=None, arg4=None):
712
751
raise errors.NoSuchFile('foo')
713
752
repository.add_inventory = raise_
753
repository.add_inventory_by_delta = raise_
714
754
self.assertRaises(errors.NoSuchFile, tree.commit, message_callback=cb)
715
755
self.assertFalse(cb.called)
747
787
rev_id = tree.commit('commit 1')
748
788
rev = tree.branch.repository.get_revision(rev_id)
749
789
self.assertFalse('author' in rev.properties)
790
self.assertFalse('authors' in rev.properties)
751
792
def test_commit_author(self):
752
793
"""Passing a non-empty author kwarg to MutableTree.commit should add
753
794
the 'author' revision property.
755
796
tree = self.make_branch_and_tree('foo')
756
rev_id = tree.commit('commit 1', author='John Doe <jdoe@example.com>')
797
rev_id = self.callDeprecated(['The parameter author was '
798
'deprecated in version 1.13. Use authors instead'],
799
tree.commit, 'commit 1', author='John Doe <jdoe@example.com>')
757
800
rev = tree.branch.repository.get_revision(rev_id)
758
801
self.assertEqual('John Doe <jdoe@example.com>',
759
rev.properties['author'])
802
rev.properties['authors'])
803
self.assertFalse('author' in rev.properties)
805
def test_commit_empty_authors_list(self):
806
"""Passing an empty list to authors shouldn't add the property."""
807
tree = self.make_branch_and_tree('foo')
808
rev_id = tree.commit('commit 1', authors=[])
809
rev = tree.branch.repository.get_revision(rev_id)
810
self.assertFalse('author' in rev.properties)
811
self.assertFalse('authors' in rev.properties)
813
def test_multiple_authors(self):
814
tree = self.make_branch_and_tree('foo')
815
rev_id = tree.commit('commit 1',
816
authors=['John Doe <jdoe@example.com>',
817
'Jane Rey <jrey@example.com>'])
818
rev = tree.branch.repository.get_revision(rev_id)
819
self.assertEqual('John Doe <jdoe@example.com>\n'
820
'Jane Rey <jrey@example.com>', rev.properties['authors'])
821
self.assertFalse('author' in rev.properties)
823
def test_author_and_authors_incompatible(self):
824
tree = self.make_branch_and_tree('foo')
825
self.assertRaises(AssertionError, tree.commit, 'commit 1',
826
authors=['John Doe <jdoe@example.com>',
827
'Jane Rey <jrey@example.com>'],
828
author="Jack Me <jme@example.com>")
830
def test_author_with_newline_rejected(self):
831
tree = self.make_branch_and_tree('foo')
832
self.assertRaises(AssertionError, tree.commit, 'commit 1',
833
authors=['John\nDoe <jdoe@example.com>'])
761
835
def test_commit_with_checkout_and_branch_sharing_repo(self):
762
836
repo = self.make_repository('repo', shared=True)
763
837
# make_branch_and_tree ignores shared repos
764
branch = bzrdir.BzrDir.create_branch_convenience('repo/branch')
838
branch = controldir.ControlDir.create_branch_convenience('repo/branch')
765
839
tree2 = branch.create_checkout('repo/tree2')
766
840
tree2.commit('message', rev_id='rev1')
767
841
self.assertTrue(tree2.branch.repository.has_revision('rev1'))