3 # revert_tests.py: testing 'svn revert'.
5 # Subversion is a tool for revision control.
6 # See http://subversion.tigris.org for more information.
8 # ====================================================================
9 # Copyright (c) 2000-2007 CollabNet. All rights reserved.
11 # This software is licensed as described in the file COPYING, which
12 # you should have received as part of this distribution. The terms
13 # are also available at http://subversion.tigris.org/license-1.html.
14 # If newer versions of this license are posted there, you may use a
15 # newer version instead, at your option.
17 ######################################################################
24 from svntest
import wc
28 Skip
= svntest
.testcase
.Skip
29 XFail
= svntest
.testcase
.XFail
30 Item
= svntest
.wc
.StateItem
33 ######################################################################
36 def revert_replacement_with_props(sbox
, wc_copy
):
37 """Helper implementing the core of
38 revert_{repos,wc}_to_wc_replace_with_props().
40 Uses a working copy (when wc_copy == True) or a URL (when wc_copy ==
41 False) source to copy from."""
46 # Use a temp file to set properties with wildcards in their values
47 # otherwise Win32/VS2005 will expand them
48 prop_path
= os
.path
.join(wc_dir
, 'proptmp')
49 svntest
.main
.file_append(prop_path
, '*')
51 # Set props on file which is copy-source later on
52 pi_path
= os
.path
.join(wc_dir
, 'A', 'D', 'G', 'pi')
53 rho_path
= os
.path
.join(wc_dir
, 'A', 'D', 'G', 'rho')
54 svntest
.actions
.run_and_verify_svn(None, None, [],
55 'ps', 'phony-prop', '-F', prop_path
,
58 svntest
.actions
.run_and_verify_svn(None, None, [],
59 'ps', 'svn:eol-style', 'LF', rho_path
)
61 # Verify props having been set
62 expected_disk
= svntest
.main
.greek_state
.copy()
63 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 1)
64 expected_disk
.tweak('A/D/G/pi',
65 props
={ 'phony-prop': '*' })
66 expected_disk
.tweak('A/D/G/rho',
67 props
={ 'svn:eol-style': 'LF' })
69 actual_disk
= svntest
.tree
.build_tree_from_wc(wc_dir
, 1)
70 svntest
.tree
.compare_trees("disk", actual_disk
, expected_disk
.old_tree())
73 expected_output
= svntest
.wc
.State(wc_dir
, {
74 'A/D/G/pi': Item(verb
='Sending'),
75 'A/D/G/rho': Item(verb
='Sending'),
77 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 1)
78 expected_status
.tweak('A/D/G/pi', wc_rev
='2')
79 expected_status
.tweak('A/D/G/rho', wc_rev
='2')
80 svntest
.actions
.run_and_verify_commit(wc_dir
,
86 svntest
.actions
.run_and_verify_svn(None, None, [], 'up', wc_dir
)
88 # File scheduled for deletion
89 svntest
.actions
.run_and_verify_svn(None, None, [], 'rm', rho_path
)
91 # Status before attempting copies
92 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 2)
93 expected_status
.tweak('A/D/G/rho', status
='D ')
94 svntest
.actions
.run_and_verify_status(wc_dir
, expected_status
)
96 # The copy shouldn't fail
98 pi_src
= os
.path
.join(wc_dir
, 'A', 'D', 'G', 'pi')
100 pi_src
= sbox
.repo_url
+ '/A/D/G/pi'
102 svntest
.actions
.run_and_verify_svn(None, None, [],
103 'cp', pi_src
, rho_path
)
105 # Verify both content and props have been copied
107 props
= { 'phony-prop' : '*',
108 'svn:mergeinfo' : '' }
110 props
= { 'phony-prop' : '*' }
112 expected_disk
.tweak('A/D/G/rho',
113 contents
="This is the file 'pi'.\n",
115 actual_disk
= svntest
.tree
.build_tree_from_wc(wc_dir
, 1)
116 svntest
.tree
.compare_trees("disk", actual_disk
, expected_disk
.old_tree())
119 expected_status
.tweak('A/D/G/rho', status
='R ', copied
='+', wc_rev
='-')
120 svntest
.actions
.run_and_verify_status(wc_dir
, expected_status
)
122 expected_status
.tweak('A/D/G/rho', status
=' ', copied
=None, wc_rev
='2')
123 expected_output
= ["Reverted '" + rho_path
+ "'\n"]
124 svntest
.actions
.run_and_verify_svn(None, expected_output
, [],
125 'revert', '-R', wc_dir
)
126 svntest
.actions
.run_and_verify_status(wc_dir
, expected_status
)
129 expected_disk
= svntest
.main
.greek_state
.copy()
130 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 1)
131 expected_disk
.tweak('A/D/G/pi',
132 props
={ 'phony-prop': '*' })
133 expected_disk
.tweak('A/D/G/rho',
134 props
={ 'svn:eol-style': 'LF' })
135 actual_disk
= svntest
.tree
.build_tree_from_wc(wc_dir
, 1)
136 svntest
.tree
.compare_trees("disk", actual_disk
, expected_disk
.old_tree())
141 ######################################################################
144 # Each test must return on success or raise on failure.
147 #----------------------------------------------------------------------
149 def revert_from_wc_root(sbox
):
150 "revert relative to wc root"
152 sbox
.build(read_only
= True)
157 # Mostly taken from basic_revert
158 # Modify some files and props.
159 beta_path
= os
.path
.join('A', 'B', 'E', 'beta')
160 gamma_path
= os
.path
.join('A', 'D', 'gamma')
162 rho_path
= os
.path
.join('A', 'D', 'G', 'rho')
163 zeta_path
= os
.path
.join('A', 'D', 'H', 'zeta')
164 svntest
.main
.file_append(beta_path
, "Added some text to 'beta'.\n")
165 svntest
.main
.file_append(iota_path
, "Added some text to 'iota'.\n")
166 svntest
.main
.file_append(rho_path
, "Added some text to 'rho'.\n")
167 svntest
.main
.file_append(zeta_path
, "Added some text to 'zeta'.\n")
169 svntest
.actions
.run_and_verify_svn("Add command", None, [],
171 svntest
.actions
.run_and_verify_svn("Add prop command", None, [],
172 'ps', 'random-prop', 'propvalue',
174 svntest
.actions
.run_and_verify_svn("Add prop command", None, [],
175 'ps', 'random-prop', 'propvalue',
177 svntest
.actions
.run_and_verify_svn("Add prop command", None, [],
178 'ps', 'random-prop', 'propvalue',
180 svntest
.actions
.run_and_verify_svn("Add prop command", None, [],
181 'ps', 'random-prop', 'propvalue',
184 # Verify modified status.
185 expected_output
= svntest
.actions
.get_virginal_state('', 1)
186 expected_output
.tweak('A/B/E/beta', 'A/D/G/rho', status
='M ')
187 expected_output
.tweak('iota', status
='MM')
188 expected_output
.tweak('', 'A/D/gamma', 'A', status
=' M')
189 expected_output
.add({
190 'A/D/H/zeta' : Item(status
='A ', wc_rev
=0),
193 svntest
.actions
.run_and_verify_status('', expected_output
)
196 svntest
.actions
.run_and_verify_svn("Revert command", None, [],
199 svntest
.actions
.run_and_verify_svn("Revert command", None, [],
200 'revert', gamma_path
)
202 svntest
.actions
.run_and_verify_svn("Revert command", None, [],
205 svntest
.actions
.run_and_verify_svn("Revert command", None, [],
208 svntest
.actions
.run_and_verify_svn("Revert command", None, [],
211 svntest
.actions
.run_and_verify_svn("Revert command", None, [],
214 svntest
.actions
.run_and_verify_svn("Revert command", None, [],
217 # Verify unmodified status.
218 expected_output
= svntest
.actions
.get_virginal_state('', 1)
220 svntest
.actions
.run_and_verify_status('', expected_output
)
223 def revert_reexpand_keyword(sbox
):
224 "revert reexpands manually contracted keyword"
226 # This is for issue #1663. The bug is that if the only difference
227 # between a locally modified working file and the base version of
228 # same was that the former had a contracted keyword that would be
229 # expanded in the latter, then 'svn revert' wouldn't notice the
230 # difference, and therefore wouldn't revert. And why wouldn't it
231 # notice? Since text bases are always stored with keywords
232 # contracted, and working files are contracted before comparison
233 # with text base, there would appear to be no difference when the
234 # contraction is the only difference. For most commands, this is
235 # correct -- but revert's job is to restore the working file, not
240 newfile_path
= os
.path
.join(wc_dir
, "newfile")
241 unexpanded_contents
= "This is newfile: $Rev$.\n"
243 # Put an unexpanded keyword into iota.
244 svntest
.main
.file_write(newfile_path
, unexpanded_contents
)
246 # Commit, without svn:keywords property set.
247 svntest
.main
.run_svn(None, 'add', newfile_path
)
248 svntest
.main
.run_svn(None,
249 'commit', '-m', 'r2', newfile_path
)
251 # Set the property and commit. This should expand the keyword.
252 svntest
.main
.run_svn(None, 'propset', 'svn:keywords', 'rev', newfile_path
)
253 svntest
.main
.run_svn(None,
254 'commit', '-m', 'r3', newfile_path
)
256 # Verify that the keyword got expanded.
257 def check_expanded(path
):
259 lines
= fp
.readlines()
261 if lines
[0] != "This is newfile: $Rev: 3 $.\n":
262 raise svntest
.Failure
264 check_expanded(newfile_path
)
266 # Now un-expand the keyword again.
267 svntest
.main
.file_write(newfile_path
, unexpanded_contents
)
269 fp
= open(newfile_path
, 'r')
270 lines
= fp
.readlines()
273 # Revert the file. The keyword should reexpand.
274 svntest
.main
.run_svn(None, 'revert', newfile_path
)
276 # Verify that the keyword got re-expanded.
277 check_expanded(newfile_path
)
280 #----------------------------------------------------------------------
281 # Regression test for issue #1775:
282 # Should be able to revert a file with no properties i.e. no prop-base
283 def revert_replaced_file_without_props(sbox
):
284 "revert a replaced file with no properties"
289 file1_path
= os
.path
.join(wc_dir
, 'file1')
291 # Add a new file, file1, that has no prop-base
292 svntest
.main
.file_append(file1_path
, "This is the file 'file1' revision 2.")
293 svntest
.actions
.run_and_verify_svn(None, None, [], 'add', file1_path
)
296 expected_output
= svntest
.wc
.State(wc_dir
, {
297 'file1' : Item(verb
='Adding')
300 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 1)
301 expected_status
.add({
302 'file1' : Item(status
=' ', wc_rev
=2),
305 svntest
.actions
.run_and_verify_commit(wc_dir
, expected_output
,
306 expected_status
, None, wc_dir
)
309 svntest
.actions
.run_and_verify_svn(None, None, [], 'rm', file1_path
)
311 # test that file1 is scheduled for deletion.
312 expected_status
.tweak('file1', status
='D ')
313 svntest
.actions
.run_and_verify_status(wc_dir
, expected_status
)
315 # recreate and add file1
316 svntest
.main
.file_append(file1_path
, "This is the file 'file1' revision 3.")
317 svntest
.actions
.run_and_verify_svn(None, None, [], 'add', file1_path
)
319 # Test to see if file1 is schedule for replacement
320 expected_status
.tweak('file1', status
='R ')
321 svntest
.actions
.run_and_verify_status(wc_dir
, expected_status
)
324 svntest
.actions
.run_and_verify_svn(None, ["Reverted '" + file1_path
+ "'\n"],
325 [], 'revert', file1_path
)
327 # test that file1 really was reverted
328 expected_status
.tweak('file1', status
=' ', wc_rev
=2)
329 svntest
.actions
.run_and_verify_status(wc_dir
, expected_status
)
331 #----------------------------------------------------------------------
332 # Regression test for issue #876:
333 # svn revert of an svn move'd file does not revert the file
334 def revert_moved_file(sbox
):
335 "revert a moved file"
337 sbox
.build(read_only
= True)
339 iota_path
= os
.path
.join(wc_dir
, 'iota')
340 iota_path_moved
= os
.path
.join(wc_dir
, 'iota_moved')
342 svntest
.actions
.run_and_verify_svn(None, None, [], 'mv', iota_path
,
344 expected_output
= svntest
.actions
.get_virginal_state(wc_dir
, 1)
345 expected_output
.tweak('iota', status
='D ')
346 expected_output
.add({
347 'iota_moved' : Item(status
='A ', copied
='+', wc_rev
='-'),
349 svntest
.actions
.run_and_verify_status(wc_dir
, expected_output
)
351 # now revert the file iota
352 svntest
.actions
.run_and_verify_svn(None,
353 ["Reverted '" + iota_path
+ "'\n"], [], 'revert', iota_path
)
355 # at this point, svn status on iota_path_moved should return nothing
356 # since it should disappear on reverting the move, and since svn status
357 # on a non-existent file returns nothing.
359 svntest
.actions
.run_and_verify_svn(None, [], [],
360 'status', '-v', iota_path_moved
)
363 #----------------------------------------------------------------------
364 # Test for issue 2135
366 # It is like merge_file_replace (in merge_tests.py), but reverts file
369 def revert_file_merge_replace_with_history(sbox
):
370 "revert a merge replacement of file with history"
375 # File scheduled for deletion
376 rho_path
= os
.path
.join(wc_dir
, 'A', 'D', 'G', 'rho')
377 svntest
.actions
.run_and_verify_svn(None, None, [], 'rm', rho_path
)
379 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 1)
380 expected_status
.tweak('A/D/G/rho', status
='D ')
381 svntest
.actions
.run_and_verify_status(wc_dir
, expected_status
)
383 expected_output
= svntest
.wc
.State(wc_dir
, {
384 'A/D/G/rho': Item(verb
='Deleting'),
387 expected_status
.remove('A/D/G/rho')
390 svntest
.actions
.run_and_verify_commit(wc_dir
,
394 # create new rho file
395 svntest
.main
.file_write(rho_path
, "new rho\n")
398 svntest
.actions
.run_and_verify_svn(None, None, [], 'add', rho_path
)
401 expected_status
.add({
402 'A/D/G/rho' : Item(status
='A ', wc_rev
='0')
404 svntest
.actions
.run_and_verify_status(wc_dir
, expected_status
)
405 expected_output
= svntest
.wc
.State(wc_dir
, {
406 'A/D/G/rho': Item(verb
='Adding'),
409 svntest
.actions
.run_and_verify_commit(wc_dir
,
413 # Update working copy
414 expected_output
= svntest
.wc
.State(wc_dir
, {})
415 expected_disk
= svntest
.main
.greek_state
.copy()
416 expected_disk
.tweak('A/D/G/rho', contents
='new rho\n' )
417 expected_status
.tweak(wc_rev
='3')
418 expected_status
.tweak('A/D/G/rho', status
=' ')
420 svntest
.actions
.run_and_verify_update(wc_dir
,
425 # merge changes from r3:1
426 expected_output
= svntest
.wc
.State(wc_dir
, {
427 'A/D/G/rho': Item(status
='R ')
429 expected_status
.tweak('A/D/G/rho', status
='R ', copied
='+', wc_rev
='-')
430 expected_skip
= wc
.State(wc_dir
, { })
431 expected_disk
.tweak('A/D/G/rho', contents
="This is the file 'rho'.\n")
432 svntest
.actions
.run_and_verify_merge(wc_dir
, '3', '1',
440 svntest
.actions
.run_and_verify_svn(None,
442 [], 'revert', rho_path
)
444 # test that rho really was reverted
445 expected_status
.tweak('A/D/G/rho', copied
=None, status
=' ', wc_rev
=3)
446 svntest
.actions
.run_and_verify_status(wc_dir
, expected_status
)
448 actual_disk
= svntest
.tree
.build_tree_from_wc(wc_dir
, 1)
449 expected_disk
.tweak('A/D/G/rho', contents
="new rho\n")
450 svntest
.tree
.compare_trees("disk", actual_disk
, expected_disk
.old_tree())
452 # Make sure the revert removed the copy from information.
453 exit_code
, output
, err
= svntest
.actions
.run_and_verify_svn(None, None, [],
456 if line
.find("Copied") != -1:
457 print "Error: Revert didn't get rid of copy from information"
458 raise svntest
.Failure
460 def revert_wc_to_wc_replace_with_props(sbox
):
461 "revert svn cp PATH PATH replace file with props"
463 revert_replacement_with_props(sbox
, 1)
465 def revert_repos_to_wc_replace_with_props(sbox
):
466 "revert svn cp URL PATH replace file with props"
468 revert_replacement_with_props(sbox
, 0)
470 def revert_after_second_replace(sbox
):
471 "revert file after second replace"
473 sbox
.build(read_only
= True)
476 # File scheduled for deletion
477 rho_path
= os
.path
.join(wc_dir
, 'A', 'D', 'G', 'rho')
478 svntest
.actions
.run_and_verify_svn(None, None, [], 'rm', rho_path
)
480 # Status before attempting copy
481 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 1)
482 expected_status
.tweak('A/D/G/rho', status
='D ')
483 svntest
.actions
.run_and_verify_status(wc_dir
, expected_status
)
485 # Replace file for the first time
486 pi_src
= os
.path
.join(wc_dir
, 'A', 'D', 'G', 'pi')
488 svntest
.actions
.run_and_verify_svn(None, None, [],
489 'cp', pi_src
, rho_path
)
491 expected_status
.tweak('A/D/G/rho', status
='R ', copied
='+', wc_rev
='-')
492 svntest
.actions
.run_and_verify_status(wc_dir
, expected_status
)
494 # Now delete replaced file.
495 svntest
.actions
.run_and_verify_svn(None, None, [], 'rm', '--force', rho_path
)
497 # Status should be same as after first delete
498 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 1)
499 expected_status
.tweak('A/D/G/rho', status
='D ')
500 svntest
.actions
.run_and_verify_status(wc_dir
, expected_status
)
502 # Replace file for the second time
503 pi_src
= os
.path
.join(wc_dir
, 'A', 'D', 'G', 'pi')
505 svntest
.actions
.run_and_verify_svn(None, None, [], 'cp', pi_src
, rho_path
)
507 expected_status
.tweak('A/D/G/rho', status
='R ', copied
='+', wc_rev
='-')
508 svntest
.actions
.run_and_verify_status(wc_dir
, expected_status
)
511 svntest
.actions
.run_and_verify_svn(None, None, [],
512 'revert', '-R', wc_dir
)
515 expected_disk
= svntest
.main
.greek_state
.copy()
516 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 1)
517 actual_disk
= svntest
.tree
.build_tree_from_wc(wc_dir
, 1)
518 svntest
.tree
.compare_trees("disk", actual_disk
, expected_disk
.old_tree())
521 #----------------------------------------------------------------------
522 # Tests for issue #2517.
524 # Manual conflict resolution leads to spurious revert report.
526 def revert_after_manual_conflict_resolution__text(sbox
):
527 "revert after manual text-conflict resolution"
529 # Make two working copies
531 wc_dir_1
= sbox
.wc_dir
532 wc_dir_2
= sbox
.add_wc_path('other')
533 svntest
.actions
.duplicate_dir(wc_dir_1
, wc_dir_2
)
535 # Cause a (text) conflict
536 iota_path_1
= os
.path
.join(wc_dir_1
, 'iota')
537 iota_path_2
= os
.path
.join(wc_dir_2
, 'iota')
538 svntest
.main
.file_write(iota_path_1
, 'Modified iota text')
539 svntest
.main
.file_write(iota_path_2
, 'Conflicting iota text')
540 svntest
.main
.run_svn(None,
541 'commit', '-m', 'r2', wc_dir_1
)
542 svntest
.main
.run_svn(None,
545 # Resolve the conflict "manually"
546 svntest
.main
.file_write(iota_path_2
, 'Modified iota text')
547 os
.remove(iota_path_2
+ '.mine')
548 os
.remove(iota_path_2
+ '.r1')
549 os
.remove(iota_path_2
+ '.r2')
551 # Verify no output from status, diff, or revert
552 svntest
.actions
.run_and_verify_svn(None, [], [], "status", wc_dir_2
)
553 svntest
.actions
.run_and_verify_svn(None, [], [], "diff", wc_dir_2
)
554 svntest
.actions
.run_and_verify_svn(None, [], [], "revert", "-R", wc_dir_2
)
556 def revert_after_manual_conflict_resolution__prop(sbox
):
557 "revert after manual property-conflict resolution"
559 # Make two working copies
561 wc_dir_1
= sbox
.wc_dir
562 wc_dir_2
= sbox
.add_wc_path('other')
563 svntest
.actions
.duplicate_dir(wc_dir_1
, wc_dir_2
)
565 # Cause a (property) conflict
566 iota_path_1
= os
.path
.join(wc_dir_1
, 'iota')
567 iota_path_2
= os
.path
.join(wc_dir_2
, 'iota')
568 svntest
.main
.run_svn(None, 'propset', 'foo', '1', iota_path_1
)
569 svntest
.main
.run_svn(None, 'propset', 'foo', '2', iota_path_2
)
570 svntest
.main
.run_svn(None,
571 'commit', '-m', 'r2', wc_dir_1
)
572 svntest
.main
.run_svn(None,
575 # Resolve the conflict "manually"
576 svntest
.main
.run_svn(None, 'propset', 'foo', '1', iota_path_2
)
577 os
.remove(iota_path_2
+ '.prej')
579 # Verify no output from status, diff, or revert
580 svntest
.actions
.run_and_verify_svn(None, [], [], "status", wc_dir_2
)
581 svntest
.actions
.run_and_verify_svn(None, [], [], "diff", wc_dir_2
)
582 svntest
.actions
.run_and_verify_svn(None, [], [], "revert", "-R", wc_dir_2
)
584 def revert_propset__dir(sbox
):
585 "revert a simple propset on a dir"
587 sbox
.build(read_only
= True)
589 a_path
= os
.path
.join(wc_dir
, 'A')
590 svntest
.main
.run_svn(None, 'propset', 'foo', 'x', a_path
)
591 expected_output
= re
.escape("Reverted '" + a_path
+ "'")
592 svntest
.actions
.run_and_verify_svn(None, expected_output
, [], "revert",
595 def revert_propset__file(sbox
):
596 "revert a simple propset on a file"
598 sbox
.build(read_only
= True)
600 iota_path
= os
.path
.join(wc_dir
, 'iota')
601 svntest
.main
.run_svn(None, 'propset', 'foo', 'x', iota_path
)
602 expected_output
= re
.escape("Reverted '" + iota_path
+ "'")
603 svntest
.actions
.run_and_verify_svn(None, expected_output
, [], "revert",
606 def revert_propdel__dir(sbox
):
607 "revert a simple propdel on a dir"
611 a_path
= os
.path
.join(wc_dir
, 'A')
612 svntest
.main
.run_svn(None, 'propset', 'foo', 'x', a_path
)
613 svntest
.main
.run_svn(None,
614 'commit', '-m', 'ps', a_path
)
615 svntest
.main
.run_svn(None, 'propdel', 'foo', a_path
)
616 expected_output
= re
.escape("Reverted '" + a_path
+ "'")
617 svntest
.actions
.run_and_verify_svn(None, expected_output
, [], "revert",
620 def revert_propdel__file(sbox
):
621 "revert a simple propdel on a file"
625 iota_path
= os
.path
.join(wc_dir
, 'iota')
626 svntest
.main
.run_svn(None, 'propset', 'foo', 'x', iota_path
)
627 svntest
.main
.run_svn(None,
628 'commit', '-m', 'ps', iota_path
)
629 svntest
.main
.run_svn(None, 'propdel', 'foo', iota_path
)
630 expected_output
= re
.escape("Reverted '" + iota_path
+ "'")
631 svntest
.actions
.run_and_verify_svn(None, expected_output
, [], "revert",
634 def revert_replaced_with_history_file_1(sbox
):
635 "revert a committed replace-with-history == no-op"
639 iota_path
= os
.path
.join(wc_dir
, 'iota')
640 mu_path
= os
.path
.join(wc_dir
, 'A', 'mu')
642 # Remember the original text of 'mu'
643 exit_code
, text_r1
, err
= svntest
.actions
.run_and_verify_svn(None, None, [],
645 # delete mu and replace it with a copy of iota
646 svntest
.main
.run_svn(None, 'rm', mu_path
)
647 svntest
.main
.run_svn(None, 'mv', iota_path
, mu_path
)
649 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 1)
650 expected_status
.tweak('A/mu', status
=' ', wc_rev
=2)
651 expected_status
.remove('iota')
652 expected_output
= svntest
.wc
.State(wc_dir
, {
653 'iota': Item(verb
='Deleting'),
654 'A/mu': Item(verb
='Replacing'),
656 svntest
.actions
.run_and_verify_commit(wc_dir
,
661 # update the working copy
662 svntest
.main
.run_svn(None, 'up', wc_dir
)
664 # now revert back to the state in r1
665 expected_output
= svntest
.wc
.State(wc_dir
, {
666 'A/mu': Item(status
='R '),
667 'iota': Item(status
='A ')
669 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 2)
670 expected_status
.tweak('A/mu', status
='R ', copied
='+', wc_rev
='-')
671 expected_status
.tweak('iota', status
='A ', copied
='+', wc_rev
='-')
672 expected_skip
= wc
.State(wc_dir
, { })
673 expected_disk
= svntest
.main
.greek_state
.copy()
674 svntest
.actions
.run_and_verify_merge(wc_dir
, '2', '1',
682 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 2)
683 expected_status
.tweak('A/mu', status
=' ', wc_rev
=3)
684 expected_status
.tweak('iota', status
=' ', wc_rev
=3)
685 expected_output
= svntest
.wc
.State(wc_dir
, {
686 'iota': Item(verb
='Adding'),
687 'A/mu': Item(verb
='Replacing'),
689 svntest
.actions
.run_and_verify_commit(wc_dir
,
694 # Verify the content of 'mu'
695 svntest
.actions
.run_and_verify_svn(None, text_r1
, [], 'cat', mu_path
)
697 # situation: no local modifications, mu has its original content again.
699 # revert 'mu' locally, shouldn't change a thing.
700 svntest
.actions
.run_and_verify_svn(None, [], [], "revert",
703 # Verify the content of 'mu'
704 svntest
.actions
.run_and_verify_svn(None, text_r1
, [], 'cat', mu_path
)
706 #----------------------------------------------------------------------
707 # Test for issue #2804.
708 def status_of_missing_dir_after_revert(sbox
):
709 "status after schedule-delete, revert, and local rm"
711 sbox
.build(read_only
= True)
713 A_D_G_path
= os
.path
.join(wc_dir
, "A", "D", "G")
715 svntest
.actions
.run_and_verify_svn(None, None, [], "rm", A_D_G_path
)
716 expected_output
= re
.escape("Reverted '" + A_D_G_path
+ "'")
717 svntest
.actions
.run_and_verify_svn(None, expected_output
, [], "revert",
720 expected_output
= svntest
.verify
.UnorderedOutput(
721 ["D " + os
.path
.join(A_D_G_path
, "pi") + "\n",
722 "D " + os
.path
.join(A_D_G_path
, "rho") + "\n",
723 "D " + os
.path
.join(A_D_G_path
, "tau") + "\n"])
724 svntest
.actions
.run_and_verify_svn(None, expected_output
, [],
727 svntest
.main
.safe_rmtree(A_D_G_path
)
729 expected_output
= svntest
.verify
.UnorderedOutput(
730 ["! " + A_D_G_path
+ "\n"])
731 svntest
.actions
.run_and_verify_svn(None, expected_output
, [], "status",
734 #----------------------------------------------------------------------
735 # Test for issue #2804 with replaced directory
736 def status_of_missing_dir_after_revert_replaced_with_history_dir(sbox
):
737 "status after replace+, revert, and local rm"
741 repo_url
= sbox
.repo_url
743 # delete A/D/G and commit
744 G_path
= os
.path
.join(wc_dir
, "A", "D", "G")
745 svntest
.actions
.run_and_verify_svn(None, None, [], "rm", G_path
)
746 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 1)
747 expected_status
.remove('A/D/G', 'A/D/G/rho', 'A/D/G/pi', 'A/D/G/tau')
748 expected_output
= svntest
.wc
.State(wc_dir
, {
749 'A/D/G': Item(verb
='Deleting'),
751 svntest
.actions
.run_and_verify_commit(wc_dir
,
756 # copy A/D/G from A/B/E and commit
757 E_path
= os
.path
.join(wc_dir
, "A", "B", "E")
758 svntest
.actions
.run_and_verify_svn(None, None, [], "cp", E_path
, G_path
)
759 expected_status
.add({
760 'A/D/G' : Item(status
=' ', wc_rev
='3'),
761 'A/D/G/alpha' : Item(status
=' ', wc_rev
='3'),
762 'A/D/G/beta' : Item(status
=' ', wc_rev
='3')
764 expected_output
= svntest
.wc
.State(wc_dir
, {
765 'A/D/G': Item(verb
='Adding'),
767 svntest
.actions
.run_and_verify_commit(wc_dir
,
772 # update the working copy
773 svntest
.main
.run_svn(None, 'up', wc_dir
)
775 # now rollback to r1, thereby reinstating the old 'G'
776 ### Eventually, expected output for 'A/D/G' should be 'R '
777 ### (replaced) instead of 'A ' (added). See issue #571 for details.
778 expected_output
= svntest
.wc
.State(wc_dir
, {
779 'A/D/G': Item(status
='R '),
780 'A/D/G/rho': Item(status
='A '),
781 'A/D/G/pi': Item(status
='A '),
782 'A/D/G/tau': Item(status
='A '),
784 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 3)
785 expected_status
.tweak('A/D/G', status
='R ', copied
='+', wc_rev
='-')
786 expected_status
.tweak('A/D/G/rho', status
='A ', copied
='+', wc_rev
='-')
787 expected_status
.tweak('A/D/G/pi', status
='A ', copied
='+', wc_rev
='-')
788 expected_status
.tweak('A/D/G/tau', status
='A ', copied
='+', wc_rev
='-')
789 expected_status
.add({
790 'A/D/G/alpha' : Item(status
='D ', copied
='+', wc_rev
='-'),
791 'A/D/G/beta' : Item(status
='D ', copied
='+', wc_rev
='-')
793 expected_skip
= wc
.State(wc_dir
, { })
794 expected_disk
= svntest
.main
.greek_state
.copy()
795 svntest
.actions
.run_and_verify_merge(wc_dir
, '3', '1',
803 # now test if the revert works ok
804 expected_output
= svntest
.verify
.UnorderedOutput(
805 ["Reverted '" + G_path
+ "'\n",
806 "Reverted '" + os
.path
.join(G_path
, 'pi') + "'\n",
807 "Reverted '" + os
.path
.join(G_path
, 'rho') + "'\n",
808 "Reverted '" + os
.path
.join(G_path
, 'tau') + "'\n",
809 "Reverted '" + os
.path
.join(G_path
, 'alpha') + "'\n",
810 "Reverted '" + os
.path
.join(G_path
, 'beta') + "'\n"])
812 svntest
.actions
.run_and_verify_svn(None, expected_output
, [], "revert", "-R",
815 expected_output
= svntest
.verify
.UnorderedOutput(
816 ["? " + os
.path
.join(G_path
, "pi") + "\n",
817 "? " + os
.path
.join(G_path
, "rho") + "\n",
818 "? " + os
.path
.join(G_path
, "tau") + "\n"])
819 svntest
.actions
.run_and_verify_svn(None, expected_output
, [],
822 svntest
.main
.safe_rmtree(G_path
)
824 expected_output
= svntest
.verify
.UnorderedOutput(
825 ["! " + G_path
+ "\n"])
826 svntest
.actions
.run_and_verify_svn(None, expected_output
, [], "status",
829 # Test for issue #2928.
830 def revert_replaced_with_history_file_2(sbox
):
831 "reverted replace with history restores checksum"
835 iota_path
= os
.path
.join(wc_dir
, 'iota')
836 mu_path
= os
.path
.join(wc_dir
, 'A', 'mu')
838 # Delete mu and replace it with a copy of iota
839 svntest
.main
.run_svn(None, 'rm', mu_path
)
840 svntest
.main
.run_svn(None, 'cp', iota_path
, mu_path
)
843 svntest
.main
.run_svn(None, 'revert', mu_path
)
845 # If we make local mods to the reverted mu the commit will
846 # fail if the checksum is incorrect.
847 svntest
.main
.file_write(mu_path
, "new text")
848 expected_output
= svntest
.wc
.State(wc_dir
, {
849 'A/mu': Item(verb
='Sending'),
851 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 1)
852 expected_status
.tweak('A/mu', status
=' ', wc_rev
=2)
853 svntest
.actions
.run_and_verify_commit(wc_dir
,
858 ########################################################################
862 # list all tests here, starting with None:
865 revert_reexpand_keyword
,
866 revert_replaced_file_without_props
,
867 XFail(revert_moved_file
),
868 revert_wc_to_wc_replace_with_props
,
869 revert_file_merge_replace_with_history
,
870 revert_repos_to_wc_replace_with_props
,
871 revert_after_second_replace
,
872 revert_after_manual_conflict_resolution__text
,
873 revert_after_manual_conflict_resolution__prop
,
875 revert_propset__file
,
877 revert_propdel__file
,
878 revert_replaced_with_history_file_1
,
879 status_of_missing_dir_after_revert
,
880 status_of_missing_dir_after_revert_replaced_with_history_dir
,
881 revert_replaced_with_history_file_2
,
884 if __name__
== '__main__':
885 svntest
.main
.run_tests(test_list
)