~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: John Arbash Meinel
  • Date: 2008-08-28 20:13:31 UTC
  • mfrom: (3658 +trunk)
  • mto: This revision was merged to the branch mainline in revision 3688.
  • Revision ID: john@arbash-meinel.com-20080828201331-dqffxf54l2heokll
Merge bzr.dev 3658

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2011 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
2
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
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
 
18
18
"""Tests for the commit CLI of bzr."""
19
19
 
20
 
import doctest
21
20
import os
22
 
import re
23
21
import sys
24
22
 
25
 
from testtools.matchers import DocTestMatches
26
 
 
 
23
import bzrlib
27
24
from bzrlib import (
28
 
    config,
29
25
    osutils,
30
26
    ignores,
31
 
    msgeditor,
32
 
    tests,
 
27
    osutils,
33
28
    )
34
29
from bzrlib.bzrdir import BzrDir
35
30
from bzrlib.tests import (
36
31
    probe_bad_non_ascii,
37
 
    test_foreign,
38
32
    TestSkipped,
39
 
    features,
40
33
    )
41
 
from bzrlib.tests import TestCaseWithTransport
42
 
 
43
 
 
44
 
class TestCommit(TestCaseWithTransport):
 
34
from bzrlib.tests.blackbox import ExternalBase
 
35
 
 
36
 
 
37
class TestCommit(ExternalBase):
45
38
 
46
39
    def test_05_empty_commit(self):
47
40
        """Commit of tree with no versioned files should fail"""
50
43
        self.build_tree(['hello.txt'])
51
44
        out,err = self.run_bzr('commit -m empty', retcode=3)
52
45
        self.assertEqual('', out)
53
 
        # Two ugly bits here.
54
 
        # 1) We really don't want 'aborting commit write group' anymore.
55
 
        # 2) bzr: ERROR: is a really long line, so we wrap it with '\'
56
 
        self.assertThat(
57
 
            err,
58
 
            DocTestMatches("""\
59
 
Committing to: ...
60
 
bzr: ERROR: No changes to commit.\
61
 
 Please 'bzr add' the files you want to commit,\
62
 
 or use --unchanged to force an empty commit.
63
 
""", flags=doctest.ELLIPSIS|doctest.REPORT_UDIFF))
 
46
        self.assertContainsRe(err, 'bzr: ERROR: no changes to commit\.'
 
47
                                  ' use --unchanged to commit anyhow\n')
64
48
 
65
49
    def test_commit_success(self):
66
50
        """Successful commit should not leave behind a bzr-commit-* file"""
72
56
        self.run_bzr(["commit", "--unchanged", "-m", u'foo\xb5'])
73
57
        self.assertEqual('', self.run_bzr('unknowns')[0])
74
58
 
75
 
    def test_commit_lossy_native(self):
76
 
        """A --lossy option to commit is supported."""
77
 
        self.make_branch_and_tree('.')
78
 
        self.run_bzr('commit --lossy --unchanged -m message')
79
 
        self.assertEqual('', self.run_bzr('unknowns')[0])
80
 
 
81
 
    def test_commit_lossy_foreign(self):
82
 
        test_foreign.register_dummy_foreign_for_test(self)
83
 
        self.make_branch_and_tree('.',
84
 
            format=test_foreign.DummyForeignVcsDirFormat())
85
 
        self.run_bzr('commit --lossy --unchanged -m message')
86
 
        output = self.run_bzr('revision-info')[0]
87
 
        self.assertTrue(output.startswith('1 dummy-'))
88
 
 
89
59
    def test_commit_with_path(self):
90
60
        """Commit tree with path of root specified"""
91
61
        a_tree = self.make_branch_and_tree('a')
105
75
        self.run_bzr('resolved b/a_file')
106
76
        self.run_bzr(['commit', '-m', 'merge into b', 'b'])
107
77
 
 
78
 
108
79
    def test_10_verbose_commit(self):
109
80
        """Add one file and examine verbose commit output"""
110
81
        tree = self.make_branch_and_tree('.')
135
106
                              'modified hello\.txt\n'
136
107
                              'Committed revision 2\.\n$')
137
108
 
138
 
    def test_unicode_commit_message_is_filename(self):
139
 
        """Unicode commit message same as a filename (Bug #563646).
140
 
        """
141
 
        self.requireFeature(features.UnicodeFilenameFeature)
142
 
        file_name = u'\N{euro sign}'
143
 
        self.run_bzr(['init'])
144
 
        open(file_name, 'w').write('hello world')
145
 
        self.run_bzr(['add'])
146
 
        out, err = self.run_bzr(['commit', '-m', file_name])
147
 
        reflags = re.MULTILINE|re.DOTALL|re.UNICODE
148
 
        te = osutils.get_terminal_encoding()
149
 
        self.assertContainsRe(err.decode(te),
150
 
            u'The commit message is a file name:',
151
 
            flags=reflags)
152
 
 
153
 
        # Run same test with a filename that causes encode
154
 
        # error for the terminal encoding. We do this
155
 
        # by forcing terminal encoding of ascii for
156
 
        # osutils.get_terminal_encoding which is used
157
 
        # by ui.text.show_warning
158
 
        default_get_terminal_enc = osutils.get_terminal_encoding
159
 
        try:
160
 
            osutils.get_terminal_encoding = lambda trace=None: 'ascii'
161
 
            file_name = u'foo\u1234'
162
 
            open(file_name, 'w').write('hello world')
163
 
            self.run_bzr(['add'])
164
 
            out, err = self.run_bzr(['commit', '-m', file_name])
165
 
            reflags = re.MULTILINE|re.DOTALL|re.UNICODE
166
 
            te = osutils.get_terminal_encoding()
167
 
            self.assertContainsRe(err.decode(te, 'replace'),
168
 
                u'The commit message is a file name:',
169
 
                flags=reflags)
170
 
        finally:
171
 
            osutils.get_terminal_encoding = default_get_terminal_enc
172
 
 
173
 
    def test_warn_about_forgotten_commit_message(self):
174
 
        """Test that the lack of -m parameter is caught"""
175
 
        wt = self.make_branch_and_tree('.')
176
 
        self.build_tree(['one', 'two'])
177
 
        wt.add(['two'])
178
 
        out, err = self.run_bzr('commit -m one two')
179
 
        self.assertContainsRe(err, "The commit message is a file name")
180
 
 
181
109
    def test_verbose_commit_renamed(self):
182
110
        # Verbose commit of renamed file should say so
183
111
        wt = self.prepare_simple_history()
196
124
        wt.rename_one('hello.txt', 'subdir/hello.txt')
197
125
        out, err = self.run_bzr('commit -m renamed')
198
126
        self.assertEqual('', out)
199
 
        self.assertEqual(set([
200
 
            'Committing to: %s/' % osutils.getcwd(),
201
 
            'added subdir',
202
 
            'renamed hello.txt => subdir/hello.txt',
203
 
            'Committed revision 2.',
204
 
            '',
205
 
            ]), set(err.split('\n')))
 
127
        self.assertContainsRe(err, '^Committing to: .*\n'
 
128
                              'added subdir\n'
 
129
                              'renamed hello\.txt => subdir/hello\.txt\n'
 
130
                              'Committed revision 2\.\n$')
206
131
 
207
132
    def test_verbose_commit_with_unknown(self):
208
133
        """Unknown files should not be listed by default in verbose output"""
242
167
        self.assertEqual(err, 'Committing to: %s\n'
243
168
                         'Committed revision 2.\n' % expected)
244
169
 
245
 
    def test_commit_sanitizes_CR_in_message(self):
246
 
        # See bug #433779, basically Emacs likes to pass '\r\n' style line
247
 
        # endings to 'bzr commit -m ""' which breaks because we don't allow
248
 
        # '\r' in commit messages. (Mostly because of issues where XML style
249
 
        # formats arbitrarily strip it out of the data while parsing.)
250
 
        # To make life easier for users, we just always translate '\r\n' =>
251
 
        # '\n'. And '\r' => '\n'.
252
 
        a_tree = self.make_branch_and_tree('a')
253
 
        self.build_tree(['a/b'])
254
 
        a_tree.add('b')
255
 
        self.run_bzr(['commit',
256
 
                      '-m', 'a string\r\n\r\nwith mixed\r\rendings\n'],
257
 
                     working_dir='a')
258
 
        rev_id = a_tree.branch.last_revision()
259
 
        rev = a_tree.branch.repository.get_revision(rev_id)
260
 
        self.assertEqualDiff('a string\n\nwith mixed\n\nendings\n',
261
 
                             rev.message)
262
 
 
263
170
    def test_commit_merge_reports_all_modified_files(self):
264
171
        # the commit command should show all the files that are shown by
265
172
        # bzr diff or bzr status when committing, even when they were not
313
220
        os.chdir('this')
314
221
        out,err = self.run_bzr('commit -m added')
315
222
        self.assertEqual('', out)
316
 
        self.assertEqual(set([
317
 
            'Committing to: %s/' % osutils.getcwd(),
318
 
            'modified filetomodify',
319
 
            'added newdir',
320
 
            'added newfile',
321
 
            'renamed dirtorename => renameddir',
322
 
            'renamed filetorename => renamedfile',
323
 
            'renamed dirtoreparent => renameddir/reparenteddir',
324
 
            'renamed filetoreparent => renameddir/reparentedfile',
325
 
            'deleted dirtoremove',
326
 
            'deleted filetoremove',
327
 
            'Committed revision 2.',
328
 
            ''
329
 
            ]), set(err.split('\n')))
 
223
        expected = '%s/' % (osutils.getcwd(), )
 
224
        self.assertEqualDiff(
 
225
            'Committing to: %s\n'
 
226
            'modified filetomodify\n'
 
227
            'added newdir\n'
 
228
            'added newfile\n'
 
229
            'renamed dirtorename => renameddir\n'
 
230
            'renamed filetorename => renamedfile\n'
 
231
            'renamed dirtoreparent => renameddir/reparenteddir\n'
 
232
            'renamed filetoreparent => renameddir/reparentedfile\n'
 
233
            'deleted dirtoremove\n'
 
234
            'deleted filetoremove\n'
 
235
            'Committed revision 2.\n' % (expected, ),
 
236
            err)
330
237
 
331
238
    def test_empty_commit_message(self):
332
239
        tree = self.make_branch_and_tree('.')
334
241
        tree.add('foo.c')
335
242
        self.run_bzr('commit -m ""', retcode=3)
336
243
 
 
244
    def test_unsupported_encoding_commit_message(self):
 
245
        tree = self.make_branch_and_tree('.')
 
246
        self.build_tree_contents([('foo.c', 'int main() {}')])
 
247
        tree.add('foo.c')
 
248
        # LANG env variable has no effect on Windows
 
249
        # but some characters anyway cannot be represented
 
250
        # in default user encoding
 
251
        char = probe_bad_non_ascii(bzrlib.user_encoding)
 
252
        if char is None:
 
253
            raise TestSkipped('Cannot find suitable non-ascii character'
 
254
                'for user_encoding (%s)' % bzrlib.user_encoding)
 
255
        out,err = self.run_bzr_subprocess('commit -m "%s"' % char,
 
256
                                          retcode=1,
 
257
                                          env_changes={'LANG': 'C'})
 
258
        self.assertContainsRe(err, r'bzrlib.errors.BzrError: Parameter.*is '
 
259
                                    'unsupported by the current encoding.')
 
260
 
337
261
    def test_other_branch_commit(self):
338
262
        # this branch is to ensure consistent behaviour, whether we're run
339
263
        # inside a branch, or not.
342
266
        self.build_tree_contents([
343
267
            ('branch/foo.c', 'int main() {}'),
344
268
            ('branch/bar.c', 'int main() {}')])
345
 
        inner_tree.add(['foo.c', 'bar.c'])
 
269
        inner_tree.add('foo.c')
 
270
        inner_tree.add('bar.c')
346
271
        # can't commit files in different trees; sane error
347
272
        self.run_bzr('commit -m newstuff branch/foo.c .', retcode=3)
348
 
        # can commit to branch - records foo.c only
349
273
        self.run_bzr('commit -m newstuff branch/foo.c')
350
 
        # can commit to branch - records bar.c
351
274
        self.run_bzr('commit -m newstuff branch')
352
 
        # No changes left
353
 
        self.run_bzr_error(["No changes to commit"], 'commit -m newstuff branch')
 
275
        self.run_bzr('commit -m newstuff branch', retcode=3)
354
276
 
355
277
    def test_out_of_date_tree_commit(self):
356
278
        # check we get an error code and a clear message committing with an out
380
302
    def test_commit_a_text_merge_in_a_checkout(self):
381
303
        # checkouts perform multiple actions in a transaction across bond
382
304
        # branches and their master, and have been observed to fail in the
383
 
        # past. This is a user story reported to fail in bug #43959 where
 
305
        # past. This is a user story reported to fail in bug #43959 where 
384
306
        # a merge done in a checkout (using the update command) failed to
385
307
        # commit correctly.
386
308
        trunk = self.make_branch_and_tree('trunk')
387
309
 
388
310
        u1 = trunk.branch.create_checkout('u1')
389
 
        self.build_tree_contents([('u1/hosts', 'initial contents\n')])
 
311
        self.build_tree_contents([('u1/hosts', 'initial contents')])
390
312
        u1.add('hosts')
391
313
        self.run_bzr('commit -m add-hosts u1')
392
314
 
393
315
        u2 = trunk.branch.create_checkout('u2')
394
 
        self.build_tree_contents([('u2/hosts', 'altered in u2\n')])
 
316
        self.build_tree_contents([('u2/hosts', 'altered in u2')])
395
317
        self.run_bzr('commit -m checkin-from-u2 u2')
396
318
 
397
319
        # make an offline commits
398
 
        self.build_tree_contents([('u1/hosts', 'first offline change in u1\n')])
 
320
        self.build_tree_contents([('u1/hosts', 'first offline change in u1')])
399
321
        self.run_bzr('commit -m checkin-offline --local u1')
400
322
 
401
323
        # now try to pull in online work from u2, and then commit our offline
402
324
        # work as a merge
403
325
        # retcode 1 as we expect a text conflict
404
326
        self.run_bzr('update u1', retcode=1)
405
 
        self.assertFileEqual('''\
406
 
<<<<<<< TREE
407
 
first offline change in u1
408
 
=======
409
 
altered in u2
410
 
>>>>>>> MERGE-SOURCE
411
 
''',
412
 
                             'u1/hosts')
413
 
 
414
327
        self.run_bzr('resolved u1/hosts')
415
328
        # add a text change here to represent resolving the merge conflicts in
416
329
        # favour of a new version of the file not identical to either the u1
479
392
 
480
393
        # With no changes, it should just be 'no changes'
481
394
        # Make sure that commit is failing because there is nothing to do
482
 
        self.run_bzr_error(['No changes to commit'],
 
395
        self.run_bzr_error(['no changes to commit'],
483
396
                           'commit --strict -m no-changes',
484
397
                           working_dir='tree')
485
398
 
604
517
        self.build_tree(['tree/hello.txt'])
605
518
        tree.add('hello.txt')
606
519
        self.run_bzr_error(
607
 
            ["Did not understand bug identifier orange: Must be an integer. "
608
 
             "See \"bzr help bugs\" for more information on this feature.\n"
609
 
             "Commit refused."],
 
520
            ["Invalid bug identifier for %s. Commit refused." % 'lp:orange'],
610
521
            'commit -m add-b --fixes=lp:orange',
611
522
            working_dir='tree')
612
523
 
616
527
        self.build_tree(['tree/hello.txt'])
617
528
        tree.add('hello.txt')
618
529
        self.run_bzr_error(
619
 
            [r"Invalid bug orange. Must be in the form of 'tracker:id'\. "
620
 
             r"See \"bzr help bugs\" for more information on this feature.\n"
 
530
            [r"Invalid bug orange. Must be in the form of 'tag:id'\. "
621
531
             r"Commit refused\."],
622
532
            'commit -m add-b --fixes=orange',
623
533
            working_dir='tree')
644
554
                     "tree/hello.txt"])
645
555
        last_rev = tree.branch.repository.get_revision(tree.last_revision())
646
556
        properties = last_rev.properties
647
 
        self.assertEqual(u'John D\xf6 <jdoe@example.com>', properties['authors'])
 
557
        self.assertEqual(u'John D\xf6 <jdoe@example.com>', properties['author'])
648
558
 
649
559
    def test_author_no_email(self):
650
560
        """Author's name without an email address is allowed, too."""
655
565
                                "tree/hello.txt")
656
566
        last_rev = tree.branch.repository.get_revision(tree.last_revision())
657
567
        properties = last_rev.properties
658
 
        self.assertEqual('John Doe', properties['authors'])
659
 
 
660
 
    def test_multiple_authors(self):
661
 
        """Multiple authors can be specyfied, and all are stored."""
662
 
        tree = self.make_branch_and_tree('tree')
663
 
        self.build_tree(['tree/hello.txt'])
664
 
        tree.add('hello.txt')
665
 
        out, err = self.run_bzr("commit -m hello --author='John Doe' "
666
 
                                "--author='Jane Rey' tree/hello.txt")
667
 
        last_rev = tree.branch.repository.get_revision(tree.last_revision())
668
 
        properties = last_rev.properties
669
 
        self.assertEqual('John Doe\nJane Rey', properties['authors'])
670
 
 
671
 
    def test_commit_time(self):
672
 
        tree = self.make_branch_and_tree('tree')
673
 
        self.build_tree(['tree/hello.txt'])
674
 
        tree.add('hello.txt')
675
 
        out, err = self.run_bzr("commit -m hello "
676
 
            "--commit-time='2009-10-10 08:00:00 +0100' tree/hello.txt")
677
 
        last_rev = tree.branch.repository.get_revision(tree.last_revision())
678
 
        self.assertEqual(
679
 
            'Sat 2009-10-10 08:00:00 +0100',
680
 
            osutils.format_date(last_rev.timestamp, last_rev.timezone))
681
 
        
682
 
    def test_commit_time_bad_time(self):
683
 
        tree = self.make_branch_and_tree('tree')
684
 
        self.build_tree(['tree/hello.txt'])
685
 
        tree.add('hello.txt')
686
 
        out, err = self.run_bzr("commit -m hello "
687
 
            "--commit-time='NOT A TIME' tree/hello.txt", retcode=3)
688
 
        self.assertStartsWith(
689
 
            err, "bzr: ERROR: Could not parse --commit-time:")
 
568
        self.assertEqual('John Doe', properties['author'])
690
569
 
691
570
    def test_partial_commit_with_renames_in_tree(self):
692
571
        # this test illustrates bug #140419
706
585
        self.assertContainsRe(err, r'modified test\nCommitted revision 2.')
707
586
 
708
587
    def test_commit_readonly_checkout(self):
709
 
        # https://bugs.launchpad.net/bzr/+bug/129701
 
588
        # https://bugs.edge.launchpad.net/bzr/+bug/129701
710
589
        # "UnlockableTransport error trying to commit in checkout of readonly
711
590
        # branch"
712
591
        self.make_branch('master')
717
596
            retcode=3)
718
597
        self.assertContainsRe(err,
719
598
            r'^bzr: ERROR: Cannot lock.*readonly transport')
720
 
 
721
 
    def setup_editor(self):
722
 
        # Test that commit template hooks work
723
 
        if sys.platform == "win32":
724
 
            f = file('fed.bat', 'w')
725
 
            f.write('@rem dummy fed')
726
 
            f.close()
727
 
            self.overrideEnv('BZR_EDITOR', "fed.bat")
728
 
        else:
729
 
            f = file('fed.sh', 'wb')
730
 
            f.write('#!/bin/sh\n')
731
 
            f.close()
732
 
            os.chmod('fed.sh', 0755)
733
 
            self.overrideEnv('BZR_EDITOR', "./fed.sh")
734
 
 
735
 
    def setup_commit_with_template(self):
736
 
        self.setup_editor()
737
 
        msgeditor.hooks.install_named_hook("commit_message_template",
738
 
                lambda commit_obj, msg: "save me some typing\n", None)
739
 
        tree = self.make_branch_and_tree('tree')
740
 
        self.build_tree(['tree/hello.txt'])
741
 
        tree.add('hello.txt')
742
 
        return tree
743
 
 
744
 
    def test_commit_hook_template_accepted(self):
745
 
        tree = self.setup_commit_with_template()
746
 
        out, err = self.run_bzr("commit tree/hello.txt", stdin="y\n")
747
 
        last_rev = tree.branch.repository.get_revision(tree.last_revision())
748
 
        self.assertEqual('save me some typing\n', last_rev.message)
749
 
 
750
 
    def test_commit_hook_template_rejected(self):
751
 
        tree = self.setup_commit_with_template()
752
 
        expected = tree.last_revision()
753
 
        out, err = self.run_bzr_error(["Empty commit message specified."
754
 
                  " Please specify a commit message with either"
755
 
                  " --message or --file or leave a blank message"
756
 
                  " with --message \"\"."],
757
 
            "commit tree/hello.txt", stdin="n\n")
758
 
        self.assertEqual(expected, tree.last_revision())
759
 
 
760
 
    def test_set_commit_message(self):
761
 
        msgeditor.hooks.install_named_hook("set_commit_message",
762
 
                lambda commit_obj, msg: "save me some typing\n", None)
763
 
        tree = self.make_branch_and_tree('tree')
764
 
        self.build_tree(['tree/hello.txt'])
765
 
        tree.add('hello.txt')
766
 
        out, err = self.run_bzr("commit tree/hello.txt")
767
 
        last_rev = tree.branch.repository.get_revision(tree.last_revision())
768
 
        self.assertEqual('save me some typing\n', last_rev.message)
769
 
 
770
 
    def test_commit_without_username(self):
771
 
        """Ensure commit error if username is not set.
772
 
        """
773
 
        self.run_bzr(['init', 'foo'])
774
 
        os.chdir('foo')
775
 
        open('foo.txt', 'w').write('hello')
776
 
        self.run_bzr(['add'])
777
 
        self.overrideEnv('EMAIL', None)
778
 
        self.overrideEnv('BZR_EMAIL', None)
779
 
        # Also, make sure that it's not inferred from mailname.
780
 
        self.overrideAttr(config, '_auto_user_id',
781
 
            lambda: (None, None))
782
 
        out, err = self.run_bzr(['commit', '-m', 'initial'], 3)
783
 
        self.assertContainsRe(err, 'Unable to determine your name')
784
 
 
785
 
    def test_commit_recursive_checkout(self):
786
 
        """Ensure that a commit to a recursive checkout fails cleanly.
787
 
        """
788
 
        self.run_bzr(['init', 'test_branch'])
789
 
        self.run_bzr(['checkout', 'test_branch', 'test_checkout'])
790
 
        os.chdir('test_checkout')
791
 
        self.run_bzr(['bind', '.']) # bind to self
792
 
        open('foo.txt', 'w').write('hello')
793
 
        self.run_bzr(['add'])
794
 
        out, err = self.run_bzr(['commit', '-m', 'addedfoo'], 3)
795
 
        self.assertEqual(out, '')
796
 
        self.assertContainsRe(err,
797
 
            'Branch.*test_checkout.*appears to be bound to itself')