~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Aaron Bentley
  • Date: 2006-11-10 01:55:55 UTC
  • mto: This revision was merged to the branch mainline in revision 2127.
  • Revision ID: aaron.bentley@utoronto.ca-20061110015555-f48202744b630209
Ignore html docs (both kinds)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2006 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
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
 
16
 
 
17
 
 
18
"""Tests for the commit CLI of bzr."""
 
19
 
 
20
import os
 
21
import re
 
22
import sys
 
23
 
 
24
from bzrlib.branch import Branch
 
25
from bzrlib.bzrdir import BzrDir
 
26
from bzrlib.errors import BzrCommandError
 
27
from bzrlib.tests.blackbox import ExternalBase
 
28
from bzrlib.workingtree import WorkingTree
 
29
 
 
30
 
 
31
class TestCommit(ExternalBase):
 
32
 
 
33
    def test_05_empty_commit(self):
 
34
        """Commit of tree with no versioned files should fail"""
 
35
        # If forced, it should succeed, but this is not tested here.
 
36
        self.run_bzr("init")
 
37
        self.build_tree(['hello.txt'])
 
38
        out,err = self.run_bzr("commit", "-m", "empty", retcode=3)
 
39
        self.assertEqual('', out)
 
40
        self.assertStartsWith(err, 'bzr: ERROR: no changes to commit.'
 
41
                                  ' use --unchanged to commit anyhow\n')
 
42
 
 
43
    def test_save_commit_message(self):
 
44
        """Failed commit should save the message in a file"""
 
45
        self.run_bzr("init")
 
46
        out,err = self.run_bzr("commit", "-m", "message", retcode=3)
 
47
        self.assertEqual('', out)
 
48
        self.assertStartsWith(err, 'bzr: ERROR: no changes to commit.'
 
49
                                  ' use --unchanged to commit anyhow\n'
 
50
                                  'Commit message saved. To reuse the message,'
 
51
                                  ' do\nbzr commit --file ')
 
52
        message_file = re.compile('bzr-commit-\S*').search(err).group()
 
53
        self.check_file_contents(message_file, 'message')
 
54
 
 
55
    def test_commit_success(self):
 
56
        """Successful commit should not leave behind a bzr-commit-* file"""
 
57
        self.run_bzr("init")
 
58
        self.run_bzr("commit", "--unchanged", "-m", "message")
 
59
        self.assertEqual('', self.capture('unknowns'))
 
60
 
 
61
        # same for unicode messages
 
62
        self.run_bzr("commit", "--unchanged", "-m", u'foo\xb5')
 
63
        self.assertEqual('', self.capture('unknowns'))
 
64
 
 
65
    def test_commit_with_path(self):
 
66
        """Commit tree with path of root specified"""
 
67
        self.run_bzr('init', 'a')
 
68
        self.build_tree(['a/a_file'])
 
69
        self.run_bzr('add', 'a/a_file')
 
70
        self.run_bzr('commit', '-m', 'first commit', 'a')
 
71
 
 
72
        self.run_bzr('branch', 'a', 'b')
 
73
        self.build_tree_contents([('b/a_file', 'changes in b')])
 
74
        self.run_bzr('commit', '-m', 'first commit in b', 'b')
 
75
 
 
76
        self.build_tree_contents([('a/a_file', 'new contents')])
 
77
        self.run_bzr('commit', '-m', 'change in a', 'a')
 
78
 
 
79
        os.chdir('b')
 
80
        self.run_bzr('merge', '../a', retcode=1) # will conflict
 
81
        os.chdir('..')
 
82
        self.run_bzr('resolved', 'b/a_file')
 
83
        self.run_bzr('commit', '-m', 'merge into b', 'b')
 
84
 
 
85
 
 
86
    def test_10_verbose_commit(self):
 
87
        """Add one file and examine verbose commit output"""
 
88
        self.runbzr("init")
 
89
        self.build_tree(['hello.txt'])
 
90
        self.runbzr("add hello.txt")
 
91
        out,err = self.run_bzr("commit", "-m", "added")
 
92
        self.assertEqual('', out)
 
93
        self.assertEqual('added hello.txt\n'
 
94
                         'Committed revision 1.\n',
 
95
                         err)
 
96
 
 
97
    def prepare_simple_history(self):
 
98
        """Prepare and return a working tree with one commit of one file"""
 
99
        # Commit with modified file should say so
 
100
        wt = BzrDir.create_standalone_workingtree('.')
 
101
        self.build_tree(['hello.txt', 'extra.txt'])
 
102
        wt.add(['hello.txt'])
 
103
        wt.commit(message='added')
 
104
        return wt
 
105
 
 
106
    def test_verbose_commit_modified(self):
 
107
        # Verbose commit of modified file should say so
 
108
        wt = self.prepare_simple_history()
 
109
        self.build_tree_contents([('hello.txt', 'new contents')])
 
110
        out, err = self.run_bzr("commit", "-m", "modified")
 
111
        self.assertEqual('', out)
 
112
        self.assertEqual('modified hello.txt\n'
 
113
                         'Committed revision 2.\n',
 
114
                         err)
 
115
 
 
116
    def test_verbose_commit_renamed(self):
 
117
        # Verbose commit of renamed file should say so
 
118
        wt = self.prepare_simple_history()
 
119
        wt.rename_one('hello.txt', 'gutentag.txt')
 
120
        out, err = self.run_bzr("commit", "-m", "renamed")
 
121
        self.assertEqual('', out)
 
122
        self.assertEqual('renamed hello.txt => gutentag.txt\n'
 
123
                         'Committed revision 2.\n',
 
124
                         err)
 
125
 
 
126
    def test_verbose_commit_moved(self):
 
127
        # Verbose commit of file moved to new directory should say so
 
128
        wt = self.prepare_simple_history()
 
129
        os.mkdir('subdir')
 
130
        wt.add(['subdir'])
 
131
        wt.rename_one('hello.txt', 'subdir/hello.txt')
 
132
        out, err = self.run_bzr("commit", "-m", "renamed")
 
133
        self.assertEqual('', out)
 
134
        self.assertEqualDiff('added subdir\n'
 
135
                             'renamed hello.txt => subdir/hello.txt\n'
 
136
                             'Committed revision 2.\n',
 
137
                             err)
 
138
 
 
139
    def test_verbose_commit_with_unknown(self):
 
140
        """Unknown files should not be listed by default in verbose output"""
 
141
        # Is that really the best policy?
 
142
        wt = BzrDir.create_standalone_workingtree('.')
 
143
        self.build_tree(['hello.txt', 'extra.txt'])
 
144
        wt.add(['hello.txt'])
 
145
        out,err = self.run_bzr("commit", "-m", "added")
 
146
        self.assertEqual('', out)
 
147
        self.assertEqual('added hello.txt\n'
 
148
                         'Committed revision 1.\n',
 
149
                         err)
 
150
 
 
151
    def test_verbose_commit_with_unchanged(self):
 
152
        """Unchanged files should not be listed by default in verbose output"""
 
153
        self.runbzr("init")
 
154
        self.build_tree(['hello.txt', 'unchanged.txt'])
 
155
        self.runbzr('add unchanged.txt')
 
156
        self.runbzr('commit -m unchanged unchanged.txt')
 
157
        self.runbzr("add hello.txt")
 
158
        out,err = self.run_bzr("commit", "-m", "added")
 
159
        self.assertEqual('', out)
 
160
        self.assertEqual('added hello.txt\n'
 
161
                         'Committed revision 2.\n',
 
162
                         err)
 
163
 
 
164
    def test_commit_merge_reports_all_modified_files(self):
 
165
        # the commit command should show all the files that are shown by
 
166
        # bzr diff or bzr status when committing, even when they were not
 
167
        # changed by the user but rather through doing a merge.
 
168
        this_tree = self.make_branch_and_tree('this')
 
169
        # we need a bunch of files and dirs, to perform one action on each.
 
170
        self.build_tree([
 
171
            'this/dirtorename/',
 
172
            'this/dirtoreparent/',
 
173
            'this/dirtoleave/',
 
174
            'this/dirtoremove/',
 
175
            'this/filetoreparent',
 
176
            'this/filetorename',
 
177
            'this/filetomodify',
 
178
            'this/filetoremove',
 
179
            'this/filetoleave']
 
180
            )
 
181
        this_tree.add([
 
182
            'dirtorename',
 
183
            'dirtoreparent',
 
184
            'dirtoleave',
 
185
            'dirtoremove',
 
186
            'filetoreparent',
 
187
            'filetorename',
 
188
            'filetomodify',
 
189
            'filetoremove',
 
190
            'filetoleave']
 
191
            )
 
192
        this_tree.commit('create_files')
 
193
        other_dir = this_tree.bzrdir.sprout('other')
 
194
        other_tree = other_dir.open_workingtree()
 
195
        other_tree.lock_write()
 
196
        # perform the needed actions on the files and dirs.
 
197
        try:
 
198
            other_tree.rename_one('dirtorename', 'renameddir')
 
199
            other_tree.rename_one('dirtoreparent', 'renameddir/reparenteddir')
 
200
            other_tree.rename_one('filetorename', 'renamedfile')
 
201
            other_tree.rename_one('filetoreparent', 'renameddir/reparentedfile')
 
202
            other_tree.remove(['dirtoremove', 'filetoremove'])
 
203
            self.build_tree_contents([
 
204
                ('other/newdir/', ),
 
205
                ('other/filetomodify', 'new content'),
 
206
                ('other/newfile', 'new file content')])
 
207
            other_tree.add('newfile')
 
208
            other_tree.add('newdir/')
 
209
            other_tree.commit('modify all sample files and dirs.')
 
210
        finally:
 
211
            other_tree.unlock()
 
212
        this_tree.merge_from_branch(other_tree.branch)
 
213
        os.chdir('this')
 
214
        out,err = self.run_bzr("commit", "-m", "added")
 
215
        os.chdir('..')
 
216
        self.assertEqual('', out)
 
217
        self.assertEqualDiff(
 
218
            'modified filetomodify\n'
 
219
            'added newdir\n'
 
220
            'added newfile\n'
 
221
            'renamed dirtorename => renameddir\n'
 
222
            'renamed dirtoreparent => renameddir/reparenteddir\n'
 
223
            'renamed filetoreparent => renameddir/reparentedfile\n'
 
224
            'renamed filetorename => renamedfile\n'
 
225
            'deleted dirtoremove\n'
 
226
            'deleted filetoremove\n'
 
227
            'Committed revision 2.\n',
 
228
            err)
 
229
 
 
230
    def test_empty_commit_message(self):
 
231
        self.runbzr("init")
 
232
        file('foo.c', 'wt').write('int main() {}')
 
233
        self.runbzr(['add', 'foo.c'])
 
234
        self.runbzr(["commit", "-m", ""] , retcode=3)
 
235
 
 
236
    def test_other_branch_commit(self):
 
237
        # this branch is to ensure consistent behaviour, whether we're run
 
238
        # inside a branch, or not.
 
239
        os.mkdir('empty_branch')
 
240
        os.chdir('empty_branch')
 
241
        self.runbzr('init')
 
242
        os.mkdir('branch')
 
243
        os.chdir('branch')
 
244
        self.runbzr('init')
 
245
        file('foo.c', 'wt').write('int main() {}')
 
246
        file('bar.c', 'wt').write('int main() {}')
 
247
        os.chdir('..')
 
248
        self.runbzr(['add', 'branch/foo.c'])
 
249
        self.runbzr(['add', 'branch'])
 
250
        # can't commit files in different trees; sane error
 
251
        self.runbzr('commit -m newstuff branch/foo.c .', retcode=3)
 
252
        self.runbzr('commit -m newstuff branch/foo.c')
 
253
        self.runbzr('commit -m newstuff branch')
 
254
        self.runbzr('commit -m newstuff branch', retcode=3)
 
255
 
 
256
    def test_out_of_date_tree_commit(self):
 
257
        # check we get an error code and a clear message committing with an out
 
258
        # of date checkout
 
259
        self.make_branch_and_tree('branch')
 
260
        # make a checkout
 
261
        self.runbzr('checkout --lightweight branch checkout')
 
262
        # commit to the original branch to make the checkout out of date
 
263
        self.runbzr('commit --unchanged -m message branch')
 
264
        # now commit to the checkout should emit
 
265
        # ERROR: Out of date with the branch, 'bzr update' is suggested
 
266
        output = self.runbzr('commit --unchanged -m checkout_message '
 
267
                             'checkout', retcode=3)
 
268
        self.assertEqual(output,
 
269
                         ('',
 
270
                          "bzr: ERROR: Working tree is out of date, please run "
 
271
                          "'bzr update'.\n"))
 
272
 
 
273
    def test_local_commit_unbound(self):
 
274
        # a --local commit on an unbound branch is an error
 
275
        self.make_branch_and_tree('.')
 
276
        out, err = self.run_bzr('commit', '--local', retcode=3)
 
277
        self.assertEqualDiff('', out)
 
278
        self.assertEqualDiff('bzr: ERROR: Cannot perform local-only commits '
 
279
                             'on unbound branches.\n', err)
 
280
 
 
281
    def test_commit_a_text_merge_in_a_checkout(self):
 
282
        # checkouts perform multiple actions in a transaction across bond
 
283
        # branches and their master, and have been observed to fail in the
 
284
        # past. This is a user story reported to fail in bug #43959 where 
 
285
        # a merge done in a checkout (using the update command) failed to
 
286
        # commit correctly.
 
287
        self.run_bzr('init', 'trunk')
 
288
 
 
289
        self.run_bzr('checkout', 'trunk', 'u1')
 
290
        self.build_tree_contents([('u1/hosts', 'initial contents')])
 
291
        self.run_bzr('add', 'u1/hosts')
 
292
        self.run_bzr('commit', '-m', 'add hosts', 'u1')
 
293
 
 
294
        self.run_bzr('checkout', 'trunk', 'u2')
 
295
        self.build_tree_contents([('u2/hosts', 'altered in u2')])
 
296
        self.run_bzr('commit', '-m', 'checkin from u2', 'u2')
 
297
 
 
298
        # make an offline commits
 
299
        self.build_tree_contents([('u1/hosts', 'first offline change in u1')])
 
300
        self.run_bzr('commit', '-m', 'checkin offline', '--local', 'u1')
 
301
 
 
302
        # now try to pull in online work from u2, and then commit our offline
 
303
        # work as a merge
 
304
        # retcode 1 as we expect a text conflict
 
305
        self.run_bzr('update', 'u1', retcode=1)
 
306
        self.run_bzr('resolved', 'u1/hosts')
 
307
        # add a text change here to represent resolving the merge conflicts in
 
308
        # favour of a new version of the file not identical to either the u1
 
309
        # version or the u2 version.
 
310
        self.build_tree_contents([('u1/hosts', 'merge resolution\n')])
 
311
        self.run_bzr('commit', '-m', 'checkin merge of the offline work from u1', 'u1')
 
312
 
 
313
    def test_commit_respects_spec_for_removals(self):
 
314
        """Commit with a file spec should only commit removals that match"""
 
315
        t = self.make_branch_and_tree('.')
 
316
        self.build_tree(['file-a', 'dir-a/', 'dir-a/file-b'])
 
317
        t.add(['file-a', 'dir-a', 'dir-a/file-b'])
 
318
        t.commit('Create')
 
319
        t.remove(['file-a', 'dir-a/file-b'])
 
320
        os.chdir('dir-a')
 
321
        result = self.run_bzr('commit', '.', '-m' 'removed file-b')[1]
 
322
        self.assertNotContainsRe(result, 'file-a')
 
323
        result = self.run_bzr('status')[0]
 
324
        self.assertContainsRe(result, 'removed:\n  file-a')