1# Copyright (C) 2006-2012, 2016 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 16 17from io import BytesIO 18import os 19 20from ... import ( 21 revision as _mod_revision, 22 tests, 23 trace, 24 ) 25from ...diff import show_diff_trees 26from ...merge import Merger, Merge3Merger 27from ...transform import ( 28 ROOT_PARENT, 29 resolve_conflicts, 30 ) 31from ...tree import ( 32 find_previous_path, 33 TreeChange, 34 ) 35 36from breezy.bzr.inventorytree import InventoryTreeChange 37 38from breezy.tests.per_tree import TestCaseWithTree 39 40 41from ..features import ( 42 HardlinkFeature, 43 SymlinkFeature, 44 UnicodeFilenameFeature, 45 ) 46 47 48 49class TestTransformPreview(TestCaseWithTree): 50 51 def create_tree(self): 52 tree = self.make_branch_and_tree('.') 53 self.build_tree_contents([('a', b'content 1')]) 54 tree.add('a') 55 revid1 = tree.commit('rev1') 56 return tree.branch.repository.revision_tree(revid1) 57 58 def get_empty_preview(self): 59 repository = self.make_repository('repo') 60 tree = repository.revision_tree(_mod_revision.NULL_REVISION) 61 preview = tree.preview_transform() 62 self.addCleanup(preview.finalize) 63 return preview 64 65 def test_transform_preview(self): 66 revision_tree = self.create_tree() 67 preview = revision_tree.preview_transform() 68 self.addCleanup(preview.finalize) 69 70 def test_transform_preview_tree(self): 71 revision_tree = self.create_tree() 72 preview = revision_tree.preview_transform() 73 self.addCleanup(preview.finalize) 74 preview.get_preview_tree() 75 76 def test_transform_new_file(self): 77 revision_tree = self.create_tree() 78 preview = revision_tree.preview_transform() 79 self.addCleanup(preview.finalize) 80 preview.new_file('file2', preview.root, [b'content B\n'], b'file2-id') 81 preview_tree = preview.get_preview_tree() 82 self.assertEqual(preview_tree.kind('file2'), 'file') 83 with preview_tree.get_file('file2') as f: 84 self.assertEqual(f.read(), b'content B\n') 85 86 def test_diff_preview_tree(self): 87 revision_tree = self.create_tree() 88 preview = revision_tree.preview_transform() 89 self.addCleanup(preview.finalize) 90 preview.new_file('file2', preview.root, [b'content B\n'], b'file2-id') 91 preview_tree = preview.get_preview_tree() 92 out = BytesIO() 93 show_diff_trees(revision_tree, preview_tree, out) 94 lines = out.getvalue().splitlines() 95 self.assertEqual(lines[0], b"=== added file 'file2'") 96 # 3 lines of diff administrivia 97 self.assertEqual(lines[4], b"+content B") 98 99 def test_unsupported_symlink_diff(self): 100 self.requireFeature(SymlinkFeature) 101 tree = self.make_branch_and_tree('.') 102 self.build_tree_contents([('a', 'content 1')]) 103 tree.add('a') 104 os.symlink('a', 'foo') 105 tree.add('foo') 106 revid1 = tree.commit('rev1') 107 revision_tree = tree.branch.repository.revision_tree(revid1) 108 preview = revision_tree.preview_transform() 109 self.addCleanup(preview.finalize) 110 preview.delete_versioned(preview.trans_id_tree_path('foo')) 111 preview_tree = preview.get_preview_tree() 112 out = BytesIO() 113 log = BytesIO() 114 trace.push_log_file(log) 115 os_symlink = getattr(os, 'symlink', None) 116 os.symlink = None 117 try: 118 show_diff_trees(revision_tree, preview_tree, out) 119 lines = out.getvalue().splitlines() 120 finally: 121 os.symlink = os_symlink 122 self.assertContainsRe( 123 log.getvalue(), 124 b'Ignoring "foo" as symlinks are not supported on this filesystem') 125 126 def test_transform_conflicts(self): 127 revision_tree = self.create_tree() 128 preview = revision_tree.preview_transform() 129 self.addCleanup(preview.finalize) 130 preview.new_file('a', preview.root, [b'content 2']) 131 resolve_conflicts(preview) 132 trans_id = preview.trans_id_tree_path('a') 133 self.assertEqual('a.moved', preview.final_name(trans_id)) 134 135 def get_tree_and_preview_tree(self): 136 revision_tree = self.create_tree() 137 preview = revision_tree.preview_transform() 138 self.addCleanup(preview.finalize) 139 a_trans_id = preview.trans_id_tree_path('a') 140 preview.delete_contents(a_trans_id) 141 preview.create_file([b'b content'], a_trans_id) 142 preview_tree = preview.get_preview_tree() 143 return revision_tree, preview_tree 144 145 def test_iter_changes(self): 146 revision_tree, preview_tree = self.get_tree_and_preview_tree() 147 root = revision_tree.path2id('') 148 self.assertEqual([(revision_tree.path2id('a'), ('a', 'a'), True, (True, True), 149 (root, root), ('a', 'a'), ('file', 'file'), 150 (False, False), False)], 151 list(preview_tree.iter_changes(revision_tree))) 152 153 def assertTreeChanges(self, expected, actual, tree): 154 # TODO(jelmer): Turn this into a matcher? 155 actual = list(actual) 156 if tree.supports_setting_file_ids(): 157 self.assertEqual(expected, actual) 158 else: 159 expected = [ 160 TreeChange(path=c.path, changed_content=c.changed_content, 161 versioned=c.versioned, name=c.name, 162 kind=c.kind, executable=c.executable, 163 copied=c.copied) for c in expected] 164 actual = [ 165 TreeChange(path=c.path, changed_content=c.changed_content, 166 versioned=c.versioned, name=c.name, 167 kind=c.kind, executable=c.executable, 168 copied=c.copied) for c in actual] 169 self.assertEqual(expected, actual) 170 171 def test_include_unchanged_succeeds(self): 172 revision_tree, preview_tree = self.get_tree_and_preview_tree() 173 changes = preview_tree.iter_changes(revision_tree, 174 include_unchanged=True) 175 176 root_id = revision_tree.path2id('') 177 root_entry = InventoryTreeChange( 178 root_id, ('', ''), False, (True, True), (None, None), 179 ('', ''), ('directory', 'directory'), (False, False), False) 180 a_entry = InventoryTreeChange( 181 revision_tree.path2id('a'), ('a', 'a'), True, (True, True), 182 (root_id, root_id), ('a', 'a'), ('file', 'file'), 183 (False, False), False) 184 185 self.assertTreeChanges([root_entry, a_entry], changes, preview_tree) 186 187 def test_specific_files(self): 188 revision_tree, preview_tree = self.get_tree_and_preview_tree() 189 changes = preview_tree.iter_changes(revision_tree, 190 specific_files=['']) 191 root_id = revision_tree.path2id('') 192 a_entry = (revision_tree.path2id('a'), ('a', 'a'), True, (True, True), 193 (root_id, root_id), ('a', 'a'), ('file', 'file'), 194 (False, False), False) 195 196 self.assertEqual([a_entry], list(changes)) 197 198 def test_want_unversioned(self): 199 revision_tree, preview_tree = self.get_tree_and_preview_tree() 200 changes = preview_tree.iter_changes(revision_tree, 201 want_unversioned=True) 202 root_id = revision_tree.path2id('') 203 a_entry = InventoryTreeChange( 204 revision_tree.path2id('a'), ('a', 'a'), True, (True, True), 205 (root_id, root_id), ('a', 'a'), ('file', 'file'), 206 (False, False), False) 207 208 self.assertEqual([a_entry], list(changes)) 209 210 def test_ignore_extra_trees_no_specific_files(self): 211 # extra_trees is harmless without specific_files, so we'll silently 212 # accept it, even though we won't use it. 213 revision_tree, preview_tree = self.get_tree_and_preview_tree() 214 preview_tree.iter_changes(revision_tree, extra_trees=[preview_tree]) 215 216 def test_ignore_require_versioned_no_specific_files(self): 217 # require_versioned is meaningless without specific_files. 218 revision_tree, preview_tree = self.get_tree_and_preview_tree() 219 preview_tree.iter_changes(revision_tree, require_versioned=False) 220 221 def test_ignore_pb(self): 222 # pb could be supported, but TT.iter_changes doesn't support it. 223 revision_tree, preview_tree = self.get_tree_and_preview_tree() 224 preview_tree.iter_changes(revision_tree) 225 226 def test_kind(self): 227 revision_tree = self.create_tree() 228 preview = revision_tree.preview_transform() 229 self.addCleanup(preview.finalize) 230 preview.new_file('file', preview.root, [b'contents'], b'file-id') 231 preview.new_directory('directory', preview.root, b'dir-id') 232 preview_tree = preview.get_preview_tree() 233 self.assertEqual('file', preview_tree.kind('file')) 234 self.assertEqual('directory', preview_tree.kind('directory')) 235 236 def test_get_file_mtime(self): 237 preview = self.get_empty_preview() 238 file_trans_id = preview.new_file('file', preview.root, [b'contents'], 239 b'file-id') 240 limbo_path = preview._limbo_name(file_trans_id) 241 preview_tree = preview.get_preview_tree() 242 self.assertEqual(os.stat(limbo_path).st_mtime, 243 preview_tree.get_file_mtime('file')) 244 245 def test_get_file_mtime_renamed(self): 246 work_tree = self.make_branch_and_tree('tree') 247 self.build_tree(['tree/file']) 248 work_tree.add('file') 249 preview = work_tree.preview_transform() 250 self.addCleanup(preview.finalize) 251 file_trans_id = preview.trans_id_tree_path('file') 252 preview.adjust_path('renamed', preview.root, file_trans_id) 253 preview_tree = preview.get_preview_tree() 254 preview_mtime = preview_tree.get_file_mtime('renamed') 255 work_mtime = work_tree.get_file_mtime('file') 256 257 def test_get_file_size(self): 258 work_tree = self.make_branch_and_tree('tree') 259 self.build_tree_contents([('tree/old', b'old')]) 260 work_tree.add('old') 261 preview = work_tree.preview_transform() 262 self.addCleanup(preview.finalize) 263 preview.new_file('name', preview.root, [b'contents'], b'new-id', 264 'executable') 265 tree = preview.get_preview_tree() 266 self.assertEqual(len('old'), tree.get_file_size('old')) 267 self.assertEqual(len('contents'), tree.get_file_size('name')) 268 269 def test_get_file(self): 270 preview = self.get_empty_preview() 271 preview.new_file('file', preview.root, [b'contents'], b'file-id') 272 preview_tree = preview.get_preview_tree() 273 with preview_tree.get_file('file') as tree_file: 274 self.assertEqual(b'contents', tree_file.read()) 275 276 def test_get_symlink_target(self): 277 self.requireFeature(SymlinkFeature) 278 preview = self.get_empty_preview() 279 preview.new_symlink('symlink', preview.root, 'target', b'symlink-id') 280 preview_tree = preview.get_preview_tree() 281 self.assertEqual('target', 282 preview_tree.get_symlink_target('symlink')) 283 284 def test_all_file_ids(self): 285 if not self.workingtree_format.supports_setting_file_ids: 286 raise tests.TestNotApplicable( 287 'format does not support setting file ids') 288 tree = self.make_branch_and_tree('tree') 289 self.build_tree(['tree/a', 'tree/b', 'tree/c']) 290 tree.add(['a', 'b', 'c'], [b'a-id', b'b-id', b'c-id']) 291 preview = tree.preview_transform() 292 self.addCleanup(preview.finalize) 293 preview.unversion_file(preview.trans_id_file_id(b'b-id')) 294 c_trans_id = preview.trans_id_file_id(b'c-id') 295 preview.unversion_file(c_trans_id) 296 preview.version_file(c_trans_id, file_id=b'c-id') 297 preview_tree = preview.get_preview_tree() 298 self.assertEqual({b'a-id', b'c-id', tree.path2id('')}, 299 preview_tree.all_file_ids()) 300 301 def test_path2id_deleted_unchanged(self): 302 tree = self.make_branch_and_tree('tree') 303 self.build_tree(['tree/unchanged', 'tree/deleted']) 304 tree.add(['unchanged', 'deleted']) 305 preview = tree.preview_transform() 306 self.addCleanup(preview.finalize) 307 preview.unversion_file(preview.trans_id_tree_path('deleted')) 308 preview_tree = preview.get_preview_tree() 309 self.assertEqual( 310 'unchanged', 311 find_previous_path(preview_tree, tree, 'unchanged')) 312 self.assertFalse(preview_tree.is_versioned('deleted')) 313 314 def test_path2id_created(self): 315 tree = self.make_branch_and_tree('tree') 316 self.build_tree(['tree/unchanged']) 317 tree.add(['unchanged']) 318 preview = tree.preview_transform() 319 self.addCleanup(preview.finalize) 320 preview.new_file('new', preview.trans_id_tree_path('unchanged'), 321 [b'contents'], b'new-id') 322 preview_tree = preview.get_preview_tree() 323 self.assertTrue(preview_tree.is_versioned('unchanged/new')) 324 if self.workingtree_format.supports_setting_file_ids: 325 self.assertEqual(b'new-id', preview_tree.path2id('unchanged/new')) 326 327 def test_path2id_moved(self): 328 tree = self.make_branch_and_tree('tree') 329 self.build_tree(['tree/old_parent/', 'tree/old_parent/child']) 330 tree.add(['old_parent', 'old_parent/child']) 331 preview = tree.preview_transform() 332 self.addCleanup(preview.finalize) 333 new_parent = preview.new_directory('new_parent', preview.root, 334 b'new_parent-id') 335 preview.adjust_path('child', new_parent, 336 preview.trans_id_tree_path('old_parent/child')) 337 preview_tree = preview.get_preview_tree() 338 self.assertFalse(preview_tree.is_versioned('old_parent/child')) 339 self.assertEqual( 340 'new_parent/child', 341 find_previous_path(tree, preview_tree, 'old_parent/child')) 342 if self.workingtree_format.supports_setting_file_ids: 343 self.assertEqual( 344 tree.path2id('old_parent/child'), 345 preview_tree.path2id('new_parent/child')) 346 347 def test_path2id_renamed_parent(self): 348 tree = self.make_branch_and_tree('tree') 349 self.build_tree(['tree/old_name/', 'tree/old_name/child']) 350 tree.add(['old_name', 'old_name/child']) 351 preview = tree.preview_transform() 352 self.addCleanup(preview.finalize) 353 preview.adjust_path('new_name', preview.root, 354 preview.trans_id_tree_path('old_name')) 355 preview_tree = preview.get_preview_tree() 356 self.assertFalse(preview_tree.is_versioned('old_name/child')) 357 self.assertEqual( 358 'new_name/child', 359 find_previous_path(tree, preview_tree, 'old_name/child')) 360 if tree.supports_setting_file_ids(): 361 self.assertEqual( 362 tree.path2id('old_name/child'), 363 preview_tree.path2id('new_name/child')) 364 365 def assertMatchingIterEntries(self, tt, specific_files=None): 366 preview_tree = tt.get_preview_tree() 367 preview_result = list(preview_tree.iter_entries_by_dir( 368 specific_files=specific_files)) 369 tree = tt._tree 370 tt.apply() 371 actual_result = list(tree.iter_entries_by_dir( 372 specific_files=specific_files)) 373 self.assertEqual(actual_result, preview_result) 374 375 def test_iter_entries_by_dir_new(self): 376 tree = self.make_branch_and_tree('tree') 377 tt = tree.transform() 378 tt.new_file('new', tt.root, [b'contents'], b'new-id') 379 self.assertMatchingIterEntries(tt) 380 381 def test_iter_entries_by_dir_deleted(self): 382 tree = self.make_branch_and_tree('tree') 383 self.build_tree(['tree/deleted']) 384 tree.add('deleted') 385 tt = tree.transform() 386 tt.delete_contents(tt.trans_id_tree_path('deleted')) 387 self.assertMatchingIterEntries(tt) 388 389 def test_iter_entries_by_dir_unversioned(self): 390 tree = self.make_branch_and_tree('tree') 391 self.build_tree(['tree/removed']) 392 tree.add('removed') 393 tt = tree.transform() 394 tt.unversion_file(tt.trans_id_tree_path('removed')) 395 self.assertMatchingIterEntries(tt) 396 397 def test_iter_entries_by_dir_moved(self): 398 tree = self.make_branch_and_tree('tree') 399 self.build_tree(['tree/moved', 'tree/new_parent/']) 400 tree.add(['moved', 'new_parent']) 401 tt = tree.transform() 402 tt.adjust_path( 403 'moved', tt.trans_id_tree_path('new_parent'), 404 tt.trans_id_tree_path('moved')) 405 self.assertMatchingIterEntries(tt) 406 407 def test_iter_entries_by_dir_specific_files(self): 408 tree = self.make_branch_and_tree('tree') 409 self.build_tree(['tree/parent/', 'tree/parent/child']) 410 tree.add(['parent', 'parent/child']) 411 tt = tree.transform() 412 self.assertMatchingIterEntries(tt, ['', 'parent/child']) 413 414 def test_symlink_content_summary(self): 415 self.requireFeature(SymlinkFeature) 416 preview = self.get_empty_preview() 417 preview.new_symlink('path', preview.root, 'target', b'path-id') 418 summary = preview.get_preview_tree().path_content_summary('path') 419 self.assertEqual(('symlink', None, None, 'target'), summary) 420 421 def test_missing_content_summary(self): 422 preview = self.get_empty_preview() 423 summary = preview.get_preview_tree().path_content_summary('path') 424 self.assertEqual(('missing', None, None, None), summary) 425 426 def test_deleted_content_summary(self): 427 tree = self.make_branch_and_tree('tree') 428 self.build_tree(['tree/path/']) 429 tree.add('path') 430 preview = tree.preview_transform() 431 self.addCleanup(preview.finalize) 432 preview.delete_contents(preview.trans_id_tree_path('path')) 433 summary = preview.get_preview_tree().path_content_summary('path') 434 self.assertEqual(('missing', None, None, None), summary) 435 436 def test_file_content_summary_executable(self): 437 preview = self.get_empty_preview() 438 path_id = preview.new_file('path', preview.root, [ 439 b'contents'], b'path-id') 440 preview.set_executability(True, path_id) 441 summary = preview.get_preview_tree().path_content_summary('path') 442 self.assertEqual(4, len(summary)) 443 self.assertEqual('file', summary[0]) 444 # size must be known 445 self.assertEqual(len('contents'), summary[1]) 446 # executable 447 self.assertEqual(True, summary[2]) 448 # will not have hash (not cheap to determine) 449 self.assertIs(None, summary[3]) 450 451 def test_change_executability(self): 452 tree = self.make_branch_and_tree('tree') 453 self.build_tree(['tree/path']) 454 tree.add('path') 455 preview = tree.preview_transform() 456 self.addCleanup(preview.finalize) 457 path_id = preview.trans_id_tree_path('path') 458 preview.set_executability(True, path_id) 459 summary = preview.get_preview_tree().path_content_summary('path') 460 self.assertEqual(True, summary[2]) 461 462 def test_file_content_summary_non_exec(self): 463 preview = self.get_empty_preview() 464 preview.new_file('path', preview.root, [b'contents'], b'path-id') 465 summary = preview.get_preview_tree().path_content_summary('path') 466 self.assertEqual(4, len(summary)) 467 self.assertEqual('file', summary[0]) 468 # size must be known 469 self.assertEqual(len('contents'), summary[1]) 470 # not executable 471 self.assertEqual(False, summary[2]) 472 # will not have hash (not cheap to determine) 473 self.assertIs(None, summary[3]) 474 475 def test_dir_content_summary(self): 476 preview = self.get_empty_preview() 477 preview.new_directory('path', preview.root, b'path-id') 478 summary = preview.get_preview_tree().path_content_summary('path') 479 self.assertEqual(('directory', None, None, None), summary) 480 481 def test_tree_content_summary(self): 482 preview = self.get_empty_preview() 483 path = preview.new_directory('path', preview.root, b'path-id') 484 preview.set_tree_reference(b'rev-1', path) 485 summary = preview.get_preview_tree().path_content_summary('path') 486 self.assertEqual(4, len(summary)) 487 self.assertEqual('tree-reference', summary[0]) 488 489 def test_annotate(self): 490 tree = self.make_branch_and_tree('tree') 491 self.build_tree_contents([('tree/file', b'a\n')]) 492 tree.add('file') 493 revid1 = tree.commit('a') 494 self.build_tree_contents([('tree/file', b'a\nb\n')]) 495 preview = tree.preview_transform() 496 self.addCleanup(preview.finalize) 497 file_trans_id = preview.trans_id_tree_path('file') 498 preview.delete_contents(file_trans_id) 499 preview.create_file([b'a\nb\nc\n'], file_trans_id) 500 preview_tree = preview.get_preview_tree() 501 expected = [ 502 (revid1, b'a\n'), 503 (b'me:', b'b\n'), 504 (b'me:', b'c\n'), 505 ] 506 annotation = preview_tree.annotate_iter( 507 'file', default_revision=b'me:') 508 self.assertEqual(expected, annotation) 509 510 def test_annotate_missing(self): 511 preview = self.get_empty_preview() 512 preview.new_file('file', preview.root, [b'a\nb\nc\n'], b'file-id') 513 preview_tree = preview.get_preview_tree() 514 expected = [ 515 (b'me:', b'a\n'), 516 (b'me:', b'b\n'), 517 (b'me:', b'c\n'), 518 ] 519 annotation = preview_tree.annotate_iter( 520 'file', default_revision=b'me:') 521 self.assertEqual(expected, annotation) 522 523 def test_annotate_rename(self): 524 tree = self.make_branch_and_tree('tree') 525 self.build_tree_contents([('tree/file', b'a\n')]) 526 tree.add('file') 527 revid1 = tree.commit('a') 528 preview = tree.preview_transform() 529 self.addCleanup(preview.finalize) 530 file_trans_id = preview.trans_id_tree_path('file') 531 preview.adjust_path('newname', preview.root, file_trans_id) 532 preview_tree = preview.get_preview_tree() 533 expected = [ 534 (revid1, b'a\n'), 535 ] 536 annotation = preview_tree.annotate_iter( 537 'newname', default_revision=b'me:') 538 self.assertEqual(expected, annotation) 539 annotation = preview_tree.annotate_iter( 540 'file', default_revision=b'me:') 541 self.assertIs(None, annotation) 542 543 def test_annotate_deleted(self): 544 tree = self.make_branch_and_tree('tree') 545 self.build_tree_contents([('tree/file', b'a\n')]) 546 tree.add('file') 547 tree.commit('a') 548 self.build_tree_contents([('tree/file', b'a\nb\n')]) 549 preview = tree.preview_transform() 550 self.addCleanup(preview.finalize) 551 file_trans_id = preview.trans_id_tree_path('file') 552 preview.delete_contents(file_trans_id) 553 preview_tree = preview.get_preview_tree() 554 annotation = preview_tree.annotate_iter( 555 'file', default_revision=b'me:') 556 self.assertIs(None, annotation) 557 558 def test_stored_kind(self): 559 preview = self.get_empty_preview() 560 preview.new_file('file', preview.root, [b'a\nb\nc\n'], b'file-id') 561 preview_tree = preview.get_preview_tree() 562 self.assertEqual('file', preview_tree.stored_kind('file')) 563 564 def test_is_executable(self): 565 preview = self.get_empty_preview() 566 trans_id = preview.new_file('file', preview.root, [b'a\nb\nc\n'], b'file-id') 567 preview.set_executability(True, trans_id) 568 preview_tree = preview.get_preview_tree() 569 self.assertEqual(True, preview_tree.is_executable('file')) 570 571 def test_get_set_parent_ids(self): 572 revision_tree, preview_tree = self.get_tree_and_preview_tree() 573 self.assertEqual([], preview_tree.get_parent_ids()) 574 preview_tree.set_parent_ids([revision_tree.get_revision_id()]) 575 self.assertEqual( 576 [revision_tree.get_revision_id()], 577 preview_tree.get_parent_ids()) 578 579 def test_plan_file_merge(self): 580 work_a = self.make_branch_and_tree('wta') 581 self.build_tree_contents([('wta/file', b'a\nb\nc\nd\n')]) 582 work_a.add('file') 583 base_id = work_a.commit('base version') 584 tree_b = work_a.controldir.sprout('wtb').open_workingtree() 585 preview = work_a.preview_transform() 586 self.addCleanup(preview.finalize) 587 trans_id = preview.trans_id_tree_path('file') 588 preview.delete_contents(trans_id) 589 preview.create_file([b'b\nc\nd\ne\n'], trans_id) 590 self.build_tree_contents([('wtb/file', b'a\nc\nd\nf\n')]) 591 tree_a = preview.get_preview_tree() 592 if not getattr(tree_a, 'plan_file_merge', None): 593 self.skipTest('tree does not support file merge planning') 594 tree_a.set_parent_ids([base_id]) 595 self.addCleanup(tree_b.lock_read().unlock) 596 self.assertEqual([ 597 ('killed-a', b'a\n'), 598 ('killed-b', b'b\n'), 599 ('unchanged', b'c\n'), 600 ('unchanged', b'd\n'), 601 ('new-a', b'e\n'), 602 ('new-b', b'f\n'), 603 ], list(tree_a.plan_file_merge('file', tree_b))) 604 605 def test_plan_file_merge_revision_tree(self): 606 work_a = self.make_branch_and_tree('wta') 607 self.build_tree_contents([('wta/file', b'a\nb\nc\nd\n')]) 608 work_a.add('file') 609 base_id = work_a.commit('base version') 610 tree_b = work_a.controldir.sprout('wtb').open_workingtree() 611 preview = work_a.basis_tree().preview_transform() 612 self.addCleanup(preview.finalize) 613 trans_id = preview.trans_id_tree_path('file') 614 preview.delete_contents(trans_id) 615 preview.create_file([b'b\nc\nd\ne\n'], trans_id) 616 self.build_tree_contents([('wtb/file', b'a\nc\nd\nf\n')]) 617 tree_a = preview.get_preview_tree() 618 if not getattr(tree_a, 'plan_file_merge', None): 619 self.skipTest('tree does not support file merge planning') 620 tree_a.set_parent_ids([base_id]) 621 self.addCleanup(tree_b.lock_read().unlock) 622 self.assertEqual([ 623 ('killed-a', b'a\n'), 624 ('killed-b', b'b\n'), 625 ('unchanged', b'c\n'), 626 ('unchanged', b'd\n'), 627 ('new-a', b'e\n'), 628 ('new-b', b'f\n'), 629 ], list(tree_a.plan_file_merge('file', tree_b))) 630 631 def test_walkdirs(self): 632 preview = self.get_empty_preview() 633 preview.new_directory('', ROOT_PARENT, b'tree-root') 634 # FIXME: new_directory should mark root. 635 preview.fixup_new_roots() 636 preview_tree = preview.get_preview_tree() 637 preview.new_file('a', preview.root, [b'contents'], b'a-id') 638 expected = [('', [('a', 'a', 'file', None, 'file')])] 639 self.assertEqual(expected, list(preview_tree.walkdirs())) 640 641 def test_extras(self): 642 work_tree = self.make_branch_and_tree('tree') 643 self.build_tree(['tree/removed-file', 'tree/existing-file', 644 'tree/not-removed-file']) 645 work_tree.add(['removed-file', 'not-removed-file']) 646 preview = work_tree.preview_transform() 647 self.addCleanup(preview.finalize) 648 preview.new_file('new-file', preview.root, [b'contents']) 649 preview.new_file('new-versioned-file', preview.root, [b'contents'], 650 b'new-versioned-id') 651 tree = preview.get_preview_tree() 652 self.assertEquals({'existing-file'}, set(work_tree.extras())) 653 preview.unversion_file(preview.trans_id_tree_path('removed-file')) 654 self.assertEqual({'new-file', 'removed-file', 'existing-file'}, 655 set(tree.extras())) 656 657 def test_merge_into_preview(self): 658 work_tree = self.make_branch_and_tree('tree') 659 self.build_tree_contents([('tree/file', b'b\n')]) 660 work_tree.add('file') 661 work_tree.commit('first commit') 662 child_tree = work_tree.controldir.sprout('child').open_workingtree() 663 self.build_tree_contents([('child/file', b'b\nc\n')]) 664 child_tree.commit('child commit') 665 child_tree.lock_write() 666 self.addCleanup(child_tree.unlock) 667 work_tree.lock_write() 668 self.addCleanup(work_tree.unlock) 669 preview = work_tree.preview_transform() 670 self.addCleanup(preview.finalize) 671 file_trans_id = preview.trans_id_tree_path('file') 672 preview.delete_contents(file_trans_id) 673 preview.create_file([b'a\nb\n'], file_trans_id) 674 preview_tree = preview.get_preview_tree() 675 merger = Merger.from_revision_ids(preview_tree, 676 child_tree.branch.last_revision(), 677 other_branch=child_tree.branch, 678 tree_branch=work_tree.branch) 679 merger.merge_type = Merge3Merger 680 tt = merger.make_merger().make_preview_transform() 681 self.addCleanup(tt.finalize) 682 final_tree = tt.get_preview_tree() 683 self.assertEqual( 684 b'a\nb\nc\n', 685 final_tree.get_file_text('file')) 686 687 def test_merge_preview_into_workingtree(self): 688 tree = self.make_branch_and_tree('tree') 689 if tree.supports_setting_file_ids(): 690 tree.set_root_id(b'TREE_ROOT') 691 tt = tree.preview_transform() 692 self.addCleanup(tt.finalize) 693 tt.new_file('name', tt.root, [b'content'], b'file-id') 694 tree2 = self.make_branch_and_tree('tree2') 695 if tree.supports_setting_file_ids(): 696 tree2.set_root_id(b'TREE_ROOT') 697 merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(), 698 tree.basis_tree()) 699 merger.merge_type = Merge3Merger 700 merger.do_merge() 701 702 def test_merge_preview_into_workingtree_handles_conflicts(self): 703 tree = self.make_branch_and_tree('tree') 704 self.build_tree_contents([('tree/foo', b'bar')]) 705 tree.add('foo') 706 tree.commit('foo') 707 tt = tree.preview_transform() 708 self.addCleanup(tt.finalize) 709 trans_id = tt.trans_id_tree_path('foo') 710 tt.delete_contents(trans_id) 711 tt.create_file([b'baz'], trans_id) 712 tree2 = tree.controldir.sprout('tree2').open_workingtree() 713 self.build_tree_contents([('tree2/foo', b'qux')]) 714 merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(), 715 tree.basis_tree()) 716 merger.merge_type = Merge3Merger 717 merger.do_merge() 718 719 def test_has_filename(self): 720 wt = self.make_branch_and_tree('tree') 721 self.build_tree(['tree/unmodified', 'tree/removed', 'tree/modified']) 722 tt = wt.preview_transform() 723 removed_id = tt.trans_id_tree_path('removed') 724 tt.delete_contents(removed_id) 725 tt.new_file('new', tt.root, [b'contents']) 726 modified_id = tt.trans_id_tree_path('modified') 727 tt.delete_contents(modified_id) 728 tt.create_file([b'modified-contents'], modified_id) 729 self.addCleanup(tt.finalize) 730 tree = tt.get_preview_tree() 731 self.assertTrue(tree.has_filename('unmodified')) 732 self.assertFalse(tree.has_filename('not-present')) 733 self.assertFalse(tree.has_filename('removed')) 734 self.assertTrue(tree.has_filename('new')) 735 self.assertTrue(tree.has_filename('modified')) 736 737 def test_is_executable2(self): 738 tree = self.make_branch_and_tree('tree') 739 preview = tree.preview_transform() 740 self.addCleanup(preview.finalize) 741 preview.new_file('foo', preview.root, [b'bar'], b'baz-id') 742 preview_tree = preview.get_preview_tree() 743 self.assertEqual(False, preview_tree.is_executable('tree/foo')) 744 745 def test_commit_preview_tree(self): 746 tree = self.make_branch_and_tree('tree') 747 rev_id = tree.commit('rev1') 748 tree.branch.lock_write() 749 self.addCleanup(tree.branch.unlock) 750 tt = tree.preview_transform() 751 tt.new_file('file', tt.root, [b'contents'], b'file_id') 752 self.addCleanup(tt.finalize) 753 preview = tt.get_preview_tree() 754 preview.set_parent_ids([rev_id]) 755 builder = tree.branch.get_commit_builder([rev_id]) 756 list(builder.record_iter_changes(preview, rev_id, tt.iter_changes())) 757 builder.finish_inventory() 758 rev2_id = builder.commit('rev2') 759 rev2_tree = tree.branch.repository.revision_tree(rev2_id) 760 self.assertEqual(b'contents', rev2_tree.get_file_text('file')) 761 762 def test_ascii_limbo_paths(self): 763 self.requireFeature(UnicodeFilenameFeature) 764 branch = self.make_branch('any') 765 tree = branch.repository.revision_tree(_mod_revision.NULL_REVISION) 766 tt = tree.preview_transform() 767 self.addCleanup(tt.finalize) 768 foo_id = tt.new_directory('', ROOT_PARENT) 769 bar_id = tt.new_file(u'\u1234bar', foo_id, [b'contents']) 770 limbo_path = tt._limbo_name(bar_id) 771 self.assertEqual(limbo_path, limbo_path) 772