44
51
# be a sorted list.
45
52
# u'\xe5' == a with circle
46
53
# '\xc3\xae' == u'\xee' == i with hat
47
# So these are u'pathg' and 'idg' only with a circle and a hat. (shappo?)
48
example_conflicts = ConflictList([
49
MissingParent('Not deleting', u'p\xe5thg', '\xc3\xaedg'),
50
ContentsConflict(u'p\xe5tha', None, '\xc3\xaeda'),
51
TextConflict(u'p\xe5tha'),
52
PathConflict(u'p\xe5thb', u'p\xe5thc', '\xc3\xaedb'),
53
DuplicateID('Unversioned existing file', u'p\xe5thc', u'p\xe5thc2',
54
'\xc3\xaedc', '\xc3\xaedc'),
55
DuplicateEntry('Moved existing file to', u'p\xe5thdd.moved', u'p\xe5thd',
57
ParentLoop('Cancelled move', u'p\xe5the', u'p\xe5th2e',
59
UnversionedParent('Versioned directory', u'p\xe5thf', '\xc3\xaedf'),
60
NonDirectoryParent('Created directory', u'p\xe5thg', '\xc3\xaedg'),
54
# So these are u'path' and 'id' only with a circle and a hat. (shappo?)
55
example_conflicts = conflicts.ConflictList(
56
[conflicts.MissingParent('Not deleting', u'p\xe5thg', '\xc3\xaedg'),
57
conflicts.ContentsConflict(u'p\xe5tha', None, '\xc3\xaeda'),
58
conflicts.TextConflict(u'p\xe5tha'),
59
conflicts.PathConflict(u'p\xe5thb', u'p\xe5thc', '\xc3\xaedb'),
60
conflicts.DuplicateID('Unversioned existing file',
61
u'p\xe5thc', u'p\xe5thc2',
62
'\xc3\xaedc', '\xc3\xaedc'),
63
conflicts.DuplicateEntry('Moved existing file to',
64
u'p\xe5thdd.moved', u'p\xe5thd',
66
conflicts.ParentLoop('Cancelled move', u'p\xe5the', u'p\xe5th2e',
68
conflicts.UnversionedParent('Versioned directory',
69
u'p\xe5thf', '\xc3\xaedf'),
70
conflicts.NonDirectoryParent('Created directory',
71
u'p\xe5thg', '\xc3\xaedg'),
64
class TestConflicts(TestCaseWithTransport):
75
class TestConflicts(tests.TestCaseWithTransport):
66
77
def test_conflicts(self):
67
78
"""Conflicts are detected properly"""
68
tree = self.make_branch_and_tree('.',
69
format=bzrdir.BzrDirFormat6())
71
file('hello', 'w').write('hello world4')
72
file('hello.THIS', 'w').write('hello world2')
73
file('hello.BASE', 'w').write('hello world1')
74
file('hello.OTHER', 'w').write('hello world3')
75
file('hello.sploo.BASE', 'w').write('yellow world')
76
file('hello.sploo.OTHER', 'w').write('yellow world2')
79
# Use BzrDirFormat6 so we can fake conflicts
80
tree = self.make_branch_and_tree('.', format=bzrdir.BzrDirFormat6())
81
self.build_tree_contents([('hello', 'hello world4'),
82
('hello.THIS', 'hello world2'),
83
('hello.BASE', 'hello world1'),
84
('hello.OTHER', 'hello world3'),
85
('hello.sploo.BASE', 'yellowworld'),
86
('hello.sploo.OTHER', 'yellowworld2'),
78
self.assertEqual(len(list(tree.list_files())), 6)
89
self.assertLength(6, list(tree.list_files()))
80
conflicts = tree.conflicts()
81
self.assertEqual(len(conflicts), 2)
82
self.assert_('hello' in conflicts[0].path)
83
self.assert_('hello.sploo' in conflicts[1].path)
85
restore('hello.sploo')
86
self.assertEqual(len(tree.conflicts()), 0)
91
tree_conflicts = tree.conflicts()
92
self.assertLength(2, tree_conflicts)
93
self.assertTrue('hello' in tree_conflicts[0].path)
94
self.assertTrue('hello.sploo' in tree_conflicts[1].path)
95
conflicts.restore('hello')
96
conflicts.restore('hello.sploo')
97
self.assertLength(0, tree.conflicts())
87
98
self.assertFileEqual('hello world2', 'hello')
88
99
self.assertFalse(os.path.lexists('hello.sploo'))
89
self.assertRaises(NotConflicted, restore, 'hello')
90
self.assertRaises(NotConflicted, restore, 'hello.sploo')
100
self.assertRaises(errors.NotConflicted, conflicts.restore, 'hello')
101
self.assertRaises(errors.NotConflicted,
102
conflicts.restore, 'hello.sploo')
92
104
def test_resolve_conflict_dir(self):
93
105
tree = self.make_branch_and_tree('.')
95
file('hello', 'w').write('hello world4')
106
self.build_tree_contents([('hello', 'hello world4'),
107
('hello.THIS', 'hello world2'),
108
('hello.BASE', 'hello world1'),
110
os.mkdir('hello.OTHER')
96
111
tree.add('hello', 'q')
97
file('hello.THIS', 'w').write('hello world2')
98
file('hello.BASE', 'w').write('hello world1')
99
os.mkdir('hello.OTHER')
100
l = ConflictList([TextConflict('hello')])
112
l = conflicts.ConflictList([conflicts.TextConflict('hello')])
101
113
l.remove_files(tree)
103
115
def test_select_conflicts(self):
104
116
tree = self.make_branch_and_tree('.')
105
tree_conflicts = ConflictList([ContentsConflict('foo'),
106
ContentsConflict('bar')])
107
self.assertEqual((ConflictList([ContentsConflict('bar')]),
108
ConflictList([ContentsConflict('foo')])),
109
tree_conflicts.select_conflicts(tree, ['foo']))
110
self.assertEqual((ConflictList(), tree_conflicts),
111
tree_conflicts.select_conflicts(tree, [''],
112
ignore_misses=True, recurse=True))
113
tree_conflicts = ConflictList([ContentsConflict('foo/baz'),
114
ContentsConflict('bar')])
115
self.assertEqual((ConflictList([ContentsConflict('bar')]),
116
ConflictList([ContentsConflict('foo/baz')])),
117
tree_conflicts.select_conflicts(tree, ['foo'],
120
tree_conflicts = ConflictList([PathConflict('qux', 'foo/baz')])
121
self.assertEqual((ConflictList(), tree_conflicts),
122
tree_conflicts.select_conflicts(tree, ['foo'],
125
self.assertEqual((tree_conflicts, ConflictList()),
126
tree_conflicts.select_conflicts(tree, ['foo'],
117
clist = conflicts.ConflictList
119
def check_select(not_selected, selected, paths, **kwargs):
121
(not_selected, selected),
122
tree_conflicts.select_conflicts(tree, paths, **kwargs))
124
foo = conflicts.ContentsConflict('foo')
125
bar = conflicts.ContentsConflict('bar')
126
tree_conflicts = clist([foo, bar])
128
check_select(clist([bar]), clist([foo]), ['foo'])
129
check_select(clist(), tree_conflicts,
130
[''], ignore_misses=True, recurse=True)
132
foobaz = conflicts.ContentsConflict('foo/baz')
133
tree_conflicts = clist([foobaz, bar])
135
check_select(clist([bar]), clist([foobaz]),
136
['foo'], ignore_misses=True, recurse=True)
138
qux = conflicts.PathConflict('qux', 'foo/baz')
139
tree_conflicts = clist([qux])
141
check_select(clist(), tree_conflicts,
142
['foo'], ignore_misses=True, recurse=True)
143
check_select (tree_conflicts, clist(), ['foo'], ignore_misses=True)
129
145
def test_resolve_conflicts_recursive(self):
130
146
tree = self.make_branch_and_tree('.')
131
147
self.build_tree(['dir/', 'dir/hello'])
132
148
tree.add(['dir', 'dir/hello'])
133
tree.set_conflicts(ConflictList([TextConflict('dir/hello')]))
134
resolve(tree, ['dir'], recursive=False, ignore_misses=True)
135
self.assertEqual(ConflictList([TextConflict('dir/hello')]),
137
resolve(tree, ['dir'], recursive=True, ignore_misses=True)
138
self.assertEqual(ConflictList([]),
142
class TestConflictStanzas(TestCase):
150
dirhello = conflicts.ConflictList([conflicts.TextConflict('dir/hello')])
151
tree.set_conflicts(dirhello)
153
conflicts.resolve(tree, ['dir'], recursive=False, ignore_misses=True)
154
self.assertEqual(dirhello, tree.conflicts())
156
conflicts.resolve(tree, ['dir'], recursive=True, ignore_misses=True)
157
self.assertEqual(conflicts.ConflictList([]), tree.conflicts())
160
class TestConflictStanzas(tests.TestCase):
144
162
def test_stanza_roundtrip(self):
145
163
# write and read our example stanza.
146
164
stanza_iter = example_conflicts.to_stanzas()
147
processed = ConflictList.from_stanzas(stanza_iter)
165
processed = conflicts.ConflictList.from_stanzas(stanza_iter)
148
166
for o, p in zip(processed, example_conflicts):
149
167
self.assertEqual(o, p)
171
189
self.assertStartsWith(stanza['conflict_path'], u'p\xe5th')
172
190
if 'conflict_file_id' in stanza:
173
191
self.assertStartsWith(stanza['conflict_file_id'], u'\xeed')
194
# FIXME: The shell-like tests should be converted to real whitebox tests... or
195
# moved to a blackbox module -- vila 20100205
197
# FIXME: Tests missing for DuplicateID conflict type
198
class TestResolveConflicts(script.TestCaseWithTransportAndScript):
200
preamble = None # The setup script set by daughter classes
203
super(TestResolveConflicts, self).setUp()
204
self.run_script(self.preamble)
207
class TestResolveTextConflicts(TestResolveConflicts):
212
def content_conflict_scenarios():
213
return [('file,None', dict(_this_actions='modify_file',
214
_check_this='file_has_more_content',
215
_other_actions='delete_file',
216
_check_other='file_doesnt_exist',
218
('None,file', dict(_this_actions='delete_file',
219
_check_this='file_doesnt_exist',
220
_other_actions='modify_file',
221
_check_other='file_has_more_content',
226
class TestResolveContentConflicts(tests.TestCaseWithTransport):
233
super(TestResolveContentConflicts, self).setUp()
234
builder = self.make_branch_builder('trunk')
235
builder.start_series()
236
# Create an empty trunk
237
builder.build_snapshot('start', None, [
238
('add', ('', 'root-id', 'directory', ''))])
239
# Add a minimal base content
240
builder.build_snapshot('base', ['start'], [
241
('add', ('file', 'file-id', 'file', 'trunk content\n'))])
242
# Modify the base content in branch
243
other_actions = self._get_actions(self._other_actions)
244
builder.build_snapshot('other', ['base'], other_actions())
245
# Modify the base content in trunk
246
this_actions = self._get_actions(self._this_actions)
247
builder.build_snapshot('this', ['base'], this_actions())
248
builder.finish_series()
249
self.builder = builder
251
def _get_actions(self, name):
252
return getattr(self, 'do_%s' % name)
254
def _get_check(self, name):
255
return getattr(self, 'check_%s' % name)
257
def do_modify_file(self):
258
return [('modify', ('file-id', 'trunk content\nmore content\n'))]
260
def check_file_has_more_content(self):
261
self.assertFileEqual('trunk content\nmore content\n', 'branch/file')
263
def do_delete_file(self):
264
return [('unversion', 'file-id')]
266
def check_file_doesnt_exist(self):
267
self.failIfExists('branch/file')
269
def _merge_other_into_this(self):
270
b = self.builder.get_branch()
271
wt = b.bzrdir.sprout('branch').open_workingtree()
272
wt.merge_from_branch(b, 'other')
275
def assertConflict(self, wt, ctype, **kwargs):
276
confs = wt.conflicts()
277
self.assertLength(1, confs)
279
self.assertIsInstance(c, ctype)
280
sentinel = object() # An impossible value
281
for k, v in kwargs.iteritems():
282
self.assertEqual(v, getattr(c, k, sentinel))
284
def check_resolved(self, wt, item, action):
285
conflicts.resolve(wt, [item], action=action)
286
# Check that we don't have any conflicts nor unknown left
287
self.assertLength(0, wt.conflicts())
288
self.assertLength(0, list(wt.unknowns()))
290
def test_resolve_taking_this(self):
291
wt = self._merge_other_into_this()
292
self.assertConflict(wt, conflicts.ContentsConflict,
293
path='file', file_id='file-id',)
294
self.check_resolved(wt, 'file', 'take_this')
295
check_this = self._get_check(self._check_this)
298
def test_resolve_taking_other(self):
299
wt = self._merge_other_into_this()
300
self.assertConflict(wt, conflicts.ContentsConflict,
301
path='file', file_id='file-id',)
302
self.check_resolved(wt, 'file', 'take_other')
303
check_other = self._get_check(self._check_other)
307
class TestResolveDuplicateEntry(TestResolveConflicts):
312
$ echo 'trunk content' >file
314
$ bzr commit -m 'Create trunk'
316
$ echo 'trunk content too' >file2
318
$ bzr commit -m 'Add file2 in trunk'
320
$ bzr branch . -r 1 ../branch
322
$ echo 'branch content' >file2
324
$ bzr commit -m 'Add file2 in branch'
328
2>R file2 => file2.moved
329
2>Conflict adding file file2. Moved existing file to file2.moved.
330
2>1 conflicts encountered.
333
def test_keep_this(self):
335
$ bzr rm file2 --force
336
$ bzr mv file2.moved file2
338
$ bzr commit --strict -m 'No more conflicts nor unknown files'
341
def test_keep_other(self):
342
self.failIfExists('branch/file2.moved')
344
$ bzr rm file2.moved --force
346
$ bzr commit --strict -m 'No more conflicts nor unknown files'
348
self.failIfExists('branch/file2.moved')
350
def test_resolve_taking_this(self):
352
$ bzr resolve --take-this file2
353
$ bzr commit --strict -m 'No more conflicts nor unknown files'
356
def test_resolve_taking_other(self):
358
$ bzr resolve --take-other file2
359
$ bzr commit --strict -m 'No more conflicts nor unknown files'
363
class TestResolveUnversionedParent(TestResolveConflicts):
365
# FIXME: Add the reverse tests: dir deleted in trunk, file added in branch
367
# FIXME: While this *creates* UnversionedParent conflicts, this really only
368
# tests MissingParent resolution :-/
374
$ bzr commit -m 'Create trunk'
376
$ echo 'trunk content' >dir/file
378
$ bzr commit -m 'Add dir/file in trunk'
380
$ bzr branch . -r 1 ../branch
383
$ bzr commit -m 'Remove dir in branch'
388
2>Conflict adding files to dir. Created directory.
389
2>Conflict because dir is not versioned, but has versioned children. Versioned directory.
390
2>2 conflicts encountered.
393
def test_take_this(self):
397
$ bzr commit --strict -m 'No more conflicts nor unknown files'
400
def test_take_other(self):
403
$ bzr commit --strict -m 'No more conflicts nor unknown files'
407
class TestResolveMissingParent(TestResolveConflicts):
413
$ echo 'trunk content' >dir/file
415
$ bzr commit -m 'Create trunk'
417
$ echo 'trunk content' >dir/file2
419
$ bzr commit -m 'Add dir/file2 in branch'
421
$ bzr branch . -r 1 ../branch
423
$ bzr rm dir/file --force
425
$ bzr commit -m 'Remove dir/file'
430
2>Conflict adding files to dir. Created directory.
431
2>Conflict because dir is not versioned, but has versioned children. Versioned directory.
432
2>2 conflicts encountered.
435
def test_keep_them_all(self):
438
$ bzr commit --strict -m 'No more conflicts nor unknown files'
441
def test_adopt_child(self):
443
$ bzr mv dir/file2 file2
446
$ bzr commit --strict -m 'No more conflicts nor unknown files'
449
def test_kill_them_all(self):
453
$ bzr commit --strict -m 'No more conflicts nor unknown files'
456
def test_resolve_taking_this(self):
458
$ bzr resolve --take-this dir
459
$ bzr commit --strict -m 'No more conflicts nor unknown files'
462
def test_resolve_taking_other(self):
464
$ bzr resolve --take-other dir
465
$ bzr commit --strict -m 'No more conflicts nor unknown files'
469
class TestResolveDeletingParent(TestResolveConflicts):
475
$ echo 'trunk content' >dir/file
477
$ bzr commit -m 'Create trunk'
479
$ bzr rm dir/file --force
481
$ bzr commit -m 'Remove dir/file'
483
$ bzr branch . -r 1 ../branch
485
$ echo 'branch content' >dir/file2
487
$ bzr commit -m 'Add dir/file2 in branch'
491
2>Conflict: can't delete dir because it is not empty. Not deleting.
492
2>Conflict because dir is not versioned, but has versioned children. Versioned directory.
493
2>2 conflicts encountered.
496
def test_keep_them_all(self):
499
$ bzr commit --strict -m 'No more conflicts nor unknown files'
502
def test_adopt_child(self):
504
$ bzr mv dir/file2 file2
507
$ bzr commit --strict -m 'No more conflicts nor unknown files'
510
def test_kill_them_all(self):
514
$ bzr commit --strict -m 'No more conflicts nor unknown files'
517
def test_resolve_taking_this(self):
519
$ bzr resolve --take-this dir
520
$ bzr commit --strict -m 'No more conflicts nor unknown files'
523
def test_resolve_taking_other(self):
525
$ bzr resolve --take-other dir
526
$ bzr commit --strict -m 'No more conflicts nor unknown files'
530
class TestResolvePathConflict(TestResolveConflicts):
537
$ bzr commit -m 'Create trunk'
539
$ bzr mv file file-in-trunk
540
$ bzr commit -m 'Renamed to file-in-trunk'
542
$ bzr branch . -r 1 ../branch
544
$ bzr mv file file-in-branch
545
$ bzr commit -m 'Renamed to file-in-branch'
548
2>R file-in-branch => file-in-trunk
549
2>Path conflict: file-in-branch / file-in-trunk
550
2>1 conflicts encountered.
553
def test_keep_source(self):
555
$ bzr resolve file-in-trunk
556
$ bzr commit --strict -m 'No more conflicts nor unknown files'
559
def test_keep_target(self):
561
$ bzr mv file-in-trunk file-in-branch
562
$ bzr resolve file-in-branch
563
$ bzr commit --strict -m 'No more conflicts nor unknown files'
566
def test_resolve_taking_this(self):
568
$ bzr resolve --take-this file-in-branch
569
$ bzr commit --strict -m 'No more conflicts nor unknown files'
572
def test_resolve_taking_other(self):
574
$ bzr resolve --take-other file-in-branch
575
$ bzr commit --strict -m 'No more conflicts nor unknown files'
579
class TestResolveParentLoop(TestResolveConflicts):
586
$ bzr commit -m 'Create trunk'
589
$ bzr commit -m 'Moved dir2 into dir1'
591
$ bzr branch . -r 1 ../branch
594
$ bzr commit -m 'Moved dir1 into dir2'
597
2>Conflict moving dir2/dir1 into dir2. Cancelled move.
598
2>1 conflicts encountered.
601
def test_take_this(self):
604
$ bzr commit --strict -m 'No more conflicts nor unknown files'
607
def test_take_other(self):
609
$ bzr mv dir2/dir1 dir1
612
$ bzr commit --strict -m 'No more conflicts nor unknown files'
615
def test_resolve_taking_this(self):
617
$ bzr resolve --take-this dir2
618
$ bzr commit --strict -m 'No more conflicts nor unknown files'
620
self.failUnlessExists('dir2')
622
def test_resolve_taking_other(self):
624
$ bzr resolve --take-other dir2
625
$ bzr commit --strict -m 'No more conflicts nor unknown files'
627
self.failUnlessExists('dir1')
630
class TestResolveNonDirectoryParent(TestResolveConflicts):
636
$ bzr commit -m 'Create trunk'
637
$ echo "Boing" >foo/bar
639
$ bzr commit -m 'Add foo/bar'
641
$ bzr branch . -r 1 ../branch
645
$ bzr commit -m 'foo is now a file'
650
# FIXME: The message is misleading, foo.new *is* a directory when the message
651
# is displayed -- vila 090916
652
2>Conflict: foo.new is not a directory, but has files in it. Created directory.
653
2>1 conflicts encountered.
656
def test_take_this(self):
658
$ bzr rm foo.new --force
659
# FIXME: Isn't it weird that foo is now unkown even if foo.new has been put
660
# aside ? -- vila 090916
662
$ bzr resolve foo.new
663
$ bzr commit --strict -m 'No more conflicts nor unknown files'
666
def test_take_other(self):
671
$ bzr commit --strict -m 'No more conflicts nor unknown files'
674
def test_resolve_taking_this(self):
676
$ bzr resolve --take-this foo.new
677
$ bzr commit --strict -m 'No more conflicts nor unknown files'
680
def test_resolve_taking_other(self):
682
$ bzr resolve --take-other foo.new
683
$ bzr commit --strict -m 'No more conflicts nor unknown files'
687
class TestMalformedTransform(script.TestCaseWithTransportAndScript):
689
def test_bug_430129(self):
690
# This is nearly like TestResolveNonDirectoryParent but with branch and
691
# trunk switched. As such it should certainly produce the same
697
$ bzr commit -m 'Create trunk'
700
$ bzr commit -m 'foo is now a file'
702
$ bzr branch . -r 1 ../branch
704
$ echo "Boing" >foo/bar
706
$ bzr commit -m 'Add foo/bar'
709
2>bzr: ERROR: Tree transform is malformed [('unversioned executability', 'new-1')]
713
class TestResolveActionOption(tests.TestCase):
716
super(TestResolveActionOption, self).setUp()
717
self.options = [conflicts.ResolveActionOption()]
718
self.parser = option.get_optparser(dict((o.name, o)
719
for o in self.options))
721
def parse(self, args):
722
return self.parser.parse_args(args)
724
def test_unknown_action(self):
725
self.assertRaises(errors.BadOptionValue,
726
self.parse, ['--action', 'take-me-to-the-moon'])
729
opts, args = self.parse(['--action', 'done'])
730
self.assertEqual({'action':'done'}, opts)
732
def test_take_this(self):
733
opts, args = self.parse(['--action', 'take-this'])
734
self.assertEqual({'action': 'take_this'}, opts)
735
opts, args = self.parse(['--take-this'])
736
self.assertEqual({'action': 'take_this'}, opts)
738
def test_take_other(self):
739
opts, args = self.parse(['--action', 'take-other'])
740
self.assertEqual({'action': 'take_other'}, opts)
741
opts, args = self.parse(['--take-other'])
742
self.assertEqual({'action': 'take_other'}, opts)