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 (
27
26
from bzrlib.branch import Branch
28
from bzrlib.bzrdir import BzrDir, BzrDirMetaFormat1
27
from bzrlib.bzrdir import BzrDirMetaFormat1
29
28
from bzrlib.commit import Commit, NullCommitReporter
30
from bzrlib.config import BranchConfig
31
from bzrlib.errors import (PointlessCommit, BzrError, SigningFailed,
33
from bzrlib.tests import SymlinkFeature, TestCaseWithTransport
34
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
37
45
# TODO: Test commit with some added, and added-but-missing files
39
class MustSignConfig(BranchConfig):
41
def signature_needed(self):
44
def gpg_signing_command(self):
48
class BranchWithHooks(BranchConfig):
50
def post_commit(self):
51
return "bzrlib.ahook bzrlib.ahook"
47
class MustSignConfig(config.MemoryStack):
50
super(MustSignConfig, self).__init__('''
51
gpg_signing_command=cat -
52
create_signatures=always
54
56
class CapturingReporter(NullCommitReporter):
80
82
"""Commit and check two versions of a single file."""
81
83
wt = self.make_branch_and_tree('.')
83
file('hello', 'w').write('hello world')
85
with file('hello', 'w') as f: f.write('hello world')
85
wt.commit(message='add hello')
87
rev1 = wt.commit(message='add hello')
86
88
file_id = wt.path2id('hello')
88
file('hello', 'w').write('version 2')
89
wt.commit(message='commit 2')
90
with file('hello', 'w') as f: f.write('version 2')
91
rev2 = wt.commit(message='commit 2')
91
eq = self.assertEquals
93
rh = b.revision_history()
94
rev = b.repository.get_revision(rh[0])
95
rev = b.repository.get_revision(rev1)
95
96
eq(rev.message, 'add hello')
97
tree1 = b.repository.revision_tree(rh[0])
98
tree1 = b.repository.revision_tree(rev1)
99
100
text = tree1.get_file_text(file_id)
101
102
self.assertEqual('hello world', text)
103
tree2 = b.repository.revision_tree(rh[1])
104
tree2 = b.repository.revision_tree(rev2)
104
105
tree2.lock_read()
105
106
text = tree2.get_file_text(file_id)
107
108
self.assertEqual('version 2', text)
109
def test_delete_commit(self):
110
"""Test a commit with a deleted file"""
111
wt = self.make_branch_and_tree('.')
113
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.assertEqual('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.assertEqual('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.assertEqual('dummy-v1:1302659388.0-0-0', revid)
143
self.assertEqual('dummy-v1:1302659388.0-0-0',
144
foreign_branch.last_revision())
145
self.assertEqual('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')
114
153
wt.add(['hello'], ['hello-id'])
115
154
wt.commit(message='add hello')
117
156
os.remove('hello')
118
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')],
120
163
tree = b.repository.revision_tree('rev2')
121
164
self.assertFalse(tree.has_id('hello-id'))
166
def test_partial_commit_move(self):
167
"""Test a partial commit where a file was renamed but not committed.
169
https://bugs.launchpad.net/bzr/+bug/83039
171
If not handled properly, commit will try to snapshot
172
dialog.py with olive/ as a parent, while
173
olive/ has not been snapshotted yet.
175
wt = self.make_branch_and_tree('.')
177
self.build_tree(['annotate/', 'annotate/foo.py',
178
'olive/', 'olive/dialog.py'
180
wt.add(['annotate', 'olive', 'annotate/foo.py', 'olive/dialog.py'])
181
wt.commit(message='add files')
182
wt.rename_one("olive/dialog.py", "aaa")
183
self.build_tree_contents([('annotate/foo.py', 'modified\n')])
184
wt.commit('renamed hello', specific_files=["annotate"])
123
186
def test_pointless_commit(self):
124
187
"""Commit refuses unless there are changes or it's forced."""
125
188
wt = self.make_branch_and_tree('.')
127
file('hello', 'w').write('hello')
190
with file('hello', 'w') as f: f.write('hello')
128
191
wt.add(['hello'])
129
192
wt.commit(message='add hello')
130
self.assertEquals(b.revno(), 1)
193
self.assertEqual(b.revno(), 1)
131
194
self.assertRaises(PointlessCommit,
134
197
allow_pointless=False)
135
self.assertEquals(b.revno(), 1)
198
self.assertEqual(b.revno(), 1)
137
200
def test_commit_empty(self):
138
201
"""Commiting an empty tree works."""
139
202
wt = self.make_branch_and_tree('.')
144
207
message='empty tree',
145
208
allow_pointless=False)
146
209
wt.commit(message='empty tree', allow_pointless=True)
147
self.assertEquals(b.revno(), 2)
210
self.assertEqual(b.revno(), 2)
149
212
def test_selective_delete(self):
150
213
"""Selective commit in tree with deletions"""
151
214
wt = self.make_branch_and_tree('.')
153
file('hello', 'w').write('hello')
154
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')
155
218
wt.add(['hello', 'buongia'],
156
219
['hello-id', 'buongia-id'])
157
220
wt.commit(message='add files',
158
221
rev_id='test@rev-1')
160
223
os.remove('hello')
161
file('buongia', 'w').write('new text')
224
with file('buongia', 'w') as f: f.write('new text')
162
225
wt.commit(message='update text',
163
226
specific_files=['buongia'],
164
227
allow_pointless=False,
196
259
tree.rename_one('hello', 'fruity')
197
260
tree.commit(message='renamed', rev_id='test@rev-2', allow_pointless=False)
199
eq = self.assertEquals
262
eq = self.assertEqual
200
263
tree1 = b.repository.revision_tree('test@rev-1')
201
264
tree1.lock_read()
202
265
self.addCleanup(tree1.unlock)
203
266
eq(tree1.id2path('hello-id'), 'hello')
204
267
eq(tree1.get_file_text('hello-id'), 'contents of hello\n')
205
268
self.assertFalse(tree1.has_filename('fruity'))
206
self.check_inventory_shape(tree1.inventory, ['hello'])
207
ie = tree1.inventory['hello-id']
208
eq(ie.revision, 'test@rev-1')
269
self.check_tree_shape(tree1, ['hello'])
270
eq(tree1.get_file_revision('hello-id'), 'test@rev-1')
210
272
tree2 = b.repository.revision_tree('test@rev-2')
211
273
tree2.lock_read()
212
274
self.addCleanup(tree2.unlock)
213
275
eq(tree2.id2path('hello-id'), 'fruity')
214
276
eq(tree2.get_file_text('hello-id'), 'contents of hello\n')
215
self.check_inventory_shape(tree2.inventory, ['fruity'])
216
ie = tree2.inventory['hello-id']
217
eq(ie.revision, 'test@rev-2')
277
self.check_tree_shape(tree2, ['fruity'])
278
eq(tree2.get_file_revision('hello-id'), 'test@rev-2')
219
280
def test_reused_rev_id(self):
220
281
"""Test that a revision id cannot be reused in a branch"""
388
447
wt = self.make_branch_and_tree('.')
389
448
branch = wt.branch
390
449
wt.commit("base", allow_pointless=True, rev_id='A')
391
self.failIf(branch.repository.has_signature_for_revision_id('A'))
450
self.assertFalse(branch.repository.has_signature_for_revision_id('A'))
393
from bzrlib.testament import Testament
394
452
# monkey patch gpg signing mechanism
395
453
bzrlib.gpg.GPGStrategy = bzrlib.gpg.DisabledGPGStrategy
396
config = MustSignConfig(branch)
454
conf = config.MemoryStack('''
455
gpg_signing_command=cat -
456
create_signatures=always
397
458
self.assertRaises(SigningFailed,
398
commit.Commit(config=config).commit,
459
commit.Commit(config_stack=conf).commit,
400
461
allow_pointless=True,
403
464
branch = Branch.open(self.get_url('.'))
404
self.assertEqual(branch.revision_history(), ['A'])
405
self.failIf(branch.repository.has_revision('B'))
465
self.assertEqual(branch.last_revision(), 'A')
466
self.assertFalse(branch.repository.has_revision('B'))
407
468
bzrlib.gpg.GPGStrategy = oldstrategy
730
787
rev_id = tree.commit('commit 1')
731
788
rev = tree.branch.repository.get_revision(rev_id)
732
789
self.assertFalse('author' in rev.properties)
790
self.assertFalse('authors' in rev.properties)
734
792
def test_commit_author(self):
735
793
"""Passing a non-empty author kwarg to MutableTree.commit should add
736
794
the 'author' revision property.
738
796
tree = self.make_branch_and_tree('foo')
739
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>')
740
800
rev = tree.branch.repository.get_revision(rev_id)
741
801
self.assertEqual('John Doe <jdoe@example.com>',
742
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>'])
835
def test_commit_with_checkout_and_branch_sharing_repo(self):
836
repo = self.make_repository('repo', shared=True)
837
# make_branch_and_tree ignores shared repos
838
branch = controldir.ControlDir.create_branch_convenience('repo/branch')
839
tree2 = branch.create_checkout('repo/tree2')
840
tree2.commit('message', rev_id='rev1')
841
self.assertTrue(tree2.branch.repository.has_revision('rev1'))