1
# Copyright (C) 2005, 2006 by Canonical Ltd
1
# Copyright (C) 2006-2011 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
11
# GNU General Public License for more details.
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
18
18
"""Tests for the commit CLI of bzr."""
20
from cStringIO import StringIO
26
from bzrlib.branch import Branch
27
import bzrlib.bzrdir as bzrdir
28
from bzrlib.errors import BzrCommandError
29
from bzrlib.tests.blackbox import ExternalBase
30
from bzrlib.workingtree import WorkingTree
33
class TestCommit(ExternalBase):
25
from testtools.matchers import DocTestMatches
33
from bzrlib.bzrdir import BzrDir
34
from bzrlib.tests import (
38
from bzrlib.tests import TestCaseWithTransport
41
class TestCommit(TestCaseWithTransport):
35
43
def test_05_empty_commit(self):
36
44
"""Commit of tree with no versioned files should fail"""
37
45
# If forced, it should succeed, but this is not tested here.
46
self.make_branch_and_tree('.')
39
47
self.build_tree(['hello.txt'])
40
self.runbzr("commit -m empty", retcode=3)
48
out,err = self.run_bzr('commit -m empty', retcode=3)
49
self.assertEqual('', out)
51
# 1) We really don't want 'aborting commit write group' anymore.
52
# 2) bzr: ERROR: is a really long line, so we wrap it with '\'
57
bzr: ERROR: No changes to commit.\
58
Please 'bzr add' the files you want to commit,\
59
or use --unchanged to force an empty commit.
60
""", flags=doctest.ELLIPSIS|doctest.REPORT_UDIFF))
62
def test_commit_success(self):
63
"""Successful commit should not leave behind a bzr-commit-* file"""
64
self.make_branch_and_tree('.')
65
self.run_bzr('commit --unchanged -m message')
66
self.assertEqual('', self.run_bzr('unknowns')[0])
68
# same for unicode messages
69
self.run_bzr(["commit", "--unchanged", "-m", u'foo\xb5'])
70
self.assertEqual('', self.run_bzr('unknowns')[0])
72
def test_commit_lossy_native(self):
73
"""A --lossy option to commit is supported."""
74
self.make_branch_and_tree('.')
75
self.run_bzr('commit --lossy --unchanged -m message')
76
self.assertEqual('', self.run_bzr('unknowns')[0])
78
def test_commit_lossy_foreign(self):
79
test_foreign.register_dummy_foreign_for_test(self)
80
self.make_branch_and_tree('.',
81
format=test_foreign.DummyForeignVcsDirFormat())
82
self.run_bzr('commit --lossy --unchanged -m message')
83
output = self.run_bzr('revision-info')[0]
84
self.assertTrue(output.startswith('1 dummy-'))
86
def test_commit_with_path(self):
87
"""Commit tree with path of root specified"""
88
a_tree = self.make_branch_and_tree('a')
89
self.build_tree(['a/a_file'])
91
self.run_bzr(['commit', '-m', 'first commit', 'a'])
93
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
94
self.build_tree_contents([('b/a_file', 'changes in b')])
95
self.run_bzr(['commit', '-m', 'first commit in b', 'b'])
97
self.build_tree_contents([('a/a_file', 'new contents')])
98
self.run_bzr(['commit', '-m', 'change in a', 'a'])
100
b_tree.merge_from_branch(a_tree.branch)
101
self.assertEqual(len(b_tree.conflicts()), 1)
102
self.run_bzr('resolved b/a_file')
103
self.run_bzr(['commit', '-m', 'merge into b', 'b'])
42
105
def test_10_verbose_commit(self):
43
106
"""Add one file and examine verbose commit output"""
107
tree = self.make_branch_and_tree('.')
45
108
self.build_tree(['hello.txt'])
46
self.runbzr("add hello.txt")
47
out,err = self.run_bzr("commit", "-m", "added")
48
self.assertEqual('', out)
49
self.assertEqual('added hello.txt\n'
50
'Committed revision 1.\n',
53
def test_15_verbose_commit_with_unknown(self):
109
tree.add("hello.txt")
110
out,err = self.run_bzr('commit -m added')
111
self.assertEqual('', out)
112
self.assertContainsRe(err, '^Committing to: .*\n'
114
'Committed revision 1.\n$',)
116
def prepare_simple_history(self):
117
"""Prepare and return a working tree with one commit of one file"""
118
# Commit with modified file should say so
119
wt = BzrDir.create_standalone_workingtree('.')
120
self.build_tree(['hello.txt', 'extra.txt'])
121
wt.add(['hello.txt'])
122
wt.commit(message='added')
125
def test_verbose_commit_modified(self):
126
# Verbose commit of modified file should say so
127
wt = self.prepare_simple_history()
128
self.build_tree_contents([('hello.txt', 'new contents')])
129
out, err = self.run_bzr('commit -m modified')
130
self.assertEqual('', out)
131
self.assertContainsRe(err, '^Committing to: .*\n'
132
'modified hello\.txt\n'
133
'Committed revision 2\.\n$')
135
def test_unicode_commit_message_is_filename(self):
136
"""Unicode commit message same as a filename (Bug #563646).
138
self.requireFeature(features.UnicodeFilenameFeature)
139
file_name = u'\N{euro sign}'
140
self.run_bzr(['init'])
141
open(file_name, 'w').write('hello world')
142
self.run_bzr(['add'])
143
out, err = self.run_bzr(['commit', '-m', file_name])
144
reflags = re.MULTILINE|re.DOTALL|re.UNICODE
145
te = osutils.get_terminal_encoding()
146
self.assertContainsRe(err.decode(te),
147
u'The commit message is a file name:',
150
# Run same test with a filename that causes encode
151
# error for the terminal encoding. We do this
152
# by forcing terminal encoding of ascii for
153
# osutils.get_terminal_encoding which is used
154
# by ui.text.show_warning
155
default_get_terminal_enc = osutils.get_terminal_encoding
157
osutils.get_terminal_encoding = lambda trace=None: 'ascii'
158
file_name = u'foo\u1234'
159
open(file_name, 'w').write('hello world')
160
self.run_bzr(['add'])
161
out, err = self.run_bzr(['commit', '-m', file_name])
162
reflags = re.MULTILINE|re.DOTALL|re.UNICODE
163
te = osutils.get_terminal_encoding()
164
self.assertContainsRe(err.decode(te, 'replace'),
165
u'The commit message is a file name:',
168
osutils.get_terminal_encoding = default_get_terminal_enc
170
def test_warn_about_forgotten_commit_message(self):
171
"""Test that the lack of -m parameter is caught"""
172
wt = self.make_branch_and_tree('.')
173
self.build_tree(['one', 'two'])
175
out, err = self.run_bzr('commit -m one two')
176
self.assertContainsRe(err, "The commit message is a file name")
178
def test_verbose_commit_renamed(self):
179
# Verbose commit of renamed file should say so
180
wt = self.prepare_simple_history()
181
wt.rename_one('hello.txt', 'gutentag.txt')
182
out, err = self.run_bzr('commit -m renamed')
183
self.assertEqual('', out)
184
self.assertContainsRe(err, '^Committing to: .*\n'
185
'renamed hello\.txt => gutentag\.txt\n'
186
'Committed revision 2\.$\n')
188
def test_verbose_commit_moved(self):
189
# Verbose commit of file moved to new directory should say so
190
wt = self.prepare_simple_history()
193
wt.rename_one('hello.txt', 'subdir/hello.txt')
194
out, err = self.run_bzr('commit -m renamed')
195
self.assertEqual('', out)
196
self.assertEqual(set([
197
'Committing to: %s/' % osutils.getcwd(),
199
'renamed hello.txt => subdir/hello.txt',
200
'Committed revision 2.',
202
]), set(err.split('\n')))
204
def test_verbose_commit_with_unknown(self):
54
205
"""Unknown files should not be listed by default in verbose output"""
55
206
# Is that really the best policy?
207
wt = BzrDir.create_standalone_workingtree('.')
57
208
self.build_tree(['hello.txt', 'extra.txt'])
58
self.runbzr("add hello.txt")
59
out,err = self.run_bzr("commit", "-m", "added")
209
wt.add(['hello.txt'])
210
out,err = self.run_bzr('commit -m added')
60
211
self.assertEqual('', out)
61
self.assertEqual('added hello.txt\n'
62
'Committed revision 1.\n',
212
self.assertContainsRe(err, '^Committing to: .*\n'
214
'Committed revision 1\.\n$')
65
def test_16_verbose_commit_with_unchanged(self):
216
def test_verbose_commit_with_unchanged(self):
66
217
"""Unchanged files should not be listed by default in verbose output"""
218
tree = self.make_branch_and_tree('.')
68
219
self.build_tree(['hello.txt', 'unchanged.txt'])
69
self.runbzr('add unchanged.txt')
70
self.runbzr('commit -m unchanged unchanged.txt')
71
self.runbzr("add hello.txt")
72
out,err = self.run_bzr("commit", "-m", "added")
73
self.assertEqual('', out)
74
self.assertEqual('added hello.txt\n'
75
'Committed revision 2.\n',
220
tree.add('unchanged.txt')
221
self.run_bzr('commit -m unchanged unchanged.txt')
222
tree.add("hello.txt")
223
out,err = self.run_bzr('commit -m added')
224
self.assertEqual('', out)
225
self.assertContainsRe(err, '^Committing to: .*\n'
227
'Committed revision 2\.$\n')
229
def test_verbose_commit_includes_master_location(self):
230
"""Location of master is displayed when committing to bound branch"""
231
a_tree = self.make_branch_and_tree('a')
232
self.build_tree(['a/b'])
234
a_tree.commit(message='Initial message')
236
b_tree = a_tree.branch.create_checkout('b')
237
expected = "%s/" % (osutils.abspath('a'), )
238
out, err = self.run_bzr('commit -m blah --unchanged', working_dir='b')
239
self.assertEqual(err, 'Committing to: %s\n'
240
'Committed revision 2.\n' % expected)
242
def test_commit_sanitizes_CR_in_message(self):
243
# See bug #433779, basically Emacs likes to pass '\r\n' style line
244
# endings to 'bzr commit -m ""' which breaks because we don't allow
245
# '\r' in commit messages. (Mostly because of issues where XML style
246
# formats arbitrarily strip it out of the data while parsing.)
247
# To make life easier for users, we just always translate '\r\n' =>
248
# '\n'. And '\r' => '\n'.
249
a_tree = self.make_branch_and_tree('a')
250
self.build_tree(['a/b'])
252
self.run_bzr(['commit',
253
'-m', 'a string\r\n\r\nwith mixed\r\rendings\n'],
255
rev_id = a_tree.branch.last_revision()
256
rev = a_tree.branch.repository.get_revision(rev_id)
257
self.assertEqualDiff('a string\n\nwith mixed\n\nendings\n',
260
def test_commit_merge_reports_all_modified_files(self):
261
# the commit command should show all the files that are shown by
262
# bzr diff or bzr status when committing, even when they were not
263
# changed by the user but rather through doing a merge.
264
this_tree = self.make_branch_and_tree('this')
265
# we need a bunch of files and dirs, to perform one action on each.
268
'this/dirtoreparent/',
271
'this/filetoreparent',
288
this_tree.commit('create_files')
289
other_dir = this_tree.bzrdir.sprout('other')
290
other_tree = other_dir.open_workingtree()
291
other_tree.lock_write()
292
# perform the needed actions on the files and dirs.
294
other_tree.rename_one('dirtorename', 'renameddir')
295
other_tree.rename_one('dirtoreparent', 'renameddir/reparenteddir')
296
other_tree.rename_one('filetorename', 'renamedfile')
297
other_tree.rename_one('filetoreparent',
298
'renameddir/reparentedfile')
299
other_tree.remove(['dirtoremove', 'filetoremove'])
300
self.build_tree_contents([
302
('other/filetomodify', 'new content'),
303
('other/newfile', 'new file content')])
304
other_tree.add('newfile')
305
other_tree.add('newdir/')
306
other_tree.commit('modify all sample files and dirs.')
309
this_tree.merge_from_branch(other_tree.branch)
311
out,err = self.run_bzr('commit -m added')
312
self.assertEqual('', out)
313
self.assertEqual(set([
314
'Committing to: %s/' % osutils.getcwd(),
315
'modified filetomodify',
318
'renamed dirtorename => renameddir',
319
'renamed filetorename => renamedfile',
320
'renamed dirtoreparent => renameddir/reparenteddir',
321
'renamed filetoreparent => renameddir/reparentedfile',
322
'deleted dirtoremove',
323
'deleted filetoremove',
324
'Committed revision 2.',
326
]), set(err.split('\n')))
78
328
def test_empty_commit_message(self):
80
file('foo.c', 'wt').write('int main() {}')
81
self.runbzr(['add', 'foo.c'])
82
self.runbzr(["commit", "-m", ""] , retcode=3)
329
tree = self.make_branch_and_tree('.')
330
self.build_tree_contents([('foo.c', 'int main() {}')])
332
self.run_bzr('commit -m ""')
84
334
def test_other_branch_commit(self):
85
335
# this branch is to ensure consistent behaviour, whether we're run
86
336
# inside a branch, or not.
87
os.mkdir('empty_branch')
88
os.chdir('empty_branch')
93
file('foo.c', 'wt').write('int main() {}')
94
file('bar.c', 'wt').write('int main() {}')
96
self.runbzr(['add', 'branch/foo.c'])
97
self.runbzr(['add', 'branch'])
337
outer_tree = self.make_branch_and_tree('.')
338
inner_tree = self.make_branch_and_tree('branch')
339
self.build_tree_contents([
340
('branch/foo.c', 'int main() {}'),
341
('branch/bar.c', 'int main() {}')])
342
inner_tree.add(['foo.c', 'bar.c'])
98
343
# can't commit files in different trees; sane error
99
self.runbzr('commit -m newstuff branch/foo.c .', retcode=3)
100
self.runbzr('commit -m newstuff branch/foo.c')
101
self.runbzr('commit -m newstuff branch')
102
self.runbzr('commit -m newstuff branch', retcode=3)
344
self.run_bzr('commit -m newstuff branch/foo.c .', retcode=3)
345
# can commit to branch - records foo.c only
346
self.run_bzr('commit -m newstuff branch/foo.c')
347
# can commit to branch - records bar.c
348
self.run_bzr('commit -m newstuff branch')
350
self.run_bzr_error(["No changes to commit"], 'commit -m newstuff branch')
104
352
def test_out_of_date_tree_commit(self):
105
353
# check we get an error code and a clear message committing with an out
106
354
# of date checkout
107
self.make_branch_and_tree('branch')
355
tree = self.make_branch_and_tree('branch')
108
356
# make a checkout
109
self.runbzr('checkout --lightweight branch checkout')
357
checkout = tree.branch.create_checkout('checkout', lightweight=True)
110
358
# commit to the original branch to make the checkout out of date
111
self.runbzr('commit --unchanged -m message branch')
359
tree.commit('message branch', allow_pointless=True)
112
360
# now commit to the checkout should emit
113
361
# ERROR: Out of date with the branch, 'bzr update' is suggested
114
output = self.runbzr('commit --unchanged -m checkout_message '
362
output = self.run_bzr('commit --unchanged -m checkout_message '
115
363
'checkout', retcode=3)
116
364
self.assertEqual(output,
118
"bzr: ERROR: Working tree is out of date, please run "
366
"bzr: ERROR: Working tree is out of date, please "
367
"run 'bzr update'.\n"))
121
369
def test_local_commit_unbound(self):
122
370
# a --local commit on an unbound branch is an error
123
371
self.make_branch_and_tree('.')
124
out, err = self.run_bzr('commit', '--local', retcode=3)
372
out, err = self.run_bzr('commit --local', retcode=3)
125
373
self.assertEqualDiff('', out)
126
374
self.assertEqualDiff('bzr: ERROR: Cannot perform local-only commits '
127
375
'on unbound branches.\n', err)
377
def test_commit_a_text_merge_in_a_checkout(self):
378
# checkouts perform multiple actions in a transaction across bond
379
# branches and their master, and have been observed to fail in the
380
# past. This is a user story reported to fail in bug #43959 where
381
# a merge done in a checkout (using the update command) failed to
383
trunk = self.make_branch_and_tree('trunk')
385
u1 = trunk.branch.create_checkout('u1')
386
self.build_tree_contents([('u1/hosts', 'initial contents\n')])
388
self.run_bzr('commit -m add-hosts u1')
390
u2 = trunk.branch.create_checkout('u2')
391
self.build_tree_contents([('u2/hosts', 'altered in u2\n')])
392
self.run_bzr('commit -m checkin-from-u2 u2')
394
# make an offline commits
395
self.build_tree_contents([('u1/hosts', 'first offline change in u1\n')])
396
self.run_bzr('commit -m checkin-offline --local u1')
398
# now try to pull in online work from u2, and then commit our offline
400
# retcode 1 as we expect a text conflict
401
self.run_bzr('update u1', retcode=1)
402
self.assertFileEqual('''\
404
first offline change in u1
411
self.run_bzr('resolved u1/hosts')
412
# add a text change here to represent resolving the merge conflicts in
413
# favour of a new version of the file not identical to either the u1
414
# version or the u2 version.
415
self.build_tree_contents([('u1/hosts', 'merge resolution\n')])
416
self.run_bzr('commit -m checkin-merge-of-the-offline-work-from-u1 u1')
418
def test_commit_exclude_excludes_modified_files(self):
419
"""Commit -x foo should ignore changes to foo."""
420
tree = self.make_branch_and_tree('.')
421
self.build_tree(['a', 'b', 'c'])
422
tree.smart_add(['.'])
423
out, err = self.run_bzr(['commit', '-m', 'test', '-x', 'b'])
424
self.assertFalse('added b' in out)
425
self.assertFalse('added b' in err)
426
# If b was excluded it will still be 'added' in status.
427
out, err = self.run_bzr(['added'])
428
self.assertEqual('b\n', out)
429
self.assertEqual('', err)
431
def test_commit_exclude_twice_uses_both_rules(self):
432
"""Commit -x foo -x bar should ignore changes to foo and bar."""
433
tree = self.make_branch_and_tree('.')
434
self.build_tree(['a', 'b', 'c'])
435
tree.smart_add(['.'])
436
out, err = self.run_bzr(['commit', '-m', 'test', '-x', 'b', '-x', 'c'])
437
self.assertFalse('added b' in out)
438
self.assertFalse('added c' in out)
439
self.assertFalse('added b' in err)
440
self.assertFalse('added c' in err)
441
# If b was excluded it will still be 'added' in status.
442
out, err = self.run_bzr(['added'])
443
self.assertTrue('b\n' in out)
444
self.assertTrue('c\n' in out)
445
self.assertEqual('', err)
447
def test_commit_respects_spec_for_removals(self):
448
"""Commit with a file spec should only commit removals that match"""
449
t = self.make_branch_and_tree('.')
450
self.build_tree(['file-a', 'dir-a/', 'dir-a/file-b'])
451
t.add(['file-a', 'dir-a', 'dir-a/file-b'])
453
t.remove(['file-a', 'dir-a/file-b'])
455
result = self.run_bzr('commit . -m removed-file-b')[1]
456
self.assertNotContainsRe(result, 'file-a')
457
result = self.run_bzr('status')[0]
458
self.assertContainsRe(result, 'removed:\n file-a')
460
def test_strict_commit(self):
461
"""Commit with --strict works if everything is known"""
462
ignores._set_user_ignores([])
463
tree = self.make_branch_and_tree('tree')
464
self.build_tree(['tree/a'])
466
# A simple change should just work
467
self.run_bzr('commit --strict -m adding-a',
470
def test_strict_commit_no_changes(self):
471
"""commit --strict gives "no changes" if there is nothing to commit"""
472
tree = self.make_branch_and_tree('tree')
473
self.build_tree(['tree/a'])
475
tree.commit('adding a')
477
# With no changes, it should just be 'no changes'
478
# Make sure that commit is failing because there is nothing to do
479
self.run_bzr_error(['No changes to commit'],
480
'commit --strict -m no-changes',
483
# But --strict doesn't care if you supply --unchanged
484
self.run_bzr('commit --strict --unchanged -m no-changes',
487
def test_strict_commit_unknown(self):
488
"""commit --strict fails if a file is unknown"""
489
tree = self.make_branch_and_tree('tree')
490
self.build_tree(['tree/a'])
492
tree.commit('adding a')
494
# Add one file so there is a change, but forget the other
495
self.build_tree(['tree/b', 'tree/c'])
497
self.run_bzr_error(['Commit refused because there are unknown files'],
498
'commit --strict -m add-b',
501
# --no-strict overrides --strict
502
self.run_bzr('commit --strict -m add-b --no-strict',
505
def test_fixes_bug_output(self):
506
"""commit --fixes=lp:23452 succeeds without output."""
507
tree = self.make_branch_and_tree('tree')
508
self.build_tree(['tree/hello.txt'])
509
tree.add('hello.txt')
510
output, err = self.run_bzr(
511
'commit -m hello --fixes=lp:23452 tree/hello.txt')
512
self.assertEqual('', output)
513
self.assertContainsRe(err, 'Committing to: .*\n'
515
'Committed revision 1\.\n')
517
def test_no_bugs_no_properties(self):
518
"""If no bugs are fixed, the bugs property is not set.
520
see https://beta.launchpad.net/bzr/+bug/109613
522
tree = self.make_branch_and_tree('tree')
523
self.build_tree(['tree/hello.txt'])
524
tree.add('hello.txt')
525
self.run_bzr( 'commit -m hello tree/hello.txt')
526
# Get the revision properties, ignoring the branch-nick property, which
527
# we don't care about for this test.
528
last_rev = tree.branch.repository.get_revision(tree.last_revision())
529
properties = dict(last_rev.properties)
530
del properties['branch-nick']
531
self.assertFalse('bugs' in properties)
533
def test_fixes_bug_sets_property(self):
534
"""commit --fixes=lp:234 sets the lp:234 revprop to 'fixed'."""
535
tree = self.make_branch_and_tree('tree')
536
self.build_tree(['tree/hello.txt'])
537
tree.add('hello.txt')
538
self.run_bzr('commit -m hello --fixes=lp:234 tree/hello.txt')
540
# Get the revision properties, ignoring the branch-nick property, which
541
# we don't care about for this test.
542
last_rev = tree.branch.repository.get_revision(tree.last_revision())
543
properties = dict(last_rev.properties)
544
del properties['branch-nick']
546
self.assertEqual({'bugs': 'https://launchpad.net/bugs/234 fixed'},
549
def test_fixes_multiple_bugs_sets_properties(self):
550
"""--fixes can be used more than once to show that bugs are fixed."""
551
tree = self.make_branch_and_tree('tree')
552
self.build_tree(['tree/hello.txt'])
553
tree.add('hello.txt')
554
self.run_bzr('commit -m hello --fixes=lp:123 --fixes=lp:235'
557
# Get the revision properties, ignoring the branch-nick property, which
558
# we don't care about for this test.
559
last_rev = tree.branch.repository.get_revision(tree.last_revision())
560
properties = dict(last_rev.properties)
561
del properties['branch-nick']
564
{'bugs': 'https://launchpad.net/bugs/123 fixed\n'
565
'https://launchpad.net/bugs/235 fixed'},
568
def test_fixes_bug_with_alternate_trackers(self):
569
"""--fixes can be used on a properly configured branch to mark bug
570
fixes on multiple trackers.
572
tree = self.make_branch_and_tree('tree')
573
tree.branch.get_config().set_user_option(
574
'trac_twisted_url', 'http://twistedmatrix.com/trac')
575
self.build_tree(['tree/hello.txt'])
576
tree.add('hello.txt')
577
self.run_bzr('commit -m hello --fixes=lp:123 --fixes=twisted:235 tree/')
579
# Get the revision properties, ignoring the branch-nick property, which
580
# we don't care about for this test.
581
last_rev = tree.branch.repository.get_revision(tree.last_revision())
582
properties = dict(last_rev.properties)
583
del properties['branch-nick']
586
{'bugs': 'https://launchpad.net/bugs/123 fixed\n'
587
'http://twistedmatrix.com/trac/ticket/235 fixed'},
590
def test_fixes_unknown_bug_prefix(self):
591
tree = self.make_branch_and_tree('tree')
592
self.build_tree(['tree/hello.txt'])
593
tree.add('hello.txt')
595
["Unrecognized bug %s. Commit refused." % 'xxx:123'],
596
'commit -m add-b --fixes=xxx:123',
599
def test_fixes_bug_with_default_tracker(self):
600
"""commit --fixes=234 uses the default bug tracker."""
601
tree = self.make_branch_and_tree('tree')
602
self.build_tree(['tree/hello.txt'])
603
tree.add('hello.txt')
605
["bzr: ERROR: No tracker specified for bug 123. Use the form "
606
"'tracker:id' or specify a default bug tracker using the "
607
"`bugtracker` option.\n"
608
"See \"bzr help bugs\" for more information on this feature. "
610
'commit -m add-b --fixes=123',
612
tree.branch.get_config().set_user_option("bugtracker", "lp")
613
self.run_bzr('commit -m hello --fixes=234 tree/hello.txt')
614
last_rev = tree.branch.repository.get_revision(tree.last_revision())
615
self.assertEqual('https://launchpad.net/bugs/234 fixed',
616
last_rev.properties['bugs'])
618
def test_fixes_invalid_bug_number(self):
619
tree = self.make_branch_and_tree('tree')
620
self.build_tree(['tree/hello.txt'])
621
tree.add('hello.txt')
623
["Did not understand bug identifier orange: Must be an integer. "
624
"See \"bzr help bugs\" for more information on this feature.\n"
626
'commit -m add-b --fixes=lp:orange',
629
def test_fixes_invalid_argument(self):
630
"""Raise an appropriate error when the fixes argument isn't tag:id."""
631
tree = self.make_branch_and_tree('tree')
632
self.build_tree(['tree/hello.txt'])
633
tree.add('hello.txt')
635
[r"Invalid bug orange:apples:bananas. Must be in the form of "
636
r"'tracker:id'\. See \"bzr help bugs\" for more information on "
637
r"this feature.\nCommit refused\."],
638
'commit -m add-b --fixes=orange:apples:bananas',
641
def test_no_author(self):
642
"""If the author is not specified, the author property is not set."""
643
tree = self.make_branch_and_tree('tree')
644
self.build_tree(['tree/hello.txt'])
645
tree.add('hello.txt')
646
self.run_bzr( 'commit -m hello tree/hello.txt')
647
last_rev = tree.branch.repository.get_revision(tree.last_revision())
648
properties = last_rev.properties
649
self.assertFalse('author' in properties)
651
def test_author_sets_property(self):
652
"""commit --author='John Doe <jdoe@example.com>' sets the author
655
tree = self.make_branch_and_tree('tree')
656
self.build_tree(['tree/hello.txt'])
657
tree.add('hello.txt')
658
self.run_bzr(["commit", '-m', 'hello',
659
'--author', u'John D\xf6 <jdoe@example.com>',
661
last_rev = tree.branch.repository.get_revision(tree.last_revision())
662
properties = last_rev.properties
663
self.assertEqual(u'John D\xf6 <jdoe@example.com>', properties['authors'])
665
def test_author_no_email(self):
666
"""Author's name without an email address is allowed, too."""
667
tree = self.make_branch_and_tree('tree')
668
self.build_tree(['tree/hello.txt'])
669
tree.add('hello.txt')
670
out, err = self.run_bzr("commit -m hello --author='John Doe' "
672
last_rev = tree.branch.repository.get_revision(tree.last_revision())
673
properties = last_rev.properties
674
self.assertEqual('John Doe', properties['authors'])
676
def test_multiple_authors(self):
677
"""Multiple authors can be specyfied, and all are stored."""
678
tree = self.make_branch_and_tree('tree')
679
self.build_tree(['tree/hello.txt'])
680
tree.add('hello.txt')
681
out, err = self.run_bzr("commit -m hello --author='John Doe' "
682
"--author='Jane Rey' tree/hello.txt")
683
last_rev = tree.branch.repository.get_revision(tree.last_revision())
684
properties = last_rev.properties
685
self.assertEqual('John Doe\nJane Rey', properties['authors'])
687
def test_commit_time(self):
688
tree = self.make_branch_and_tree('tree')
689
self.build_tree(['tree/hello.txt'])
690
tree.add('hello.txt')
691
out, err = self.run_bzr("commit -m hello "
692
"--commit-time='2009-10-10 08:00:00 +0100' tree/hello.txt")
693
last_rev = tree.branch.repository.get_revision(tree.last_revision())
695
'Sat 2009-10-10 08:00:00 +0100',
696
osutils.format_date(last_rev.timestamp, last_rev.timezone))
698
def test_commit_time_bad_time(self):
699
tree = self.make_branch_and_tree('tree')
700
self.build_tree(['tree/hello.txt'])
701
tree.add('hello.txt')
702
out, err = self.run_bzr("commit -m hello "
703
"--commit-time='NOT A TIME' tree/hello.txt", retcode=3)
704
self.assertStartsWith(
705
err, "bzr: ERROR: Could not parse --commit-time:")
707
def test_partial_commit_with_renames_in_tree(self):
708
# this test illustrates bug #140419
709
t = self.make_branch_and_tree('.')
710
self.build_tree(['dir/', 'dir/a', 'test'])
711
t.add(['dir/', 'dir/a', 'test'])
712
t.commit('initial commit')
713
# important part: file dir/a should change parent
714
# and should appear before old parent
715
# then during partial commit we have error
716
# parent_id {dir-XXX} not in inventory
717
t.rename_one('dir/a', 'a')
718
self.build_tree_contents([('test', 'changes in test')])
720
out, err = self.run_bzr('commit test -m "partial commit"')
721
self.assertEquals('', out)
722
self.assertContainsRe(err, r'modified test\nCommitted revision 2.')
724
def test_commit_readonly_checkout(self):
725
# https://bugs.launchpad.net/bzr/+bug/129701
726
# "UnlockableTransport error trying to commit in checkout of readonly
728
self.make_branch('master')
729
master = BzrDir.open_from_transport(
730
self.get_readonly_transport('master')).open_branch()
731
master.create_checkout('checkout')
732
out, err = self.run_bzr(['commit', '--unchanged', '-mfoo', 'checkout'],
734
self.assertContainsRe(err,
735
r'^bzr: ERROR: Cannot lock.*readonly transport')
737
def setup_editor(self):
738
# Test that commit template hooks work
739
if sys.platform == "win32":
740
f = file('fed.bat', 'w')
741
f.write('@rem dummy fed')
743
self.overrideEnv('BZR_EDITOR', "fed.bat")
745
f = file('fed.sh', 'wb')
746
f.write('#!/bin/sh\n')
748
os.chmod('fed.sh', 0755)
749
self.overrideEnv('BZR_EDITOR', "./fed.sh")
751
def setup_commit_with_template(self):
753
msgeditor.hooks.install_named_hook("commit_message_template",
754
lambda commit_obj, msg: "save me some typing\n", None)
755
tree = self.make_branch_and_tree('tree')
756
self.build_tree(['tree/hello.txt'])
757
tree.add('hello.txt')
760
def test_edit_empty_message(self):
761
tree = self.make_branch_and_tree('tree')
763
self.build_tree(['tree/hello.txt'])
764
tree.add('hello.txt')
765
out, err = self.run_bzr("commit tree/hello.txt", retcode=3,
767
self.assertContainsRe(err,
768
"bzr: ERROR: Empty commit message specified")
770
def test_commit_hook_template_accepted(self):
771
tree = self.setup_commit_with_template()
772
out, err = self.run_bzr("commit tree/hello.txt", stdin="y\n")
773
last_rev = tree.branch.repository.get_revision(tree.last_revision())
774
self.assertEqual('save me some typing\n', last_rev.message)
776
def test_commit_hook_template_rejected(self):
777
tree = self.setup_commit_with_template()
778
expected = tree.last_revision()
779
out, err = self.run_bzr_error(["Empty commit message specified."
780
" Please specify a commit message with either"
781
" --message or --file or leave a blank message"
782
" with --message \"\"."],
783
"commit tree/hello.txt", stdin="n\n")
784
self.assertEqual(expected, tree.last_revision())
786
def test_set_commit_message(self):
787
msgeditor.hooks.install_named_hook("set_commit_message",
788
lambda commit_obj, msg: "save me some typing\n", None)
789
tree = self.make_branch_and_tree('tree')
790
self.build_tree(['tree/hello.txt'])
791
tree.add('hello.txt')
792
out, err = self.run_bzr("commit tree/hello.txt")
793
last_rev = tree.branch.repository.get_revision(tree.last_revision())
794
self.assertEqual('save me some typing\n', last_rev.message)
796
def test_commit_without_username(self):
797
"""Ensure commit error if username is not set.
799
self.run_bzr(['init', 'foo'])
801
open('foo.txt', 'w').write('hello')
802
self.run_bzr(['add'])
803
self.overrideEnv('EMAIL', None)
804
self.overrideEnv('BZR_EMAIL', None)
805
# Also, make sure that it's not inferred from mailname.
806
self.overrideAttr(config, '_auto_user_id',
807
lambda: (None, None))
808
out, err = self.run_bzr(['commit', '-m', 'initial'], 3)
809
self.assertContainsRe(err, 'Unable to determine your name')
811
def test_commit_recursive_checkout(self):
812
"""Ensure that a commit to a recursive checkout fails cleanly.
814
self.run_bzr(['init', 'test_branch'])
815
self.run_bzr(['checkout', 'test_branch', 'test_checkout'])
816
os.chdir('test_checkout')
817
self.run_bzr(['bind', '.']) # bind to self
818
open('foo.txt', 'w').write('hello')
819
self.run_bzr(['add'])
820
out, err = self.run_bzr(['commit', '-m', 'addedfoo'], 3)
821
self.assertEqual(out, '')
822
self.assertContainsRe(err,
823
'Branch.*test_checkout.*appears to be bound to itself')