/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: Parth Malwankar
  • Date: 2010-04-20 10:26:38 UTC
  • mto: This revision was merged to the branch mainline in revision 5214.
  • Revision ID: parth.malwankar@gmail.com-20100420102638-90omempm4tiv1sxm
removed blank line from test case.

Show diffs side-by-side

added added

removed removed

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