/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/blackbox/test_commit.py

  • Committer: Martin Pool
  • Date: 2009-11-14 11:23:07 UTC
  • mto: This revision was merged to the branch mainline in revision 4828.
  • Revision ID: mbp@sourcefrog.net-20091114112307-gezkfgg9ymipqv96
Further cleanup of removal reporting code

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 by Canonical Ltd
2
 
 
 
1
# Copyright (C) 2005, 2006, 2007, 2009 Canonical Ltd
 
2
#
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.
7
 
 
 
7
#
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.
12
 
 
 
12
#
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
16
16
 
17
17
 
18
18
"""Tests for the commit CLI of bzr."""
19
19
 
20
 
from cStringIO import StringIO
21
20
import os
22
 
import re
23
 
import shutil
24
21
import sys
25
22
 
26
 
from bzrlib.branch import Branch
27
 
import bzrlib.bzrdir as bzrdir
28
 
from bzrlib.errors import BzrCommandError
 
23
from bzrlib import (
 
24
    osutils,
 
25
    ignores,
 
26
    msgeditor,
 
27
    osutils,
 
28
    )
 
29
from bzrlib.bzrdir import BzrDir
 
30
from bzrlib.tests import (
 
31
    probe_bad_non_ascii,
 
32
    TestSkipped,
 
33
    )
29
34
from bzrlib.tests.blackbox import ExternalBase
30
 
from bzrlib.workingtree import WorkingTree
31
35
 
32
36
 
33
37
class TestCommit(ExternalBase):
35
39
    def test_05_empty_commit(self):
36
40
        """Commit of tree with no versioned files should fail"""
37
41
        # If forced, it should succeed, but this is not tested here.
38
 
        self.runbzr("init")
 
42
        self.make_branch_and_tree('.')
39
43
        self.build_tree(['hello.txt'])
40
 
        self.runbzr("commit -m empty", retcode=3)
 
44
        out,err = self.run_bzr('commit -m empty', retcode=3)
 
45
        self.assertEqual('', out)
 
46
        self.assertContainsRe(err, 'bzr: ERROR: No changes to commit\.'
 
47
                                  ' Use --unchanged to commit anyhow.\n')
 
48
 
 
49
    def test_commit_success(self):
 
50
        """Successful commit should not leave behind a bzr-commit-* file"""
 
51
        self.make_branch_and_tree('.')
 
52
        self.run_bzr('commit --unchanged -m message')
 
53
        self.assertEqual('', self.run_bzr('unknowns')[0])
 
54
 
 
55
        # same for unicode messages
 
56
        self.run_bzr(["commit", "--unchanged", "-m", u'foo\xb5'])
 
57
        self.assertEqual('', self.run_bzr('unknowns')[0])
 
58
 
 
59
    def test_commit_with_path(self):
 
60
        """Commit tree with path of root specified"""
 
61
        a_tree = self.make_branch_and_tree('a')
 
62
        self.build_tree(['a/a_file'])
 
63
        a_tree.add('a_file')
 
64
        self.run_bzr(['commit', '-m', 'first commit', 'a'])
 
65
 
 
66
        b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
 
67
        self.build_tree_contents([('b/a_file', 'changes in b')])
 
68
        self.run_bzr(['commit', '-m', 'first commit in b', 'b'])
 
69
 
 
70
        self.build_tree_contents([('a/a_file', 'new contents')])
 
71
        self.run_bzr(['commit', '-m', 'change in a', 'a'])
 
72
 
 
73
        b_tree.merge_from_branch(a_tree.branch)
 
74
        self.assertEqual(len(b_tree.conflicts()), 1)
 
75
        self.run_bzr('resolved b/a_file')
 
76
        self.run_bzr(['commit', '-m', 'merge into b', 'b'])
 
77
 
41
78
 
42
79
    def test_10_verbose_commit(self):
43
80
        """Add one file and examine verbose commit output"""
44
 
        self.runbzr("init")
 
81
        tree = self.make_branch_and_tree('.')
45
82
        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',
51
 
                         err)
52
 
 
53
 
    def test_15_verbose_commit_with_unknown(self):
 
83
        tree.add("hello.txt")
 
84
        out,err = self.run_bzr('commit -m added')
 
85
        self.assertEqual('', out)
 
86
        self.assertContainsRe(err, '^Committing to: .*\n'
 
87
                              'added hello.txt\n'
 
88
                              'Committed revision 1.\n$',)
 
89
 
 
90
    def prepare_simple_history(self):
 
91
        """Prepare and return a working tree with one commit of one file"""
 
92
        # Commit with modified file should say so
 
93
        wt = BzrDir.create_standalone_workingtree('.')
 
94
        self.build_tree(['hello.txt', 'extra.txt'])
 
95
        wt.add(['hello.txt'])
 
96
        wt.commit(message='added')
 
97
        return wt
 
98
 
 
99
    def test_verbose_commit_modified(self):
 
100
        # Verbose commit of modified file should say so
 
101
        wt = self.prepare_simple_history()
 
102
        self.build_tree_contents([('hello.txt', 'new contents')])
 
103
        out, err = self.run_bzr('commit -m modified')
 
104
        self.assertEqual('', out)
 
105
        self.assertContainsRe(err, '^Committing to: .*\n'
 
106
                              'modified hello\.txt\n'
 
107
                              'Committed revision 2\.\n$')
 
108
 
 
109
    def test_verbose_commit_renamed(self):
 
110
        # Verbose commit of renamed file should say so
 
111
        wt = self.prepare_simple_history()
 
112
        wt.rename_one('hello.txt', 'gutentag.txt')
 
113
        out, err = self.run_bzr('commit -m renamed')
 
114
        self.assertEqual('', out)
 
115
        self.assertContainsRe(err, '^Committing to: .*\n'
 
116
                              'renamed hello\.txt => gutentag\.txt\n'
 
117
                              'Committed revision 2\.$\n')
 
118
 
 
119
    def test_verbose_commit_moved(self):
 
120
        # Verbose commit of file moved to new directory should say so
 
121
        wt = self.prepare_simple_history()
 
122
        os.mkdir('subdir')
 
123
        wt.add(['subdir'])
 
124
        wt.rename_one('hello.txt', 'subdir/hello.txt')
 
125
        out, err = self.run_bzr('commit -m renamed')
 
126
        self.assertEqual('', out)
 
127
        self.assertEqual(set([
 
128
            'Committing to: %s/' % osutils.getcwd(),
 
129
            'added subdir',
 
130
            'renamed hello.txt => subdir/hello.txt',
 
131
            'Committed revision 2.',
 
132
            '',
 
133
            ]), set(err.split('\n')))
 
134
 
 
135
    def test_verbose_commit_with_unknown(self):
54
136
        """Unknown files should not be listed by default in verbose output"""
55
137
        # Is that really the best policy?
56
 
        self.runbzr("init")
 
138
        wt = BzrDir.create_standalone_workingtree('.')
57
139
        self.build_tree(['hello.txt', 'extra.txt'])
58
 
        self.runbzr("add hello.txt")
59
 
        out,err = self.run_bzr("commit", "-m", "added")
 
140
        wt.add(['hello.txt'])
 
141
        out,err = self.run_bzr('commit -m added')
60
142
        self.assertEqual('', out)
61
 
        self.assertEqual('added hello.txt\n'
62
 
                         'Committed revision 1.\n',
63
 
                         err)
 
143
        self.assertContainsRe(err, '^Committing to: .*\n'
 
144
                              'added hello\.txt\n'
 
145
                              'Committed revision 1\.\n$')
64
146
 
65
 
    def test_16_verbose_commit_with_unchanged(self):
 
147
    def test_verbose_commit_with_unchanged(self):
66
148
        """Unchanged files should not be listed by default in verbose output"""
67
 
        self.runbzr("init")
 
149
        tree = self.make_branch_and_tree('.')
68
150
        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',
76
 
                         err)
 
151
        tree.add('unchanged.txt')
 
152
        self.run_bzr('commit -m unchanged unchanged.txt')
 
153
        tree.add("hello.txt")
 
154
        out,err = self.run_bzr('commit -m added')
 
155
        self.assertEqual('', out)
 
156
        self.assertContainsRe(err, '^Committing to: .*\n'
 
157
                              'added hello\.txt\n'
 
158
                              'Committed revision 2\.$\n')
 
159
 
 
160
    def test_verbose_commit_includes_master_location(self):
 
161
        """Location of master is displayed when committing to bound branch"""
 
162
        a_tree = self.make_branch_and_tree('a')
 
163
        self.build_tree(['a/b'])
 
164
        a_tree.add('b')
 
165
        a_tree.commit(message='Initial message')
 
166
 
 
167
        b_tree = a_tree.branch.create_checkout('b')
 
168
        expected = "%s/" % (osutils.abspath('a'), )
 
169
        out, err = self.run_bzr('commit -m blah --unchanged', working_dir='b')
 
170
        self.assertEqual(err, 'Committing to: %s\n'
 
171
                         'Committed revision 2.\n' % expected)
 
172
 
 
173
    def test_commit_sanitizes_CR_in_message(self):
 
174
        # See bug #433779, basically Emacs likes to pass '\r\n' style line
 
175
        # endings to 'bzr commit -m ""' which breaks because we don't allow
 
176
        # '\r' in commit messages. (Mostly because of issues where XML style
 
177
        # formats arbitrarily strip it out of the data while parsing.)
 
178
        # To make life easier for users, we just always translate '\r\n' =>
 
179
        # '\n'. And '\r' => '\n'.
 
180
        a_tree = self.make_branch_and_tree('a')
 
181
        self.build_tree(['a/b'])
 
182
        a_tree.add('b')
 
183
        self.run_bzr(['commit',
 
184
                      '-m', 'a string\r\n\r\nwith mixed\r\rendings\n'],
 
185
                     working_dir='a')
 
186
        rev_id = a_tree.branch.last_revision()
 
187
        rev = a_tree.branch.repository.get_revision(rev_id)
 
188
        self.assertEqualDiff('a string\n\nwith mixed\n\nendings\n',
 
189
                             rev.message)
 
190
 
 
191
    def test_commit_merge_reports_all_modified_files(self):
 
192
        # the commit command should show all the files that are shown by
 
193
        # bzr diff or bzr status when committing, even when they were not
 
194
        # changed by the user but rather through doing a merge.
 
195
        this_tree = self.make_branch_and_tree('this')
 
196
        # we need a bunch of files and dirs, to perform one action on each.
 
197
        self.build_tree([
 
198
            'this/dirtorename/',
 
199
            'this/dirtoreparent/',
 
200
            'this/dirtoleave/',
 
201
            'this/dirtoremove/',
 
202
            'this/filetoreparent',
 
203
            'this/filetorename',
 
204
            'this/filetomodify',
 
205
            'this/filetoremove',
 
206
            'this/filetoleave']
 
207
            )
 
208
        this_tree.add([
 
209
            'dirtorename',
 
210
            'dirtoreparent',
 
211
            'dirtoleave',
 
212
            'dirtoremove',
 
213
            'filetoreparent',
 
214
            'filetorename',
 
215
            'filetomodify',
 
216
            'filetoremove',
 
217
            'filetoleave']
 
218
            )
 
219
        this_tree.commit('create_files')
 
220
        other_dir = this_tree.bzrdir.sprout('other')
 
221
        other_tree = other_dir.open_workingtree()
 
222
        other_tree.lock_write()
 
223
        # perform the needed actions on the files and dirs.
 
224
        try:
 
225
            other_tree.rename_one('dirtorename', 'renameddir')
 
226
            other_tree.rename_one('dirtoreparent', 'renameddir/reparenteddir')
 
227
            other_tree.rename_one('filetorename', 'renamedfile')
 
228
            other_tree.rename_one('filetoreparent',
 
229
                                  'renameddir/reparentedfile')
 
230
            other_tree.remove(['dirtoremove', 'filetoremove'])
 
231
            self.build_tree_contents([
 
232
                ('other/newdir/',),
 
233
                ('other/filetomodify', 'new content'),
 
234
                ('other/newfile', 'new file content')])
 
235
            other_tree.add('newfile')
 
236
            other_tree.add('newdir/')
 
237
            other_tree.commit('modify all sample files and dirs.')
 
238
        finally:
 
239
            other_tree.unlock()
 
240
        this_tree.merge_from_branch(other_tree.branch)
 
241
        os.chdir('this')
 
242
        out,err = self.run_bzr('commit -m added')
 
243
        self.assertEqual('', out)
 
244
        self.assertEqual(set([
 
245
            'Committing to: %s/' % osutils.getcwd(),
 
246
            'modified filetomodify',
 
247
            'added newdir',
 
248
            'added newfile',
 
249
            'renamed dirtorename => renameddir',
 
250
            'renamed filetorename => renamedfile',
 
251
            'renamed dirtoreparent => renameddir/reparenteddir',
 
252
            'renamed filetoreparent => renameddir/reparentedfile',
 
253
            'deleted dirtoremove',
 
254
            'deleted filetoremove',
 
255
            'Committed revision 2.',
 
256
            ''
 
257
            ]), set(err.split('\n')))
77
258
 
78
259
    def test_empty_commit_message(self):
79
 
        self.runbzr("init")
80
 
        file('foo.c', 'wt').write('int main() {}')
81
 
        self.runbzr(['add', 'foo.c'])
82
 
        self.runbzr(["commit", "-m", ""] , retcode=3)
 
260
        tree = self.make_branch_and_tree('.')
 
261
        self.build_tree_contents([('foo.c', 'int main() {}')])
 
262
        tree.add('foo.c')
 
263
        self.run_bzr('commit -m ""', retcode=3)
 
264
 
 
265
    def test_unsupported_encoding_commit_message(self):
 
266
        tree = self.make_branch_and_tree('.')
 
267
        self.build_tree_contents([('foo.c', 'int main() {}')])
 
268
        tree.add('foo.c')
 
269
        # LANG env variable has no effect on Windows
 
270
        # but some characters anyway cannot be represented
 
271
        # in default user encoding
 
272
        char = probe_bad_non_ascii(osutils.get_user_encoding())
 
273
        if char is None:
 
274
            raise TestSkipped('Cannot find suitable non-ascii character'
 
275
                'for user_encoding (%s)' % osutils.get_user_encoding())
 
276
        # TODO: jam 2009-07-23 This test seems to fail on Windows now. My best
 
277
        #       guess is that the change to use Unicode command lines means
 
278
        #       that we no longer pay any attention to LANG=C when decoding the
 
279
        #       commandline arguments.
 
280
        out,err = self.run_bzr_subprocess('commit -m "%s"' % char,
 
281
                                          retcode=1,
 
282
                                          env_changes={'LANG': 'C'})
 
283
        self.assertContainsRe(err, r'bzrlib.errors.BzrError: Parameter.*is '
 
284
                                    'unsupported by the current encoding.')
83
285
 
84
286
    def test_other_branch_commit(self):
85
287
        # this branch is to ensure consistent behaviour, whether we're run
86
288
        # inside a branch, or not.
87
 
        os.mkdir('empty_branch')
88
 
        os.chdir('empty_branch')
89
 
        self.runbzr('init')
90
 
        os.mkdir('branch')
91
 
        os.chdir('branch')
92
 
        self.runbzr('init')
93
 
        file('foo.c', 'wt').write('int main() {}')
94
 
        file('bar.c', 'wt').write('int main() {}')
95
 
        os.chdir('..')
96
 
        self.runbzr(['add', 'branch/foo.c'])
97
 
        self.runbzr(['add', 'branch'])
 
289
        outer_tree = self.make_branch_and_tree('.')
 
290
        inner_tree = self.make_branch_and_tree('branch')
 
291
        self.build_tree_contents([
 
292
            ('branch/foo.c', 'int main() {}'),
 
293
            ('branch/bar.c', 'int main() {}')])
 
294
        inner_tree.add(['foo.c', 'bar.c'])
98
295
        # 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)
 
296
        self.run_bzr('commit -m newstuff branch/foo.c .', retcode=3)
 
297
        # can commit to branch - records foo.c only
 
298
        self.run_bzr('commit -m newstuff branch/foo.c')
 
299
        # can commit to branch - records bar.c
 
300
        self.run_bzr('commit -m newstuff branch')
 
301
        # No changes left
 
302
        self.run_bzr_error(["No changes to commit"], 'commit -m newstuff branch')
103
303
 
104
304
    def test_out_of_date_tree_commit(self):
105
305
        # check we get an error code and a clear message committing with an out
106
306
        # of date checkout
107
 
        self.make_branch_and_tree('branch')
 
307
        tree = self.make_branch_and_tree('branch')
108
308
        # make a checkout
109
 
        self.runbzr('checkout --lightweight branch checkout')
 
309
        checkout = tree.branch.create_checkout('checkout', lightweight=True)
110
310
        # commit to the original branch to make the checkout out of date
111
 
        self.runbzr('commit --unchanged -m message branch')
 
311
        tree.commit('message branch', allow_pointless=True)
112
312
        # now commit to the checkout should emit
113
313
        # ERROR: Out of date with the branch, 'bzr update' is suggested
114
 
        output = self.runbzr('commit --unchanged -m checkout_message '
 
314
        output = self.run_bzr('commit --unchanged -m checkout_message '
115
315
                             'checkout', retcode=3)
116
316
        self.assertEqual(output,
117
317
                         ('',
118
 
                          "bzr: ERROR: Working tree is out of date, please run "
119
 
                          "'bzr update'.\n"))
 
318
                          "bzr: ERROR: Working tree is out of date, please "
 
319
                          "run 'bzr update'.\n"))
120
320
 
121
321
    def test_local_commit_unbound(self):
122
322
        # a --local commit on an unbound branch is an error
123
323
        self.make_branch_and_tree('.')
124
 
        out, err = self.run_bzr('commit', '--local', retcode=3)
 
324
        out, err = self.run_bzr('commit --local', retcode=3)
125
325
        self.assertEqualDiff('', out)
126
326
        self.assertEqualDiff('bzr: ERROR: Cannot perform local-only commits '
127
327
                             'on unbound branches.\n', err)
 
328
 
 
329
    def test_commit_a_text_merge_in_a_checkout(self):
 
330
        # checkouts perform multiple actions in a transaction across bond
 
331
        # branches and their master, and have been observed to fail in the
 
332
        # past. This is a user story reported to fail in bug #43959 where
 
333
        # a merge done in a checkout (using the update command) failed to
 
334
        # commit correctly.
 
335
        trunk = self.make_branch_and_tree('trunk')
 
336
 
 
337
        u1 = trunk.branch.create_checkout('u1')
 
338
        self.build_tree_contents([('u1/hosts', 'initial contents')])
 
339
        u1.add('hosts')
 
340
        self.run_bzr('commit -m add-hosts u1')
 
341
 
 
342
        u2 = trunk.branch.create_checkout('u2')
 
343
        self.build_tree_contents([('u2/hosts', 'altered in u2')])
 
344
        self.run_bzr('commit -m checkin-from-u2 u2')
 
345
 
 
346
        # make an offline commits
 
347
        self.build_tree_contents([('u1/hosts', 'first offline change in u1')])
 
348
        self.run_bzr('commit -m checkin-offline --local u1')
 
349
 
 
350
        # now try to pull in online work from u2, and then commit our offline
 
351
        # work as a merge
 
352
        # retcode 1 as we expect a text conflict
 
353
        self.run_bzr('update u1', retcode=1)
 
354
        self.run_bzr('resolved u1/hosts')
 
355
        # add a text change here to represent resolving the merge conflicts in
 
356
        # favour of a new version of the file not identical to either the u1
 
357
        # version or the u2 version.
 
358
        self.build_tree_contents([('u1/hosts', 'merge resolution\n')])
 
359
        self.run_bzr('commit -m checkin-merge-of-the-offline-work-from-u1 u1')
 
360
 
 
361
    def test_commit_exclude_excludes_modified_files(self):
 
362
        """Commit -x foo should ignore changes to foo."""
 
363
        tree = self.make_branch_and_tree('.')
 
364
        self.build_tree(['a', 'b', 'c'])
 
365
        tree.smart_add(['.'])
 
366
        out, err = self.run_bzr(['commit', '-m', 'test', '-x', 'b'])
 
367
        self.assertFalse('added b' in out)
 
368
        self.assertFalse('added b' in err)
 
369
        # If b was excluded it will still be 'added' in status.
 
370
        out, err = self.run_bzr(['added'])
 
371
        self.assertEqual('b\n', out)
 
372
        self.assertEqual('', err)
 
373
 
 
374
    def test_commit_exclude_twice_uses_both_rules(self):
 
375
        """Commit -x foo -x bar should ignore changes to foo and bar."""
 
376
        tree = self.make_branch_and_tree('.')
 
377
        self.build_tree(['a', 'b', 'c'])
 
378
        tree.smart_add(['.'])
 
379
        out, err = self.run_bzr(['commit', '-m', 'test', '-x', 'b', '-x', 'c'])
 
380
        self.assertFalse('added b' in out)
 
381
        self.assertFalse('added c' in out)
 
382
        self.assertFalse('added b' in err)
 
383
        self.assertFalse('added c' in err)
 
384
        # If b was excluded it will still be 'added' in status.
 
385
        out, err = self.run_bzr(['added'])
 
386
        self.assertTrue('b\n' in out)
 
387
        self.assertTrue('c\n' in out)
 
388
        self.assertEqual('', err)
 
389
 
 
390
    def test_commit_respects_spec_for_removals(self):
 
391
        """Commit with a file spec should only commit removals that match"""
 
392
        t = self.make_branch_and_tree('.')
 
393
        self.build_tree(['file-a', 'dir-a/', 'dir-a/file-b'])
 
394
        t.add(['file-a', 'dir-a', 'dir-a/file-b'])
 
395
        t.commit('Create')
 
396
        t.remove(['file-a', 'dir-a/file-b'])
 
397
        os.chdir('dir-a')
 
398
        result = self.run_bzr('commit . -m removed-file-b')[1]
 
399
        self.assertNotContainsRe(result, 'file-a')
 
400
        result = self.run_bzr('status')[0]
 
401
        self.assertContainsRe(result, 'removed:\n  file-a')
 
402
 
 
403
    def test_strict_commit(self):
 
404
        """Commit with --strict works if everything is known"""
 
405
        ignores._set_user_ignores([])
 
406
        tree = self.make_branch_and_tree('tree')
 
407
        self.build_tree(['tree/a'])
 
408
        tree.add('a')
 
409
        # A simple change should just work
 
410
        self.run_bzr('commit --strict -m adding-a',
 
411
                     working_dir='tree')
 
412
 
 
413
    def test_strict_commit_no_changes(self):
 
414
        """commit --strict gives "no changes" if there is nothing to commit"""
 
415
        tree = self.make_branch_and_tree('tree')
 
416
        self.build_tree(['tree/a'])
 
417
        tree.add('a')
 
418
        tree.commit('adding a')
 
419
 
 
420
        # With no changes, it should just be 'no changes'
 
421
        # Make sure that commit is failing because there is nothing to do
 
422
        self.run_bzr_error(['No changes to commit'],
 
423
                           'commit --strict -m no-changes',
 
424
                           working_dir='tree')
 
425
 
 
426
        # But --strict doesn't care if you supply --unchanged
 
427
        self.run_bzr('commit --strict --unchanged -m no-changes',
 
428
                     working_dir='tree')
 
429
 
 
430
    def test_strict_commit_unknown(self):
 
431
        """commit --strict fails if a file is unknown"""
 
432
        tree = self.make_branch_and_tree('tree')
 
433
        self.build_tree(['tree/a'])
 
434
        tree.add('a')
 
435
        tree.commit('adding a')
 
436
 
 
437
        # Add one file so there is a change, but forget the other
 
438
        self.build_tree(['tree/b', 'tree/c'])
 
439
        tree.add('b')
 
440
        self.run_bzr_error(['Commit refused because there are unknown files'],
 
441
                           'commit --strict -m add-b',
 
442
                           working_dir='tree')
 
443
 
 
444
        # --no-strict overrides --strict
 
445
        self.run_bzr('commit --strict -m add-b --no-strict',
 
446
                     working_dir='tree')
 
447
 
 
448
    def test_fixes_bug_output(self):
 
449
        """commit --fixes=lp:23452 succeeds without output."""
 
450
        tree = self.make_branch_and_tree('tree')
 
451
        self.build_tree(['tree/hello.txt'])
 
452
        tree.add('hello.txt')
 
453
        output, err = self.run_bzr(
 
454
            'commit -m hello --fixes=lp:23452 tree/hello.txt')
 
455
        self.assertEqual('', output)
 
456
        self.assertContainsRe(err, 'Committing to: .*\n'
 
457
                              'added hello\.txt\n'
 
458
                              'Committed revision 1\.\n')
 
459
 
 
460
    def test_no_bugs_no_properties(self):
 
461
        """If no bugs are fixed, the bugs property is not set.
 
462
 
 
463
        see https://beta.launchpad.net/bzr/+bug/109613
 
464
        """
 
465
        tree = self.make_branch_and_tree('tree')
 
466
        self.build_tree(['tree/hello.txt'])
 
467
        tree.add('hello.txt')
 
468
        self.run_bzr( 'commit -m hello tree/hello.txt')
 
469
        # Get the revision properties, ignoring the branch-nick property, which
 
470
        # we don't care about for this test.
 
471
        last_rev = tree.branch.repository.get_revision(tree.last_revision())
 
472
        properties = dict(last_rev.properties)
 
473
        del properties['branch-nick']
 
474
        self.assertFalse('bugs' in properties)
 
475
 
 
476
    def test_fixes_bug_sets_property(self):
 
477
        """commit --fixes=lp:234 sets the lp:234 revprop to 'fixed'."""
 
478
        tree = self.make_branch_and_tree('tree')
 
479
        self.build_tree(['tree/hello.txt'])
 
480
        tree.add('hello.txt')
 
481
        self.run_bzr('commit -m hello --fixes=lp:234 tree/hello.txt')
 
482
 
 
483
        # Get the revision properties, ignoring the branch-nick property, which
 
484
        # we don't care about for this test.
 
485
        last_rev = tree.branch.repository.get_revision(tree.last_revision())
 
486
        properties = dict(last_rev.properties)
 
487
        del properties['branch-nick']
 
488
 
 
489
        self.assertEqual({'bugs': 'https://launchpad.net/bugs/234 fixed'},
 
490
                         properties)
 
491
 
 
492
    def test_fixes_multiple_bugs_sets_properties(self):
 
493
        """--fixes can be used more than once to show that bugs are fixed."""
 
494
        tree = self.make_branch_and_tree('tree')
 
495
        self.build_tree(['tree/hello.txt'])
 
496
        tree.add('hello.txt')
 
497
        self.run_bzr('commit -m hello --fixes=lp:123 --fixes=lp:235'
 
498
                     ' tree/hello.txt')
 
499
 
 
500
        # Get the revision properties, ignoring the branch-nick property, which
 
501
        # we don't care about for this test.
 
502
        last_rev = tree.branch.repository.get_revision(tree.last_revision())
 
503
        properties = dict(last_rev.properties)
 
504
        del properties['branch-nick']
 
505
 
 
506
        self.assertEqual(
 
507
            {'bugs': 'https://launchpad.net/bugs/123 fixed\n'
 
508
                     'https://launchpad.net/bugs/235 fixed'},
 
509
            properties)
 
510
 
 
511
    def test_fixes_bug_with_alternate_trackers(self):
 
512
        """--fixes can be used on a properly configured branch to mark bug
 
513
        fixes on multiple trackers.
 
514
        """
 
515
        tree = self.make_branch_and_tree('tree')
 
516
        tree.branch.get_config().set_user_option(
 
517
            'trac_twisted_url', 'http://twistedmatrix.com/trac')
 
518
        self.build_tree(['tree/hello.txt'])
 
519
        tree.add('hello.txt')
 
520
        self.run_bzr('commit -m hello --fixes=lp:123 --fixes=twisted:235 tree/')
 
521
 
 
522
        # Get the revision properties, ignoring the branch-nick property, which
 
523
        # we don't care about for this test.
 
524
        last_rev = tree.branch.repository.get_revision(tree.last_revision())
 
525
        properties = dict(last_rev.properties)
 
526
        del properties['branch-nick']
 
527
 
 
528
        self.assertEqual(
 
529
            {'bugs': 'https://launchpad.net/bugs/123 fixed\n'
 
530
                     'http://twistedmatrix.com/trac/ticket/235 fixed'},
 
531
            properties)
 
532
 
 
533
    def test_fixes_unknown_bug_prefix(self):
 
534
        tree = self.make_branch_and_tree('tree')
 
535
        self.build_tree(['tree/hello.txt'])
 
536
        tree.add('hello.txt')
 
537
        self.run_bzr_error(
 
538
            ["Unrecognized bug %s. Commit refused." % 'xxx:123'],
 
539
            'commit -m add-b --fixes=xxx:123',
 
540
            working_dir='tree')
 
541
 
 
542
    def test_fixes_invalid_bug_number(self):
 
543
        tree = self.make_branch_and_tree('tree')
 
544
        self.build_tree(['tree/hello.txt'])
 
545
        tree.add('hello.txt')
 
546
        self.run_bzr_error(
 
547
            ["Did not understand bug identifier orange: Must be an integer. "
 
548
             "See \"bzr help bugs\" for more information on this feature.\n"
 
549
             "Commit refused."],
 
550
            'commit -m add-b --fixes=lp:orange',
 
551
            working_dir='tree')
 
552
 
 
553
    def test_fixes_invalid_argument(self):
 
554
        """Raise an appropriate error when the fixes argument isn't tag:id."""
 
555
        tree = self.make_branch_and_tree('tree')
 
556
        self.build_tree(['tree/hello.txt'])
 
557
        tree.add('hello.txt')
 
558
        self.run_bzr_error(
 
559
            [r"Invalid bug orange. Must be in the form of 'tracker:id'\. "
 
560
             r"See \"bzr help bugs\" for more information on this feature.\n"
 
561
             r"Commit refused\."],
 
562
            'commit -m add-b --fixes=orange',
 
563
            working_dir='tree')
 
564
 
 
565
    def test_no_author(self):
 
566
        """If the author is not specified, the author property is not set."""
 
567
        tree = self.make_branch_and_tree('tree')
 
568
        self.build_tree(['tree/hello.txt'])
 
569
        tree.add('hello.txt')
 
570
        self.run_bzr( 'commit -m hello tree/hello.txt')
 
571
        last_rev = tree.branch.repository.get_revision(tree.last_revision())
 
572
        properties = last_rev.properties
 
573
        self.assertFalse('author' in properties)
 
574
 
 
575
    def test_author_sets_property(self):
 
576
        """commit --author='John Doe <jdoe@example.com>' sets the author
 
577
           revprop.
 
578
        """
 
579
        tree = self.make_branch_and_tree('tree')
 
580
        self.build_tree(['tree/hello.txt'])
 
581
        tree.add('hello.txt')
 
582
        self.run_bzr(["commit", '-m', 'hello',
 
583
                      '--author', u'John D\xf6 <jdoe@example.com>',
 
584
                     "tree/hello.txt"])
 
585
        last_rev = tree.branch.repository.get_revision(tree.last_revision())
 
586
        properties = last_rev.properties
 
587
        self.assertEqual(u'John D\xf6 <jdoe@example.com>', properties['authors'])
 
588
 
 
589
    def test_author_no_email(self):
 
590
        """Author's name without an email address is allowed, too."""
 
591
        tree = self.make_branch_and_tree('tree')
 
592
        self.build_tree(['tree/hello.txt'])
 
593
        tree.add('hello.txt')
 
594
        out, err = self.run_bzr("commit -m hello --author='John Doe' "
 
595
                                "tree/hello.txt")
 
596
        last_rev = tree.branch.repository.get_revision(tree.last_revision())
 
597
        properties = last_rev.properties
 
598
        self.assertEqual('John Doe', properties['authors'])
 
599
 
 
600
    def test_multiple_authors(self):
 
601
        """Multiple authors can be specyfied, and all are stored."""
 
602
        tree = self.make_branch_and_tree('tree')
 
603
        self.build_tree(['tree/hello.txt'])
 
604
        tree.add('hello.txt')
 
605
        out, err = self.run_bzr("commit -m hello --author='John Doe' "
 
606
                                "--author='Jane Rey' tree/hello.txt")
 
607
        last_rev = tree.branch.repository.get_revision(tree.last_revision())
 
608
        properties = last_rev.properties
 
609
        self.assertEqual('John Doe\nJane Rey', properties['authors'])
 
610
 
 
611
    def test_partial_commit_with_renames_in_tree(self):
 
612
        # this test illustrates bug #140419
 
613
        t = self.make_branch_and_tree('.')
 
614
        self.build_tree(['dir/', 'dir/a', 'test'])
 
615
        t.add(['dir/', 'dir/a', 'test'])
 
616
        t.commit('initial commit')
 
617
        # important part: file dir/a should change parent
 
618
        # and should appear before old parent
 
619
        # then during partial commit we have error
 
620
        # parent_id {dir-XXX} not in inventory
 
621
        t.rename_one('dir/a', 'a')
 
622
        self.build_tree_contents([('test', 'changes in test')])
 
623
        # partial commit
 
624
        out, err = self.run_bzr('commit test -m "partial commit"')
 
625
        self.assertEquals('', out)
 
626
        self.assertContainsRe(err, r'modified test\nCommitted revision 2.')
 
627
 
 
628
    def test_commit_readonly_checkout(self):
 
629
        # https://bugs.edge.launchpad.net/bzr/+bug/129701
 
630
        # "UnlockableTransport error trying to commit in checkout of readonly
 
631
        # branch"
 
632
        self.make_branch('master')
 
633
        master = BzrDir.open_from_transport(
 
634
            self.get_readonly_transport('master')).open_branch()
 
635
        master.create_checkout('checkout')
 
636
        out, err = self.run_bzr(['commit', '--unchanged', '-mfoo', 'checkout'],
 
637
            retcode=3)
 
638
        self.assertContainsRe(err,
 
639
            r'^bzr: ERROR: Cannot lock.*readonly transport')
 
640
 
 
641
    def test_commit_hook_template(self):
 
642
        # Test that commit template hooks work
 
643
        def restoreDefaults():
 
644
            msgeditor.hooks['commit_message_template'] = []
 
645
            osutils.set_or_unset_env('BZR_EDITOR', default_editor)
 
646
        if sys.platform == "win32":
 
647
            f = file('fed.bat', 'w')
 
648
            f.write('@rem dummy fed')
 
649
            f.close()
 
650
            default_editor = osutils.set_or_unset_env('BZR_EDITOR', "fed.bat")
 
651
        else:
 
652
            f = file('fed.sh', 'wb')
 
653
            f.write('#!/bin/sh\n')
 
654
            f.close()
 
655
            os.chmod('fed.sh', 0755)
 
656
            default_editor = osutils.set_or_unset_env('BZR_EDITOR', "./fed.sh")
 
657
        self.addCleanup(restoreDefaults)
 
658
        msgeditor.hooks.install_named_hook("commit_message_template",
 
659
                lambda commit_obj, msg: "save me some typing\n", None)
 
660
        tree = self.make_branch_and_tree('tree')
 
661
        self.build_tree(['tree/hello.txt'])
 
662
        tree.add('hello.txt')
 
663
        out, err = self.run_bzr("commit tree/hello.txt")
 
664
        last_rev = tree.branch.repository.get_revision(tree.last_revision())
 
665
        self.assertEqual('save me some typing\n', last_rev.message)