~bzr-pqm/bzr/bzr.dev

2052.3.2 by John Arbash Meinel
Change Copyright .. by Canonical to Copyright ... Canonical
1
# Copyright (C) 2005, 2006 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1508.1.22 by Robert Collins
implement out of date working tree checks in commit.
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
1508.1.22 by Robert Collins
implement out of date working tree checks in commit.
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
1508.1.22 by Robert Collins
implement out of date working tree checks in commit.
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
1669.2.1 by Martin Pool
verbose commit now specifically identifies modified/renamed/reparented files
25
from bzrlib.bzrdir import BzrDir
1508.1.22 by Robert Collins
implement out of date working tree checks in commit.
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
1616.1.3 by Martin Pool
Clean up cut&pasted test for verbose commit
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.
1711.2.60 by John Arbash Meinel
Fix an empty commit to raise the right exception.
36
        self.run_bzr("init")
1508.1.22 by Robert Collins
implement out of date working tree checks in commit.
37
        self.build_tree(['hello.txt'])
2089.1.1 by wang
If a commit fails, the commit message is stored in a file at the root of
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'))
1616.1.3 by Martin Pool
Clean up cut&pasted test for verbose commit
64
1704.2.11 by Martin Pool
Handle 'bzr commit DIR' when dir contains pending merges.
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
1616.1.3 by Martin Pool
Clean up cut&pasted test for verbose commit
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
1669.2.1 by Martin Pool
verbose commit now specifically identifies modified/renamed/reparented files
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)
1668.1.5 by Martin Pool
[broken] fix up display of files changed by a commit
122
        self.assertEqual('renamed hello.txt => gutentag.txt\n'
1669.2.1 by Martin Pool
verbose commit now specifically identifies modified/renamed/reparented files
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'
1668.1.5 by Martin Pool
[broken] fix up display of files changed by a commit
135
                             'renamed hello.txt => subdir/hello.txt\n'
1669.2.1 by Martin Pool
verbose commit now specifically identifies modified/renamed/reparented files
136
                             'Committed revision 2.\n',
137
                             err)
138
139
    def test_verbose_commit_with_unknown(self):
1616.1.3 by Martin Pool
Clean up cut&pasted test for verbose commit
140
        """Unknown files should not be listed by default in verbose output"""
141
        # Is that really the best policy?
1669.2.1 by Martin Pool
verbose commit now specifically identifies modified/renamed/reparented files
142
        wt = BzrDir.create_standalone_workingtree('.')
1616.1.3 by Martin Pool
Clean up cut&pasted test for verbose commit
143
        self.build_tree(['hello.txt', 'extra.txt'])
1669.2.1 by Martin Pool
verbose commit now specifically identifies modified/renamed/reparented files
144
        wt.add(['hello.txt'])
1616.1.3 by Martin Pool
Clean up cut&pasted test for verbose commit
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
1668.1.5 by Martin Pool
[broken] fix up display of files changed by a commit
151
    def test_verbose_commit_with_unchanged(self):
1616.1.4 by Martin Pool
Verbose commit shouldn't talk about every unchanged file.
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)
1508.1.22 by Robert Collins
implement out of date working tree checks in commit.
163
1668.1.5 by Martin Pool
[broken] fix up display of files changed by a commit
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()
1979.2.1 by Robert Collins
(robertc) adds a convenience method "merge_from_branch" to WorkingTree.
212
        this_tree.merge_from_branch(other_tree.branch)
1668.1.5 by Martin Pool
[broken] fix up display of files changed by a commit
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
1508.1.22 by Robert Collins
implement out of date working tree checks in commit.
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'])
1616.1.3 by Martin Pool
Clean up cut&pasted test for verbose commit
234
        self.runbzr(["commit", "-m", ""] , retcode=3)
1508.1.22 by Robert Collins
implement out of date working tree checks in commit.
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
1587.1.14 by Robert Collins
Make bound branch creation happen via 'checkout'
261
        self.runbzr('checkout --lightweight branch checkout')
1508.1.22 by Robert Collins
implement out of date working tree checks in commit.
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"))
1587.1.8 by Robert Collins
Local commits on unbound branches fail.
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)
1668.1.3 by Martin Pool
[patch] use the correct transaction when committing snapshot (Malone: #43959)
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')])
1704.2.12 by Martin Pool
use the correct transaction when committing snapshot (Malone: #43959)
311
        self.run_bzr('commit', '-m', 'checkin merge of the offline work from u1', 'u1')
1551.7.24 by Aaron Bentley
Ensure commit respects file spec when committing removals
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')