1#!/usr/bin/env python
2#
3#  update_tests.py:  testing update cases.
4#
5#  Subversion is a tool for revision control.
6#  See http://subversion.apache.org for more information.
7#
8# ====================================================================
9#    Licensed to the Apache Software Foundation (ASF) under one
10#    or more contributor license agreements.  See the NOTICE file
11#    distributed with this work for additional information
12#    regarding copyright ownership.  The ASF licenses this file
13#    to you under the Apache License, Version 2.0 (the
14#    "License"); you may not use this file except in compliance
15#    with the License.  You may obtain a copy of the License at
16#
17#      http://www.apache.org/licenses/LICENSE-2.0
18#
19#    Unless required by applicable law or agreed to in writing,
20#    software distributed under the License is distributed on an
21#    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
22#    KIND, either express or implied.  See the License for the
23#    specific language governing permissions and limitations
24#    under the License.
25######################################################################
26
27# General modules
28import sys, re, os, subprocess
29import time
30import logging
31
32logger = logging.getLogger()
33
34# Our testing module
35import svntest
36from svntest import wc, actions, verify, deeptrees
37from svntest.mergetrees import expected_merge_output
38from svntest.mergetrees import set_up_branch
39
40# (abbreviation)
41Skip = svntest.testcase.Skip_deco
42SkipUnless = svntest.testcase.SkipUnless_deco
43XFail = svntest.testcase.XFail_deco
44Issues = svntest.testcase.Issues_deco
45Issue = svntest.testcase.Issue_deco
46Wimp = svntest.testcase.Wimp_deco
47Item = svntest.wc.StateItem
48exp_noop_up_out = svntest.actions.expected_noop_update_output
49
50from svntest.main import SVN_PROP_MERGEINFO, server_has_mergeinfo
51
52######################################################################
53# Tests
54#
55#   Each test must return on success or raise on failure.
56
57
58
59def update_binary_file(sbox):
60  "update a locally-modified binary file"
61
62  sbox.build()
63  wc_dir = sbox.wc_dir
64
65  # Add a binary file to the project.
66  theta_contents = open(os.path.join(sys.path[0], "theta.bin"), 'rb').read()
67  # Write PNG file data into 'A/theta'.
68  theta_path = sbox.ospath('A/theta')
69  svntest.main.file_write(theta_path, theta_contents, 'wb')
70
71  svntest.main.run_svn(None, 'add', theta_path)
72
73  # Created expected output tree for 'svn ci'
74  expected_output = svntest.wc.State(wc_dir, {
75    'A/theta' : Item(verb='Adding  (bin)'),
76    })
77
78  # Create expected status tree
79  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
80  expected_status.add({
81    'A/theta' : Item(status='  ', wc_rev=2),
82    })
83
84  # Commit the new binary file, creating revision 2.
85  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
86                                        expected_status)
87
88  # Make a backup copy of the working copy.
89  wc_backup = sbox.add_wc_path('backup')
90  svntest.actions.duplicate_dir(wc_dir, wc_backup)
91  theta_backup_path = os.path.join(wc_backup, 'A', 'theta')
92
93  # Make a change to the binary file in the original working copy
94  svntest.main.file_append(theta_path, "revision 3 text")
95  theta_contents_r3 = theta_contents + b"revision 3 text"
96
97  # Created expected output tree for 'svn ci'
98  expected_output = svntest.wc.State(wc_dir, {
99    'A/theta' : Item(verb='Sending'),
100    })
101
102  # Create expected status tree
103  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
104  expected_status.add({
105    'A/theta' : Item(status='  ', wc_rev=3),
106    })
107
108  # Commit original working copy again, creating revision 3.
109  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
110                                        expected_status)
111
112  # Now start working in the backup working copy:
113
114  # Make a local mod to theta
115  svntest.main.file_append(theta_backup_path, "extra theta text")
116  theta_contents_local = theta_contents + b"extra theta text"
117
118  # Create expected output tree for an update of wc_backup.
119  expected_output = svntest.wc.State(wc_backup, {
120    'A/theta' : Item(status='C '),
121    })
122
123  # Create expected disk tree for the update --
124  #    look!  binary contents, and a binary property!
125  expected_disk = svntest.main.greek_state.copy()
126  expected_disk.add({
127    'A/theta' : Item(theta_contents_local,
128                     props={'svn:mime-type' : 'application/octet-stream'}),
129    })
130
131  # Create expected status tree for the update.
132  expected_status = svntest.actions.get_virginal_state(wc_backup, 3)
133  expected_status.add({
134    'A/theta' : Item(status='C ', wc_rev=3),
135    })
136
137  extra_files = ['theta.r2', 'theta.r3']
138
139  # Do the update and check the results in three ways.  Pass our
140  # custom singleton handler to verify the .orig file; this handler
141  # will verify the existence (and contents) of both binary files
142  # after the update finishes.
143  svntest.actions.run_and_verify_update(wc_backup,
144                                        expected_output,
145                                        expected_disk,
146                                        expected_status,
147                                        [], True,
148                                        extra_files=extra_files)
149
150#----------------------------------------------------------------------
151
152def update_binary_file_2(sbox):
153  "update to an old revision of a binary files"
154
155  sbox.build()
156  wc_dir = sbox.wc_dir
157
158  # Suck up contents of a test .png file.
159  theta_contents = open(os.path.join(sys.path[0], "theta.bin"), 'rb').read()
160
161  # 102400 is svn_txdelta_window_size.  We're going to make sure we
162  # have at least 102401 bytes of data in our second binary file (for
163  # no reason other than we have had problems in the past with getting
164  # svndiff data out of the repository for files > 102400 bytes).
165  # How?  Well, we'll just keep doubling the binary contents of the
166  # original theta.png until we're big enough.
167  zeta_contents = theta_contents
168  while(len(zeta_contents) < 102401):
169    zeta_contents = zeta_contents + zeta_contents
170
171  # Write our two files' contents out to disk, in A/theta and A/zeta.
172  theta_path = sbox.ospath('A/theta')
173  svntest.main.file_write(theta_path, theta_contents, 'wb')
174  zeta_path = sbox.ospath('A/zeta')
175  svntest.main.file_write(zeta_path, zeta_contents, 'wb')
176
177  # Now, `svn add' those two files.
178  svntest.main.run_svn(None, 'add', theta_path, zeta_path)
179
180  # Created expected output tree for 'svn ci'
181  expected_output = svntest.wc.State(wc_dir, {
182    'A/theta' : Item(verb='Adding  (bin)'),
183    'A/zeta' : Item(verb='Adding  (bin)'),
184    })
185
186  # Create expected status tree
187  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
188  expected_status.add({
189    'A/theta' : Item(status='  ', wc_rev=2),
190    'A/zeta' : Item(status='  ', wc_rev=2),
191    })
192
193  # Commit the new binary filea, creating revision 2.
194  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
195                                        expected_status)
196
197  # Make some mods to the binary files.
198  svntest.main.file_append(theta_path, "foobar")
199  new_theta_contents = theta_contents + b"foobar"
200  svntest.main.file_append(zeta_path, "foobar")
201  new_zeta_contents = zeta_contents + b"foobar"
202
203  # Created expected output tree for 'svn ci'
204  expected_output = svntest.wc.State(wc_dir, {
205    'A/theta' : Item(verb='Sending'),
206    'A/zeta' : Item(verb='Sending'),
207    })
208
209  # Create expected status tree
210  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
211  expected_status.add({
212    'A/theta' : Item(status='  ', wc_rev=3),
213    'A/zeta' : Item(status='  ', wc_rev=3),
214    })
215
216  # Commit original working copy again, creating revision 3.
217  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
218                                        expected_status)
219
220  # Create expected output tree for an update to rev 2.
221  expected_output = svntest.wc.State(wc_dir, {
222    'A/theta' : Item(status='U '),
223    'A/zeta' : Item(status='U '),
224    })
225
226  # Create expected disk tree for the update --
227  #    look!  binary contents, and a binary property!
228  expected_disk = svntest.main.greek_state.copy()
229  expected_disk.add({
230    'A/theta' : Item(theta_contents,
231                     props={'svn:mime-type' : 'application/octet-stream'}),
232    'A/zeta' : Item(zeta_contents,
233                    props={'svn:mime-type' : 'application/octet-stream'}),
234    })
235
236  # Create expected status tree for the update.
237  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
238  expected_status.add({
239    'A/theta' : Item(status='  ', wc_rev=2),
240    'A/zeta' : Item(status='  ', wc_rev=2),
241    })
242
243  # Do an update from revision 2 and make sure that our binary file
244  # gets reverted to its original contents.
245  svntest.actions.run_and_verify_update(wc_dir,
246                                        expected_output,
247                                        expected_disk,
248                                        expected_status,
249                                        [], True,
250                                        '-r', '2', wc_dir)
251
252
253#----------------------------------------------------------------------
254
255@Issue(4128)
256def update_binary_file_3(sbox):
257  "update locally modified file to equal versions"
258
259  sbox.build()
260  wc_dir = sbox.wc_dir
261
262  # Suck up contents of a test .png file.
263  theta_contents = open(os.path.join(sys.path[0], "theta.bin"), 'rb').read()
264
265  # Write our files contents out to disk, in A/theta.
266  theta_path = sbox.ospath('A/theta')
267  svntest.main.file_write(theta_path, theta_contents, 'wb')
268
269  # Now, `svn add' that file.
270  svntest.main.run_svn(None, 'add', theta_path)
271
272  # Created expected output tree for 'svn ci'
273  expected_output = svntest.wc.State(wc_dir, {
274    'A/theta' : Item(verb='Adding  (bin)'),
275    })
276
277  # Create expected status tree
278  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
279  expected_status.add({
280    'A/theta' : Item(status='  ', wc_rev=2),
281    })
282
283  # Commit the new binary file, creating revision 2.
284  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
285                                        expected_status)
286
287  # Make some mods to the binary files.
288  svntest.main.file_append(theta_path, "foobar")
289  new_theta_contents = theta_contents + b"foobar"
290
291  # Created expected output tree for 'svn ci'
292  expected_output = svntest.wc.State(wc_dir, {
293    'A/theta' : Item(verb='Sending'),
294    })
295
296  # Create expected status tree
297  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
298  expected_status.add({
299    'A/theta' : Item(status='  ', wc_rev=3),
300    })
301
302  # Commit modified working copy, creating revision 3.
303  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
304                                        expected_status)
305
306  # Now we locally modify the file back to the old version.
307  svntest.main.file_write(theta_path, theta_contents, 'wb')
308
309  # Create expected output tree for an update to rev 2.
310  expected_output = svntest.wc.State(wc_dir, {
311    'A/theta' : Item(status='G '),
312    })
313
314  # Create expected disk tree for the update
315  expected_disk = svntest.main.greek_state.copy()
316  expected_disk.add({
317    'A/theta' : Item(theta_contents,
318                     props={'svn:mime-type' : 'application/octet-stream'}),
319    })
320
321  # Create expected status tree for the update.
322  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
323  expected_status.add({
324    'A/theta' : Item(status='  ', wc_rev=2),
325    })
326
327  # Do an update from revision 2 and make sure that our binary file
328  # gets reverted to its original contents.
329  # This used to raise a conflict.
330  svntest.actions.run_and_verify_update(wc_dir,
331                                        expected_output,
332                                        expected_disk,
333                                        expected_status,
334                                        [], True,
335                                        '-r', '2', wc_dir)
336
337#----------------------------------------------------------------------
338
339def update_missing(sbox):
340  "update missing items (by name) in working copy"
341
342  sbox.build()
343  wc_dir = sbox.wc_dir
344
345  # Remove some files and dirs from the working copy.
346  mu_path = sbox.ospath('A/mu')
347  rho_path = sbox.ospath('A/D/G/rho')
348  E_path = sbox.ospath('A/B/E')
349  H_path = sbox.ospath('A/D/H')
350
351  # remove two files to verify that they get restored
352  os.remove(mu_path)
353  os.remove(rho_path)
354
355  ### FIXME I think directories work because they generate 'A'
356  ### feedback, is this the correct feedback?
357  svntest.main.safe_rmtree(E_path)
358  svntest.main.safe_rmtree(H_path)
359
360  # In single-db mode all missing items will just be restored
361  A_or_Restored = Item(verb='Restored')
362
363  # Create expected output tree for an update of the missing items by name
364  expected_output = svntest.wc.State(wc_dir, {
365    'A/mu'        : Item(verb='Restored'),
366    'A/D/G/rho'   : Item(verb='Restored'),
367    'A/B/E'       : A_or_Restored,
368    'A/B/E/alpha' : A_or_Restored,
369    'A/B/E/beta'  : A_or_Restored,
370    'A/D/H'       : A_or_Restored,
371    'A/D/H/chi'   : A_or_Restored,
372    'A/D/H/omega' : A_or_Restored,
373    'A/D/H/psi'   : A_or_Restored,
374    })
375
376  # Create expected disk tree for the update.
377  expected_disk = svntest.main.greek_state.copy()
378
379  # Create expected status tree for the update.
380  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
381
382  # Do the update and check the results in three ways.
383  svntest.actions.run_and_verify_update(wc_dir,
384                                        expected_output,
385                                        expected_disk,
386                                        expected_status,
387                                        [], False,
388                                        mu_path, rho_path,
389                                        E_path, H_path)
390
391#----------------------------------------------------------------------
392
393def update_ignores_added(sbox):
394  "update should not munge adds or replaces"
395
396  sbox.build()
397  wc_dir = sbox.wc_dir
398
399  # Commit something so there's actually a new revision to update to.
400  rho_path = sbox.ospath('A/D/G/rho')
401  svntest.main.file_append(rho_path, "More stuff in rho.\n")
402  svntest.main.run_svn(None,
403                       'ci', '-m', 'log msg', rho_path)
404
405  # Create a new file, 'zeta', and schedule it for addition.
406  zeta_path = sbox.ospath('A/B/zeta')
407  svntest.main.file_append(zeta_path, "This is the file 'zeta'.\n")
408  svntest.main.run_svn(None, 'add', zeta_path)
409
410  # Schedule another file, say, 'gamma', for replacement.
411  gamma_path = sbox.ospath('A/D/gamma')
412  svntest.main.run_svn(None, 'delete', gamma_path)
413  svntest.main.file_append(gamma_path, "This is a new 'gamma' now.\n")
414  svntest.main.run_svn(None, 'add', gamma_path)
415
416  # Now update.  "zeta at revision 0" should *not* be reported at all,
417  # so it should remain scheduled for addition at revision 0.  gamma
418  # was scheduled for replacement, so it also should remain marked as
419  # such, and maintain its revision of 1.
420
421  # Create expected output tree for an update of the wc_backup.
422  expected_output = svntest.wc.State(wc_dir, { })
423
424  # Create expected disk tree for the update.
425  expected_disk = svntest.main.greek_state.copy()
426  expected_disk.add({
427    'A/B/zeta' : Item("This is the file 'zeta'.\n"),
428    })
429  expected_disk.tweak('A/D/gamma', contents="This is a new 'gamma' now.\n")
430  expected_disk.tweak('A/D/G/rho',
431                      contents="This is the file 'rho'.\nMore stuff in rho.\n")
432
433  # Create expected status tree for the update.
434  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
435
436  # Before WC-NG we couldn't bump the wc_rev for gamma from 1 to 2 because it could
437  # be replaced with history and we couldn't store all the revision information.
438  # WC-NG just bumps the revision as it can easily store different revisions.
439  expected_status.tweak('A/D/gamma', wc_rev=2, status='R ')
440  expected_status.add({
441    'A/B/zeta' : Item(status='A ', wc_rev=0),
442    })
443
444  # Do the update and check the results in three ways.
445  svntest.actions.run_and_verify_update(wc_dir,
446                                        expected_output,
447                                        expected_disk,
448                                        expected_status)
449
450
451#----------------------------------------------------------------------
452
453def update_to_rev_zero(sbox):
454  "update to revision 0"
455
456  sbox.build()
457  wc_dir = sbox.wc_dir
458
459  iota_path = sbox.ospath('iota')
460  A_path = sbox.ospath('A')
461
462  # Create expected output tree for an update to rev 0
463  expected_output = svntest.wc.State(wc_dir, {
464    'iota' : Item(status='D '),
465    'A' : Item(status='D '),
466    })
467
468  # Create expected disk tree for the update to rev 0
469  expected_disk = svntest.wc.State(wc_dir, { })
470
471  # Do the update and check the results.
472  svntest.actions.run_and_verify_update(wc_dir,
473                                        expected_output,
474                                        expected_disk,
475                                        None, [], False,
476                                        '-r', '0', wc_dir)
477
478#----------------------------------------------------------------------
479
480def receive_overlapping_same_change(sbox):
481  "overlapping identical changes should not conflict"
482
483  ### (See https://issues.apache.org/jira/browse/SVN-682.)
484  ###
485  ### How this test works:
486  ###
487  ### Create working copy foo, modify foo/iota.  Duplicate foo,
488  ### complete with locally modified iota, to bar.  Now we should
489  ### have:
490  ###
491  ###    $ svn st foo
492  ###    M    foo/iota
493  ###    $ svn st bar
494  ###    M    bar/iota
495  ###    $
496  ###
497  ### Commit the change from foo, then update bar.  The repository
498  ### change should get folded into bar/iota with no conflict, since
499  ### the two modifications are identical.
500
501  sbox.build()
502  wc_dir = sbox.wc_dir
503
504  # Modify iota.
505  iota_path = sbox.ospath('iota')
506  svntest.main.file_append(iota_path, "A change to iota.\n")
507
508  # Duplicate locally modified wc, giving us the "other" wc.
509  other_wc = sbox.add_wc_path('other')
510  svntest.actions.duplicate_dir(wc_dir, other_wc)
511  other_iota_path = os.path.join(other_wc, 'iota')
512
513  # Created expected output tree for 'svn ci'
514  expected_output = svntest.wc.State(wc_dir, {
515    'iota' : Item(verb='Sending'),
516    })
517
518  # Create expected status tree
519  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
520  expected_status.tweak('iota', wc_rev=2)
521
522  # Commit the change, creating revision 2.
523  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
524                                        expected_status)
525
526  # Expected output tree for update of other_wc.
527  expected_output = svntest.wc.State(other_wc, {
528    'iota' : Item(status='G '),
529    })
530
531  # Expected disk tree for the update.
532  expected_disk = svntest.main.greek_state.copy()
533  expected_disk.tweak('iota',
534                      contents="This is the file 'iota'.\nA change to iota.\n")
535
536  # Expected status tree for the update.
537  expected_status = svntest.actions.get_virginal_state(other_wc, 2)
538
539  # Do the update and check the results in three ways.
540  svntest.actions.run_and_verify_update(other_wc,
541                                        expected_output,
542                                        expected_disk,
543                                        expected_status)
544
545#----------------------------------------------------------------------
546
547def update_to_resolve_text_conflicts(sbox):
548  "delete files and update to resolve text conflicts"
549
550  sbox.build()
551  wc_dir = sbox.wc_dir
552
553  # Make a backup copy of the working copy
554  wc_backup = sbox.add_wc_path('backup')
555  svntest.actions.duplicate_dir(wc_dir, wc_backup)
556
557  # Make a couple of local mods to files which will be committed
558  mu_path = sbox.ospath('A/mu')
559  rho_path = sbox.ospath('A/D/G/rho')
560  svntest.main.file_append(mu_path, 'Original appended text for mu\n')
561  svntest.main.file_append(rho_path, 'Original appended text for rho\n')
562  svntest.main.run_svn(None, 'propset', 'Kubla', 'Khan', rho_path)
563
564  # Make a couple of local mods to files which will be conflicted
565  mu_path_backup = os.path.join(wc_backup, 'A', 'mu')
566  rho_path_backup = os.path.join(wc_backup, 'A', 'D', 'G', 'rho')
567  svntest.main.file_append(mu_path_backup,
568                           'Conflicting appended text for mu\n')
569  svntest.main.file_append(rho_path_backup,
570                           'Conflicting appended text for rho\n')
571  svntest.main.run_svn(None, 'propset', 'Kubla', 'Xanadu', rho_path_backup)
572
573  # Created expected output tree for 'svn ci'
574  expected_output = svntest.wc.State(wc_dir, {
575    'A/mu' : Item(verb='Sending'),
576    'A/D/G/rho' : Item(verb='Sending'),
577    })
578
579  # Create expected status tree; all local revisions should be at 1,
580  # but mu and rho should be at revision 2.
581  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
582  expected_status.tweak('A/mu', wc_rev=2)
583  expected_status.tweak('A/D/G/rho', wc_rev=2, status='  ')
584
585  # Commit.
586  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
587                                        expected_status)
588
589  # Create expected output tree for an update of the wc_backup.
590  expected_output = svntest.wc.State(wc_backup, {
591    'A/mu' : Item(status='C '),
592    'A/D/G/rho' : Item(status='CC'),
593    })
594
595  # Create expected disk tree for the update.
596  expected_disk = svntest.main.greek_state.copy()
597  expected_disk.tweak('A/mu',
598                      contents="\n".join(["This is the file 'mu'.",
599                                          "<<<<<<< .mine",
600                                          "Conflicting appended text for mu",
601                                          "||||||| .r1",
602                                          "=======",
603                                          "Original appended text for mu",
604                                          ">>>>>>> .r2",
605                                          ""]))
606  expected_disk.tweak('A/D/G/rho',
607                      contents="\n".join(["This is the file 'rho'.",
608                                          "<<<<<<< .mine",
609                                          "Conflicting appended text for rho",
610                                          "||||||| .r1",
611                                          "=======",
612                                          "Original appended text for rho",
613                                          ">>>>>>> .r2",
614                                          ""]))
615
616  # Create expected status tree for the update.
617  expected_status = svntest.actions.get_virginal_state(wc_backup, 2)
618  expected_status.tweak('A/mu', status='C ')
619  expected_status.tweak('A/D/G/rho', status='CC')
620
621  # "Extra" files that we expect to result from the conflicts.
622  # These are expressed as list of regexps.  What a cool system!  :-)
623  extra_files = ['mu.*\.r1', 'mu.*\.r2', 'mu.*\.mine',
624                 'rho.*\.r1', 'rho.*\.r2', 'rho.*\.mine', 'rho.*\.prej']
625
626  # Do the update and check the results in three ways.
627  # All "extra" files are passed to detect_conflict_files().
628  svntest.actions.run_and_verify_update(wc_backup,
629                                        expected_output,
630                                        expected_disk,
631                                        expected_status,
632                                        extra_files=extra_files)
633
634  # remove the conflicting files to clear text conflict but not props conflict
635  os.remove(mu_path_backup)
636  os.remove(rho_path_backup)
637
638  ### TODO: Can't get run_and_verify_update to work here :-( I get
639  # the error "Unequal Types: one Node is a file, the other is a
640  # directory". Use run_svn and then run_and_verify_status instead
641  exit_code, stdout_lines, stdout_lines = svntest.main.run_svn(None, 'up',
642                                                               wc_backup)
643  if len (stdout_lines) > 0:
644    logger.warn("update 2 failed")
645    raise svntest.Failure
646
647  # Create expected status tree
648  expected_status = svntest.actions.get_virginal_state(wc_backup, 2)
649  expected_status.tweak('A/D/G/rho', status=' C')
650
651  svntest.actions.run_and_verify_status(wc_backup, expected_status)
652
653#----------------------------------------------------------------------
654
655def update_delete_modified_files(sbox):
656  "update that deletes modified files"
657
658  sbox.build()
659  wc_dir = sbox.wc_dir
660
661  # Delete a file
662  alpha_path = sbox.ospath('A/B/E/alpha')
663  svntest.actions.run_and_verify_svn(None, [],
664                                     'rm', alpha_path)
665
666  # Delete a directory containing files
667  G_path = sbox.ospath('A/D/G')
668  svntest.actions.run_and_verify_svn(None, [],
669                                     'rm', G_path)
670
671  # Commit
672  svntest.actions.run_and_verify_svn(None, [],
673                                     'ci', '-m', 'log msg', wc_dir)
674
675  ### Update before backdating to avoid obstructed update error for G
676  svntest.actions.run_and_verify_svn(None, [],
677                                     'up', wc_dir)
678
679  # Backdate to restore deleted items
680  svntest.actions.run_and_verify_svn(None, [],
681                                     'up', '-r', '1', wc_dir)
682
683  # Modify the file to be deleted, and a file in the directory to be deleted
684  svntest.main.file_append(alpha_path, 'appended alpha text\n')
685  pi_path = os.path.join(G_path, 'pi')
686  svntest.main.file_append(pi_path, 'appended pi text\n')
687
688  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
689  expected_status.tweak('A/B/E/alpha', 'A/D/G/pi', status='M ')
690
691  svntest.actions.run_and_verify_status(wc_dir, expected_status)
692
693  # Now update to 'delete' modified items -- that is, remove them from
694  # version control, but leave them on disk.  It used to be we would
695  # expect an 'obstructed update' error (see issue #1196), then we
696  # expected success (see issue #1806), and now we expect tree conflicts
697  # (see issue #2282) on the missing or unversioned items.
698  expected_output = svntest.wc.State(wc_dir, {
699    'A/B/E/alpha' : Item(status='  ', treeconflict='C'),
700    'A/D/G'       : Item(status='  ', treeconflict='C'),
701    })
702
703  expected_disk = svntest.main.greek_state.copy()
704  expected_disk.tweak('A/B/E/alpha',
705                      contents=\
706                      "This is the file 'alpha'.\nappended alpha text\n")
707  expected_disk.tweak('A/D/G/pi',
708                      contents=\
709                      "This is the file 'pi'.\nappended pi text\n")
710
711  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
712  # A/B/E/alpha and the subtree rooted at A/D/G had local modificiations
713  # prior to the update.  So there is a tree conflict and both A/B/E/alpha
714  # A/D/G remain after the update, scheduled for addition as copies of
715  # themselves from r1, along with the local modifications.
716  expected_status.tweak('A/B/E/alpha', status='A ', copied='+', wc_rev='-',
717                        treeconflict='C')
718  expected_status.tweak('A/D/G/pi', status='M ')
719  expected_status.tweak('A/D/G/pi', status='M ', copied='+', wc_rev='-')
720  expected_status.tweak('A/D/G/rho', 'A/D/G/tau', status='  ', copied='+',
721                        wc_rev='-')
722  expected_status.tweak('A/D/G', status='A ', copied='+', wc_rev='-',
723                        treeconflict='C')
724
725  svntest.actions.run_and_verify_update(wc_dir,
726                                        expected_output,
727                                        expected_disk,
728                                        expected_status)
729
730#----------------------------------------------------------------------
731
732# Issue 847.  Doing an add followed by a remove for an item in state
733# "deleted" caused the "deleted" state to get forgotten
734
735def update_after_add_rm_deleted(sbox):
736  "update after add/rm of deleted state"
737
738  sbox.build()
739  wc_dir = sbox.wc_dir
740
741  # Delete a file and directory from WC
742  alpha_path = sbox.ospath('A/B/E/alpha')
743  F_path = sbox.ospath('A/B/F')
744  svntest.actions.run_and_verify_svn(None, [], 'rm', alpha_path, F_path)
745
746  # Commit deletion
747  expected_output = svntest.wc.State(wc_dir, {
748    'A/B/E/alpha' : Item(verb='Deleting'),
749    'A/B/F'       : Item(verb='Deleting'),
750    })
751
752  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
753  expected_status.remove('A/B/E/alpha')
754  expected_status.remove('A/B/F')
755
756  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
757                                        expected_status)
758
759  # alpha and F are now in state "deleted", next we add a new ones
760  svntest.main.file_append(alpha_path, "new alpha")
761  svntest.actions.run_and_verify_svn(None, [], 'add', alpha_path)
762
763  svntest.actions.run_and_verify_svn(None, [], 'mkdir', F_path)
764
765  # New alpha and F should be in add state A
766  expected_status.add({
767    'A/B/E/alpha' : Item(status='A ', wc_rev=0),
768    'A/B/F'       : Item(status='A ', wc_rev=0),
769    })
770
771  svntest.actions.run_and_verify_status(wc_dir, expected_status)
772
773  # Forced removal of new alpha and F must restore "deleted" state
774
775  svntest.actions.run_and_verify_svn(None, [], 'rm', '--force',
776                                     alpha_path, F_path)
777  if os.path.exists(alpha_path) or os.path.exists(F_path):
778    raise svntest.Failure
779
780  # "deleted" state is not visible in status
781  expected_status.remove('A/B/E/alpha', 'A/B/F')
782
783  svntest.actions.run_and_verify_status(wc_dir, expected_status)
784
785  # Although parent dir is already at rev 1, the "deleted" state will cause
786  # alpha and F to be restored in the WC when updated to rev 1
787  svntest.actions.run_and_verify_svn(None, [], 'up', '-r', '1', wc_dir)
788
789  expected_status.add({
790    'A/B/E/alpha' : Item(status='  ', wc_rev=1),
791    'A/B/F'       : Item(status='  ', wc_rev=1),
792    })
793
794  svntest.actions.run_and_verify_status(wc_dir, expected_status)
795
796#----------------------------------------------------------------------
797
798# Issue 1591.  Updating a working copy which contains local
799# obstructions marks a directory as incomplete.  Removal of the
800# obstruction and subsequent update should clear the "incomplete"
801# flag.
802
803def obstructed_update_alters_wc_props(sbox):
804  "obstructed update alters WC properties"
805
806  sbox.build()
807  wc_dir = sbox.wc_dir
808
809  # Create a new dir in the repo in prep for creating an obstruction.
810  #print "Adding dir to repo"
811  svntest.actions.run_and_verify_svn(None, [],
812                                     'mkdir', '-m',
813                                     'prep for obstruction',
814                                     sbox.repo_url + '/A/foo')
815
816  # Create an obstruction, a file in the WC with the same name as
817  # present in a newer rev of the repo.
818  #print "Creating obstruction"
819  obstruction_parent_path = sbox.ospath('A')
820  obstruction_path = os.path.join(obstruction_parent_path, 'foo')
821  svntest.main.file_append(obstruction_path, 'an obstruction')
822
823  # Update the WC to that newer rev to trigger the obstruction.
824  #print "Updating WC"
825  # svntest.factory.make(sbox, 'svn update')
826  # exit(0)
827  expected_output = svntest.wc.State(wc_dir, {
828    'A/foo'             : Item(status='  ', treeconflict='C'),
829  })
830
831  expected_disk = svntest.main.greek_state.copy()
832  expected_disk.add({
833    'A/foo'             : Item(contents="an obstruction"),
834  })
835
836  expected_status = actions.get_virginal_state(wc_dir, 2)
837  expected_status.add({
838    'A/foo'             : Item(status='D ', treeconflict='C', wc_rev=2),
839  })
840
841  actions.run_and_verify_update(wc_dir, expected_output, expected_disk,
842                                expected_status)
843
844
845  # Remove the file which caused the obstruction.
846  #print "Removing obstruction"
847  os.unlink(obstruction_path)
848
849  svntest.main.run_svn(None, 'revert', obstruction_path)
850
851  # Update the -- now unobstructed -- WC again.
852  #print "Updating WC again"
853  expected_output = svntest.wc.State(wc_dir, {
854    })
855
856  expected_disk = svntest.main.greek_state.copy()
857  expected_disk.add({
858    'A/foo' : Item(),
859    })
860
861  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
862  expected_status.add({
863    'A/foo' : Item(status='  ', wc_rev=2),
864    })
865
866  svntest.actions.run_and_verify_update(wc_dir,
867                                        expected_output,
868                                        expected_disk,
869                                        expected_status)
870
871  # The previously obstructed resource should now be in the WC.
872  if not os.path.isdir(obstruction_path):
873    raise svntest.Failure
874
875#----------------------------------------------------------------------
876
877# Issue 938.
878def update_replace_dir(sbox):
879  "update that replaces a directory"
880
881  sbox.build()
882  wc_dir = sbox.wc_dir
883
884  # Delete a directory
885  F_path = sbox.ospath('A/B/F')
886  svntest.actions.run_and_verify_svn(None, [], 'rm', F_path)
887
888  # Commit deletion
889  expected_output = svntest.wc.State(wc_dir, {
890    'A/B/F'       : Item(verb='Deleting'),
891    })
892
893  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
894  expected_status.remove('A/B/F')
895
896  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
897                                        expected_status)
898
899  # Add replacement directory
900  svntest.actions.run_and_verify_svn(None, [], 'mkdir', F_path)
901
902  # Commit addition
903  expected_output = svntest.wc.State(wc_dir, {
904    'A/B/F'       : Item(verb='Adding'),
905    })
906
907  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
908  expected_status.tweak('A/B/F', wc_rev=3)
909
910  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
911                                        expected_status)
912
913  # Update to HEAD
914  expected_output = svntest.wc.State(wc_dir, {
915    })
916
917  expected_disk = svntest.main.greek_state.copy()
918
919  expected_status = svntest.actions.get_virginal_state(wc_dir, 3)
920
921  svntest.actions.run_and_verify_update(wc_dir,
922                                        expected_output,
923                                        expected_disk,
924                                        expected_status)
925
926  # Update to revision 1 replaces the directory
927  expected_output = svntest.wc.State(wc_dir, {
928    'A/B/F' : Item(status='A ', prev_status='D '),
929  })
930  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
931  svntest.actions.run_and_verify_update(wc_dir,
932                                        expected_output,
933                                        expected_disk,
934                                        expected_status,
935                                        [], False,
936                                        '-r', '1', wc_dir)
937
938  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
939
940  svntest.actions.run_and_verify_status(wc_dir, expected_status)
941
942#----------------------------------------------------------------------
943
944def update_single_file(sbox):
945  "update with explicit file target"
946
947  sbox.build()
948  wc_dir = sbox.wc_dir
949
950  expected_disk = svntest.main.greek_state.copy()
951
952  # Make a local mod to a file which will be committed
953  mu_path = sbox.ospath('A/mu')
954  svntest.main.file_append(mu_path, '\nAppended text for mu')
955
956  # Commit.
957  expected_output = svntest.wc.State(wc_dir, {
958    'A/mu' : Item(verb='Sending'),
959    })
960
961  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
962  expected_status.tweak('A/mu', wc_rev=2)
963
964  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
965                                        expected_status)
966
967  # At one stage 'svn up file' failed with a parent lock error
968  was_cwd = os.getcwd()
969  os.chdir(sbox.ospath('A'))
970
971  ### Can't get run_and_verify_update to work having done the chdir.
972  svntest.actions.run_and_verify_svn(None, [],
973                                     'up', '-r', '1', 'mu')
974  os.chdir(was_cwd)
975
976  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
977
978  svntest.actions.run_and_verify_status(wc_dir, expected_status)
979
980#----------------------------------------------------------------------
981def prop_update_on_scheduled_delete(sbox):
982  "receive prop update to file scheduled for deletion"
983
984  sbox.build()
985  wc_dir = sbox.wc_dir
986
987  other_wc = sbox.add_wc_path('other')
988
989  # Make the "other" working copy.
990  svntest.actions.duplicate_dir(wc_dir, other_wc)
991
992  iota_path = sbox.ospath('iota')
993  other_iota_path = os.path.join(other_wc, 'iota')
994
995  svntest.main.run_svn(None, 'propset', 'foo', 'bar', iota_path)
996
997  # Created expected output tree for 'svn ci'
998  expected_output = svntest.wc.State(wc_dir, {
999    'iota' : Item(verb='Sending'),
1000    })
1001
1002  # Create expected status tree
1003  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1004  expected_status.tweak('iota', wc_rev=2)
1005
1006  # Commit the change, creating revision 2.
1007  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
1008                                        expected_status)
1009
1010  svntest.main.run_svn(None, 'rm', other_iota_path)
1011
1012  # Expected output tree for update of other_wc.
1013  expected_output = svntest.wc.State(other_wc, {
1014    'iota' : Item(status='  ', treeconflict='C'),
1015    })
1016
1017  # Expected disk tree for the update.
1018  expected_disk = svntest.main.greek_state.copy()
1019  expected_disk.remove('iota')
1020
1021  # Expected status tree for the update.
1022  expected_status = svntest.actions.get_virginal_state(other_wc, 2)
1023  expected_status.tweak('iota', status='D ', treeconflict='C')
1024
1025  # Do the update and check the results in three ways.
1026  svntest.actions.run_and_verify_update(other_wc,
1027                                        expected_output,
1028                                        expected_disk,
1029                                        expected_status)
1030
1031#----------------------------------------------------------------------
1032
1033def update_receive_illegal_name(sbox):
1034  "bail when receive a file or dir named .svn"
1035
1036  sbox.build()
1037  wc_dir = sbox.wc_dir
1038
1039  # This tests the revision 4334 fix for issue #1068.
1040
1041  legal_url = sbox.repo_url + '/A/D/G/svn'
1042  illegal_url = (sbox.repo_url
1043                 + '/A/D/G/' + svntest.main.get_admin_name())
1044  # Ha!  The client doesn't allow us to mkdir a '.svn' but it does
1045  # allow us to copy to a '.svn' so ...
1046  svntest.actions.run_and_verify_svn(None, [],
1047                                     'mkdir', '-m', 'log msg',
1048                                     legal_url)
1049  svntest.actions.run_and_verify_svn(None, [],
1050                                     'mv', '-m', 'log msg',
1051                                     legal_url, illegal_url)
1052
1053  # Do the update twice, both should fail.  After the first failure
1054  # the wc will be marked "incomplete".
1055  for n in range(2):
1056    exit_code, out, err = svntest.main.run_svn(1, 'up', wc_dir)
1057    for line in err:
1058      if line.find("of the same name") != -1:
1059        break
1060    else:
1061      raise svntest.Failure
1062
1063  # At one stage an obstructed update in an incomplete wc would leave
1064  # a txn behind
1065  exit_code, out, err = svntest.main.run_svnadmin('lstxns', sbox.repo_dir)
1066  if out or err:
1067    raise svntest.Failure
1068
1069#----------------------------------------------------------------------
1070
1071def update_deleted_missing_dir(sbox):
1072  "update missing dir to rev in which it is absent"
1073
1074  sbox.build()
1075  wc_dir = sbox.wc_dir
1076
1077  E_path = sbox.ospath('A/B/E')
1078  H_path = sbox.ospath('A/D/H')
1079
1080  # Create a new revision with directories deleted
1081  svntest.main.run_svn(None, 'rm', E_path)
1082  svntest.main.run_svn(None, 'rm', H_path)
1083  svntest.main.run_svn(None,
1084                       'ci', '-m', 'log msg', E_path, H_path)
1085
1086  # Update back to the old revision
1087  svntest.main.run_svn(None,
1088                       'up', '-r', '1', wc_dir)
1089
1090  # Delete the directories from disk
1091  svntest.main.safe_rmtree(E_path)
1092  svntest.main.safe_rmtree(H_path)
1093
1094  # Create expected output tree for an update of the missing items by name
1095  expected_output = svntest.wc.State(wc_dir, {
1096    'A/D/H/psi'         : Item(verb='Restored'),
1097    'A/D/H/omega'       : Item(verb='Restored'),
1098    'A/D/H/chi'         : Item(verb='Restored'),
1099    'A/B/E/beta'        : Item(verb='Restored'),
1100    'A/B/E/alpha'       : Item(verb='Restored'),
1101    # A/B/E and A/D/H are also restored, but are then overriden by the delete
1102    'A/B/E'             : Item(status='D ', prev_verb='Restored'),
1103    'A/D/H'             : Item(status='D ', prev_verb='Restored'),
1104  })
1105
1106  # Create expected disk tree for the update.
1107  expected_disk = svntest.main.greek_state.copy()
1108  expected_disk.remove('A/B/E', 'A/B/E/alpha', 'A/B/E/beta')
1109  expected_disk.remove('A/D/H', 'A/D/H/chi', 'A/D/H/omega', 'A/D/H/psi')
1110
1111  # Create expected status tree for the update.
1112  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1113  expected_status.remove('A/B/E', 'A/B/E/alpha', 'A/B/E/beta')
1114  expected_status.remove('A/D/H', 'A/D/H/chi', 'A/D/H/omega', 'A/D/H/psi')
1115
1116  # Do the update, specifying the deleted paths explicitly.
1117  svntest.actions.run_and_verify_update(wc_dir,
1118                                        expected_output,
1119                                        expected_disk,
1120                                        expected_status,
1121                                        [], False,
1122                                        "-r", "2", E_path, H_path)
1123
1124  # Update back to the old revision again
1125  svntest.main.run_svn(None,
1126                       'up', '-r', '1', wc_dir)
1127
1128  # This time we're updating the whole working copy
1129  expected_status.tweak(wc_rev=2)
1130
1131  # And now we don't expect restore operations
1132  expected_output = svntest.wc.State(wc_dir, {
1133    'A/B/E' : Item(status='D '),
1134    'A/D/H' : Item(status='D '),
1135    })
1136
1137  # Do the update, on the whole working copy this time
1138  svntest.actions.run_and_verify_update(wc_dir,
1139                                        expected_output,
1140                                        expected_disk,
1141                                        expected_status,
1142                                        [], False,
1143                                        "-r", "2", wc_dir)
1144
1145#----------------------------------------------------------------------
1146
1147# Issue 919.  This test was written as a regression test for "item
1148# should remain 'deleted' when an update deletes a sibling".
1149def another_hudson_problem(sbox):
1150  "another \"hudson\" problem: updates that delete"
1151
1152  sbox.build()
1153  wc_dir = sbox.wc_dir
1154
1155  # Delete/commit gamma thus making it 'deleted'
1156  gamma_path = sbox.ospath('A/D/gamma')
1157  svntest.main.run_svn(None, 'rm', gamma_path)
1158
1159  expected_output = svntest.wc.State(wc_dir, {
1160    'A/D/gamma' : Item(verb='Deleting'),
1161    })
1162
1163  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1164  expected_status.remove('A/D/gamma')
1165
1166  svntest.actions.run_and_verify_commit(wc_dir,
1167                                        expected_output,
1168                                        expected_status)
1169
1170  # Delete directory G from the repository
1171  svntest.actions.run_and_verify_svn(['Committing transaction...\n',
1172                                      'Committed revision 3.\n'], [],
1173                                     'rm', '-m', 'log msg',
1174                                     sbox.repo_url + '/A/D/G')
1175
1176  # Remove corresponding tree from working copy
1177  G_path = sbox.ospath('A/D/G')
1178  svntest.main.safe_rmtree(G_path)
1179
1180  # Update missing directory to receive the delete, this should mark G
1181  # as 'deleted' and should not alter gamma's entry.
1182
1183  expected_output = ["Updating '%s':\n" % (G_path),
1184                     'Restored \'' + G_path + '\'\n',
1185                     'Restored \'' + G_path + os.path.sep + 'pi\'\n',
1186                     'Restored \'' + G_path + os.path.sep + 'rho\'\n',
1187                     'Restored \'' + G_path + os.path.sep + 'tau\'\n',
1188                     'D    '+G_path+'\n',
1189                     'Updated to revision 3.\n',
1190                    ]
1191
1192  # Sigh, I can't get run_and_verify_update to work (but not because
1193  # of issue 919 as far as I can tell)
1194  expected_output = svntest.verify.UnorderedOutput(expected_output)
1195  svntest.actions.run_and_verify_svn(expected_output, [],
1196                                     'up', G_path)
1197
1198  # Both G and gamma should be 'deleted', update should produce no output
1199  expected_status = svntest.actions.get_virginal_state(wc_dir, 3)
1200  expected_status.remove('A/D/G', 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau',
1201                         'A/D/gamma')
1202
1203  expected_disk = svntest.main.greek_state.copy()
1204  expected_disk.remove('A/D/G', 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau',
1205                       'A/D/gamma')
1206
1207  svntest.actions.run_and_verify_update(wc_dir,
1208                                        "",
1209                                        expected_disk,
1210                                        expected_status)
1211
1212#----------------------------------------------------------------------
1213def update_deleted_targets(sbox):
1214  "explicit update of deleted=true targets"
1215
1216  sbox.build()
1217  wc_dir = sbox.wc_dir
1218
1219  # Delete/commit thus creating 'deleted=true' entries
1220  gamma_path = sbox.ospath('A/D/gamma')
1221  F_path = sbox.ospath('A/B/F')
1222  svntest.main.run_svn(None, 'rm', gamma_path, F_path)
1223
1224  expected_output = svntest.wc.State(wc_dir, {
1225    'A/D/gamma' : Item(verb='Deleting'),
1226    'A/B/F'     : Item(verb='Deleting'),
1227    })
1228
1229  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1230  expected_status.remove('A/D/gamma', 'A/B/F')
1231
1232  svntest.actions.run_and_verify_commit(wc_dir,
1233                                        expected_output,
1234                                        expected_status)
1235
1236  # Explicit update must not remove the 'deleted=true' entries
1237  svntest.actions.run_and_verify_svn(exp_noop_up_out(2), [],
1238                                     'update', gamma_path)
1239  svntest.actions.run_and_verify_svn(exp_noop_up_out(2), [],
1240                                     'update', F_path)
1241
1242  # Update to r1 to restore items, since the parent directory is already
1243  # at r1 this fails if the 'deleted=true' entries are missing (issue 2250)
1244  expected_output = svntest.wc.State(wc_dir, {
1245    'A/D/gamma' : Item(status='A '),
1246    'A/B/F'     : Item(status='A '),
1247    })
1248
1249  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1250
1251  expected_disk = svntest.main.greek_state.copy()
1252
1253  svntest.actions.run_and_verify_update(wc_dir,
1254                                        expected_output,
1255                                        expected_disk,
1256                                        expected_status,
1257                                        [], False,
1258                                        '-r', '1', wc_dir)
1259
1260
1261
1262#----------------------------------------------------------------------
1263
1264def new_dir_with_spaces(sbox):
1265  "receive new dir with spaces in its name"
1266
1267  sbox.build()
1268  wc_dir = sbox.wc_dir
1269
1270  # Create a new directory ("spacey dir") directly in repository
1271  svntest.actions.run_and_verify_svn(['Committing transaction...\n',
1272                                      'Committed revision 2.\n'], [],
1273                                     'mkdir', '-m', 'log msg',
1274                                     sbox.repo_url
1275                                     + '/A/spacey%20dir')
1276
1277  # Update, and make sure ra_neon doesn't choke on the space.
1278  expected_output = svntest.wc.State(wc_dir, {
1279    'A/spacey dir'       : Item(status='A '),
1280    })
1281
1282  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
1283  expected_status.add({
1284    'A/spacey dir'       : Item(status='  ', wc_rev=2),
1285    })
1286
1287  expected_disk = svntest.main.greek_state.copy()
1288  expected_disk.add({
1289    'A/spacey dir' : Item(),
1290    })
1291
1292  svntest.actions.run_and_verify_update(wc_dir,
1293                                        expected_output,
1294                                        expected_disk,
1295                                        expected_status)
1296
1297#----------------------------------------------------------------------
1298
1299def non_recursive_update(sbox):
1300  "non-recursive update"
1301
1302  sbox.build()
1303  wc_dir = sbox.wc_dir
1304
1305  # Commit a change to A/mu and A/D/G/rho
1306  mu_path = sbox.ospath('A/mu')
1307  rho_path = sbox.ospath('A/D/G/rho')
1308
1309  svntest.main.file_append(mu_path, "new")
1310  svntest.main.file_append(rho_path, "new")
1311
1312  expected_output = svntest.wc.State(wc_dir, {
1313    'A/mu' : Item(verb='Sending'),
1314    'A/D/G/rho' : Item(verb='Sending'),
1315    })
1316
1317  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1318  expected_status.tweak('A/mu', 'A/D/G/rho', wc_rev=2)
1319
1320  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
1321                                        expected_status)
1322
1323  # Update back to revision 1
1324  expected_output = svntest.wc.State(wc_dir, {
1325    'A/mu' : Item(status='U '),
1326    'A/D/G/rho' : Item(status='U '),
1327    })
1328
1329  expected_disk = svntest.main.greek_state.copy()
1330
1331  expected_status.tweak('A/mu', 'A/D/G/rho', wc_rev=1)
1332
1333  svntest.actions.run_and_verify_update(wc_dir, expected_output,
1334                                        expected_disk, expected_status,
1335                                        [], False,
1336                                        '-r', '1', wc_dir)
1337
1338  # Non-recursive update of A should change A/mu but not A/D/G/rho
1339  A_path = sbox.ospath('A')
1340
1341  expected_output = svntest.wc.State(wc_dir, {
1342    'A/mu' : Item(status='U '),
1343    })
1344
1345  expected_status.tweak('A', 'A/mu', wc_rev=2)
1346
1347  expected_disk.tweak('A/mu', contents="This is the file 'mu'.\nnew")
1348
1349  svntest.actions.run_and_verify_update(wc_dir, expected_output,
1350                                        expected_disk, expected_status,
1351                                        [], False,
1352                                        '-N', A_path)
1353
1354#----------------------------------------------------------------------
1355
1356def checkout_empty_dir(sbox):
1357  "check out an empty dir"
1358  # See issue #1472 -- checked out empty dir should not be marked as
1359  # incomplete ("!" in status).
1360  sbox.build(create_wc = False)
1361  wc_dir = sbox.wc_dir
1362
1363  C_url = sbox.repo_url + '/A/C'
1364
1365  svntest.main.safe_rmtree(wc_dir)
1366  svntest.actions.run_and_verify_svn(None, [], 'checkout', C_url, wc_dir)
1367
1368  svntest.actions.run_and_verify_svn([], [], 'status', wc_dir)
1369
1370
1371#----------------------------------------------------------------------
1372# Regression test for issue #919: "another ghudson bug".  Basically, if
1373# we fore- or back-date an item until it no longer exists, we were
1374# completely removing the entry, rather than marking it 'deleted'
1375# (which we now do.)
1376
1377def update_to_deletion(sbox):
1378  "update target till it's gone, then get it back"
1379
1380  sbox.build()
1381  wc_dir = sbox.wc_dir
1382
1383  iota_path = sbox.ospath('iota')
1384
1385  # Update iota to rev 0, so it gets removed.
1386  expected_output = svntest.wc.State(wc_dir, {
1387    'iota' : Item(status='D '),
1388    })
1389
1390  expected_disk = svntest.main.greek_state.copy()
1391  expected_disk.remove('iota')
1392
1393  svntest.actions.run_and_verify_update(wc_dir,
1394                                        expected_output,
1395                                        expected_disk,
1396                                        None,
1397                                        [], False,
1398                                        '-r', '0', iota_path)
1399
1400  # Update the wc root, so iota comes back.
1401  expected_output = svntest.wc.State(wc_dir, {
1402    'iota' : Item(status='A '),
1403    })
1404
1405  expected_disk = svntest.main.greek_state.copy()
1406
1407  svntest.actions.run_and_verify_update(wc_dir,
1408                                        expected_output,
1409                                        expected_disk,
1410                                        None)
1411
1412
1413#----------------------------------------------------------------------
1414
1415def update_deletion_inside_out(sbox):
1416  "update child before parent of a deleted tree"
1417
1418  sbox.build()
1419  wc_dir = sbox.wc_dir
1420
1421  parent_path = sbox.ospath('A/B')
1422  child_path = os.path.join(parent_path, 'E')  # Could be a file, doesn't matter
1423
1424  # Delete the parent directory.
1425  svntest.actions.run_and_verify_svn(None, [],
1426                                     'rm', parent_path)
1427  svntest.actions.run_and_verify_svn(None, [],
1428                                     'ci', '-m', '', wc_dir)
1429
1430  # Update back to r1.
1431  svntest.actions.run_and_verify_svn(None, [],
1432                                     'update', '-r', '1', wc_dir)
1433
1434  # Update just the child to r2.
1435  svntest.actions.run_and_verify_svn(None, [],
1436                                     'update', '-r', '2', child_path)
1437
1438  # Now try a normal update.
1439  expected_output = svntest.wc.State(wc_dir, {
1440    'A/B' : Item(status='D '),
1441    })
1442
1443  expected_disk = svntest.main.greek_state.copy()
1444  expected_disk.remove('A/B', 'A/B/lambda', 'A/B/F',
1445                       'A/B/E', 'A/B/E/alpha', 'A/B/E/beta')
1446
1447  svntest.actions.run_and_verify_update(wc_dir,
1448                                        expected_output,
1449                                        expected_disk,
1450                                        None)
1451
1452
1453#----------------------------------------------------------------------
1454# Regression test for issue #1793, whereby 'svn up dir' would delete
1455# dir if schedule-add.  Yikes.
1456
1457def update_schedule_add_dir(sbox):
1458  "update a schedule-add directory"
1459
1460  sbox.build()
1461  wc_dir = sbox.wc_dir
1462
1463  # Delete directory A/D/G in the repository via immediate commit
1464  G_path = sbox.ospath('A/D/G')
1465  G_url = sbox.repo_url + '/A/D/G'
1466  svntest.actions.run_and_verify_svn(None, [],
1467                                     'rm', G_url, '-m', 'rev 2')
1468
1469  # Update the wc to HEAD (r2)
1470  expected_output = svntest.wc.State(wc_dir, {
1471    'A/D/G' : Item(status='D '),
1472    })
1473
1474  expected_disk = svntest.main.greek_state.copy()
1475  expected_disk.remove('A/D/G', 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
1476
1477  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
1478  expected_status.remove('A/D/G', 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
1479
1480  svntest.actions.run_and_verify_update(wc_dir,
1481                                        expected_output,
1482                                        expected_disk,
1483                                        expected_status)
1484
1485  # Do a URL->wc copy, creating a new schedule-add A/D/G.
1486  # (Standard procedure when trying to resurrect the directory.)
1487  D_path = sbox.ospath('A/D')
1488  svntest.actions.run_and_verify_svn(None, [],
1489                                     'cp', G_url + '@1', D_path)
1490
1491  # status should now show the dir scheduled for addition-with-history
1492  expected_status.add({
1493    'A/D/G'     : Item(status='A ', copied='+', wc_rev='-'),
1494    'A/D/G/pi'  : Item(status='  ', copied='+', wc_rev='-'),
1495    'A/D/G/rho' : Item(status='  ', copied='+', wc_rev='-'),
1496    'A/D/G/tau' : Item(status='  ', copied='+', wc_rev='-'),
1497    })
1498
1499  svntest.actions.run_and_verify_status(wc_dir, expected_status)
1500
1501  # Now update with the schedule-add dir as the target.
1502  svntest.actions.run_and_verify_svn(None, [], 'up', G_path)
1503
1504  # The update should be a no-op, and the schedule-add directory
1505  # should still exist!  'svn status' shouldn't change at all.
1506  svntest.actions.run_and_verify_status(wc_dir, expected_status)
1507
1508
1509#----------------------------------------------------------------------
1510# Test updating items that do not exist in the current WC rev, but do
1511# exist at some future revision.
1512
1513def update_to_future_add(sbox):
1514  "update target that was added in a future rev"
1515
1516  sbox.build()
1517  wc_dir = sbox.wc_dir
1518
1519  # Update the entire WC to rev 0
1520  # Create expected output tree for an update to rev 0
1521  expected_output = svntest.wc.State(wc_dir, {
1522    'iota' : Item(status='D '),
1523    'A' : Item(status='D '),
1524    })
1525
1526  # Create expected disk tree for the update to rev 0
1527  expected_disk = svntest.wc.State(wc_dir, { })
1528
1529  # Do the update and check the results.
1530  svntest.actions.run_and_verify_update(wc_dir,
1531                                        expected_output,
1532                                        expected_disk,
1533                                        None,
1534                                        [], False,
1535                                        '-r', '0', wc_dir)
1536
1537  # Update iota to the current HEAD.
1538  iota_path = sbox.ospath('iota')
1539
1540  expected_output = svntest.wc.State(wc_dir, {
1541    'iota' : Item(status='A '),
1542    })
1543
1544  expected_disk = svntest.wc.State('', {
1545   'iota' : Item("This is the file 'iota'.\n")
1546   })
1547
1548  svntest.actions.run_and_verify_update(wc_dir,
1549                                        expected_output,
1550                                        expected_disk,
1551                                        None,
1552                                        [], False,
1553                                        iota_path)
1554
1555  # Now try updating the directory into the future
1556  A_path = sbox.ospath('A')
1557
1558  expected_output = svntest.wc.State(wc_dir, {
1559    'A'              : Item(status='A '),
1560    'A/mu'           : Item(status='A '),
1561    'A/B'            : Item(status='A '),
1562    'A/B/lambda'     : Item(status='A '),
1563    'A/B/E'          : Item(status='A '),
1564    'A/B/E/alpha'    : Item(status='A '),
1565    'A/B/E/beta'     : Item(status='A '),
1566    'A/B/F'          : Item(status='A '),
1567    'A/C'            : Item(status='A '),
1568    'A/D'            : Item(status='A '),
1569    'A/D/gamma'      : Item(status='A '),
1570    'A/D/G'          : Item(status='A '),
1571    'A/D/G/pi'       : Item(status='A '),
1572    'A/D/G/rho'      : Item(status='A '),
1573    'A/D/G/tau'      : Item(status='A '),
1574    'A/D/H'          : Item(status='A '),
1575    'A/D/H/chi'      : Item(status='A '),
1576    'A/D/H/psi'      : Item(status='A '),
1577    'A/D/H/omega'    : Item(status='A ')
1578    })
1579
1580  expected_disk = svntest.main.greek_state.copy()
1581
1582  svntest.actions.run_and_verify_update(wc_dir,
1583                                        expected_output,
1584                                        expected_disk,
1585                                        None,
1586                                        [], False,
1587                                        A_path)
1588
1589#----------------------------------------------------------------------
1590
1591def update_xml_unsafe_dir(sbox):
1592  "update dir with xml-unsafe name"
1593
1594  sbox.build()
1595  wc_dir = sbox.wc_dir
1596
1597  # Make a backup copy of the working copy
1598  wc_backup = sbox.add_wc_path('backup')
1599  svntest.actions.duplicate_dir(wc_dir, wc_backup)
1600
1601  # Make a couple of local mods to files
1602  test_path = sbox.ospath(' foo & bar')
1603  svntest.main.run_svn(None, 'mkdir', test_path)
1604
1605  # Created expected output tree for 'svn ci'
1606  expected_output = wc.State(wc_dir, {
1607    ' foo & bar' : Item(verb='Adding'),
1608    })
1609
1610  # Create expected status tree; all local revisions should be at 1,
1611  # but 'foo & bar' should be at revision 2.
1612  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1613  expected_status.add({
1614    ' foo & bar' : Item(status='  ', wc_rev=2),
1615    })
1616
1617  # Commit.
1618  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
1619                                        expected_status)
1620
1621  # chdir into the funky path, and update from there.
1622  os.chdir(test_path)
1623
1624  expected_output = wc.State('', {
1625    })
1626
1627  expected_disk = wc.State('', {
1628    })
1629
1630  expected_status = wc.State('', {
1631    '' : Item(status='  ', wc_rev=2),
1632    })
1633
1634  svntest.actions.run_and_verify_update('', expected_output, expected_disk,
1635                                        expected_status)
1636
1637#----------------------------------------------------------------------
1638# eol-style handling during update with conflicts, scenario 1:
1639# when update creates a conflict on a file, make sure the file and files
1640# r<left>, r<right> and .mine are in the eol-style defined for that file.
1641#
1642# This test for 'svn merge' can be found in merge_tests.py as
1643# merge_conflict_markers_matching_eol.
1644def conflict_markers_matching_eol(sbox):
1645  "conflict markers should match the file's eol style"
1646
1647  sbox.build()
1648  wc_dir = sbox.wc_dir
1649  filecount = 1
1650
1651  mu_path = sbox.ospath('A/mu')
1652
1653  if os.name == 'nt':
1654    native_nl = '\r\n'
1655  else:
1656    native_nl = '\n'
1657  crlf = '\r\n'
1658
1659  # Checkout a second working copy
1660  wc_backup = sbox.add_wc_path('backup')
1661  svntest.actions.run_and_verify_svn(None, [], 'checkout',
1662                                     sbox.repo_url, wc_backup)
1663
1664  # set starting revision
1665  cur_rev = 1
1666
1667  expected_disk = svntest.main.greek_state.copy()
1668  expected_status = svntest.actions.get_virginal_state(wc_dir, cur_rev)
1669  expected_backup_status = svntest.actions.get_virginal_state(wc_backup,
1670                                                              cur_rev)
1671
1672  path_backup = os.path.join(wc_backup, 'A', 'mu')
1673
1674  # do the test for each eol-style
1675  for eol, eolchar in zip(['CRLF', 'CR', 'native', 'LF'],
1676                          [crlf, '\015', native_nl, '\012']):
1677    # rewrite file mu and set the eol-style property.
1678    svntest.main.file_write(mu_path, "This is the file 'mu'."+ eolchar, 'wb')
1679    svntest.main.run_svn(None, 'propset', 'svn:eol-style', eol, mu_path)
1680
1681    expected_disk.add({
1682      'A/mu' : Item("This is the file 'mu'." + eolchar)
1683    })
1684
1685    expected_output = svntest.wc.State(wc_dir, {
1686      'A/mu' : Item(verb='Sending'),
1687    })
1688
1689    expected_status.tweak(wc_rev = cur_rev)
1690    expected_status.add({
1691      'A/mu' : Item(status='  ', wc_rev = cur_rev + 1),
1692    })
1693
1694    # Commit the original change and note the 'base' revision number
1695    svntest.actions.run_and_verify_commit(wc_dir, expected_output,
1696                                          expected_status)
1697    cur_rev = cur_rev + 1
1698    base_rev = cur_rev
1699
1700    svntest.main.run_svn(None, 'update', wc_backup)
1701
1702    # Make a local mod to mu
1703    svntest.main.file_append_binary(mu_path,
1704                                    'Original appended text for mu' + eolchar)
1705
1706    # Commit the original change and note the 'theirs' revision number
1707    svntest.main.run_svn(None, 'commit', '-m', 'test log', wc_dir)
1708    cur_rev = cur_rev + 1
1709    theirs_rev = cur_rev
1710
1711    # Make a local mod to mu, will conflict with the previous change
1712    svntest.main.file_append_binary(path_backup,
1713                                    'Conflicting appended text for mu'
1714                                    + eolchar)
1715
1716    # Create expected output tree for an update of the wc_backup.
1717    expected_backup_output = svntest.wc.State(wc_backup, {
1718      'A/mu' : Item(status='C '),
1719      })
1720
1721    # Create expected disk tree for the update.
1722    expected_backup_disk = expected_disk.copy()
1723
1724    # verify content of resulting conflicted file
1725    expected_backup_disk.add({
1726    'A/mu' : Item(contents= "This is the file 'mu'." + eolchar +
1727      "<<<<<<< .mine" + eolchar +
1728      "Conflicting appended text for mu" + eolchar +
1729      "||||||| .r" + str(cur_rev - 1) + eolchar +
1730      "=======" + eolchar +
1731      "Original appended text for mu" + eolchar +
1732      ">>>>>>> .r" + str(cur_rev) + eolchar),
1733    })
1734    # verify content of base(left) file
1735    expected_backup_disk.add({
1736    'A/mu.r' + str(base_rev ) : Item(contents= "This is the file 'mu'." +
1737      eolchar)
1738    })
1739    # verify content of theirs(right) file
1740    expected_backup_disk.add({
1741    'A/mu.r' + str(theirs_rev ) : Item(contents= "This is the file 'mu'." +
1742      eolchar +
1743      "Original appended text for mu" + eolchar)
1744    })
1745    # verify content of mine file
1746    expected_backup_disk.add({
1747    'A/mu.mine' : Item(contents= "This is the file 'mu'." +
1748      eolchar +
1749      "Conflicting appended text for mu" + eolchar)
1750    })
1751
1752    # Create expected status tree for the update.
1753    expected_backup_status.add({
1754      'A/mu'   : Item(status='  ', wc_rev=cur_rev),
1755    })
1756    expected_backup_status.tweak('A/mu', status='C ')
1757    expected_backup_status.tweak(wc_rev = cur_rev)
1758
1759    # Do the update and check the results in three ways.
1760    svntest.actions.run_and_verify_update2(wc_backup,
1761                                           expected_backup_output,
1762                                           expected_backup_disk,
1763                                           expected_backup_status,
1764                                           keep_eol_style=True)
1765
1766    # cleanup for next run
1767    svntest.main.run_svn(None, 'revert', '-R', wc_backup)
1768    svntest.main.run_svn(None, 'update', wc_dir)
1769
1770# eol-style handling during update, scenario 2:
1771# if part of that update is a propchange (add, change, delete) of
1772# svn:eol-style, make sure the correct eol-style is applied before
1773# calculating the merge (and conflicts if any)
1774#
1775# This test for 'svn merge' can be found in merge_tests.py as
1776# merge_eolstyle_handling.
1777def update_eolstyle_handling(sbox):
1778  "handle eol-style propchange during update"
1779
1780  sbox.build()
1781  wc_dir = sbox.wc_dir
1782
1783  mu_path = sbox.ospath('A/mu')
1784
1785  crlf = '\r\n'
1786
1787  # Checkout a second working copy
1788  wc_backup = sbox.add_wc_path('backup')
1789  svntest.actions.run_and_verify_svn(None, [], 'checkout',
1790                                     sbox.repo_url, wc_backup)
1791  path_backup = os.path.join(wc_backup, 'A', 'mu')
1792
1793  # Test 1: add the eol-style property and commit, change mu in the second
1794  # working copy and update; there should be no conflict!
1795  svntest.main.run_svn(None, 'propset', 'svn:eol-style', "CRLF", mu_path)
1796  svntest.main.run_svn(None,
1797                       'commit', '-m', 'set eol-style property', wc_dir)
1798
1799  svntest.main.file_append_binary(path_backup, 'Added new line of text.\012')
1800
1801  expected_backup_disk = svntest.main.greek_state.copy()
1802  expected_backup_disk.tweak(
1803  'A/mu', contents= "This is the file 'mu'." + crlf +
1804    "Added new line of text." + crlf)
1805
1806  expected_backup_output = svntest.wc.State(wc_backup, {
1807    'A/mu' : Item(status='GU'),
1808    })
1809
1810  expected_backup_status = svntest.actions.get_virginal_state(wc_backup, 2)
1811  expected_backup_status.tweak('A/mu', status='M ')
1812
1813  svntest.actions.run_and_verify_update2(wc_backup,
1814                                         expected_backup_output,
1815                                         expected_backup_disk,
1816                                         expected_backup_status,
1817                                         keep_eol_style=True)
1818
1819  # Test 2: now change the eol-style property to another value and commit,
1820  # update the still changed mu in the second working copy; there should be
1821  # no conflict!
1822  svntest.main.run_svn(None, 'propset', 'svn:eol-style', "CR", mu_path)
1823  svntest.main.run_svn(None,
1824                       'commit', '-m', 'set eol-style property', wc_dir)
1825
1826  expected_backup_disk = svntest.main.greek_state.copy()
1827  expected_backup_disk.add({
1828  'A/mu' : Item(contents= "This is the file 'mu'.\015" +
1829    "Added new line of text.\015")
1830  })
1831
1832  expected_backup_output = svntest.wc.State(wc_backup, {
1833    'A/mu' : Item(status='GU'),
1834    })
1835
1836  expected_backup_status = svntest.actions.get_virginal_state(wc_backup, 3)
1837  expected_backup_status.tweak('A/mu', status='M ')
1838
1839  svntest.actions.run_and_verify_update2(wc_backup,
1840                                         expected_backup_output,
1841                                         expected_backup_disk,
1842                                         expected_backup_status,
1843                                         keep_eol_style=True)
1844
1845  # Test 3: now delete the eol-style property and commit, update the still
1846  # changed mu in the second working copy; there should be no conflict!
1847  # EOL of mu should be unchanged (=CR).
1848  svntest.main.run_svn(None, 'propdel', 'svn:eol-style', mu_path)
1849  svntest.main.run_svn(None,
1850                       'commit', '-m', 'del eol-style property', wc_dir)
1851
1852  expected_backup_disk = svntest.main.greek_state.copy()
1853  expected_backup_disk.add({
1854  'A/mu' : Item(contents= "This is the file 'mu'.\015" +
1855    "Added new line of text.\015")
1856  })
1857
1858  expected_backup_output = svntest.wc.State(wc_backup, {
1859    'A/mu' : Item(status=' U'),
1860    })
1861
1862  expected_backup_status = svntest.actions.get_virginal_state(wc_backup, 4)
1863  expected_backup_status.tweak('A/mu', status='M ')
1864  svntest.actions.run_and_verify_update2(wc_backup,
1865                                         expected_backup_output,
1866                                         expected_backup_disk,
1867                                         expected_backup_status,
1868                                         keep_eol_style=True)
1869
1870# Bug in which "update" put a bogus revision number on a schedule-add file,
1871# causing the wrong version of it to be committed.
1872def update_copy_of_old_rev(sbox):
1873  "update schedule-add copy of old rev"
1874
1875  sbox.build()
1876  wc_dir = sbox.wc_dir
1877
1878  dir = sbox.ospath('A')
1879  dir2 = sbox.ospath('A2')
1880  file = os.path.join(dir, 'mu')
1881  file2 = os.path.join(dir2, 'mu')
1882  url = sbox.repo_url + '/A/mu'
1883  url2 = sbox.repo_url + '/A2/mu'
1884
1885  # Remember the original text of the file
1886  exit_code, text_r1, err = svntest.actions.run_and_verify_svn(None, [],
1887                                                               'cat', '-r1',
1888                                                               url)
1889
1890  # Commit a different version of the file
1891  svntest.main.file_write(file, "Second revision of 'mu'\n")
1892  svntest.actions.run_and_verify_svn(None, [],
1893                                     'ci', '-m', '', wc_dir)
1894
1895  # Copy an old revision of its directory into a new path in the WC
1896  svntest.actions.run_and_verify_svn(None, [],
1897                                     'cp', '-r1', dir, dir2)
1898
1899  # Update.  (Should do nothing, but added a bogus "revision" in "entries".)
1900  svntest.actions.run_and_verify_svn(None, [],
1901                                     'up', wc_dir)
1902
1903  # Commit, and check that it says it's committing the right thing
1904  exp_out = ['Adding         ' + dir2 + '\n',
1905             'Committing transaction...\n',
1906             'Committed revision 3.\n']
1907  svntest.actions.run_and_verify_svn(exp_out, [],
1908                                     'ci', '-m', '', wc_dir)
1909
1910  # Verify the committed file's content
1911  svntest.actions.run_and_verify_svn(text_r1, [],
1912                                     'cat', url2)
1913
1914#----------------------------------------------------------------------
1915def forced_update(sbox):
1916  "forced update tolerates obstructions to adds"
1917
1918  sbox.build()
1919  wc_dir = sbox.wc_dir
1920
1921  # Make a backup copy of the working copy
1922  wc_backup = sbox.add_wc_path('backup')
1923  svntest.actions.duplicate_dir(wc_dir, wc_backup)
1924
1925  # Make a couple of local mods to files
1926  mu_path = sbox.ospath('A/mu')
1927  rho_path = sbox.ospath('A/D/G/rho')
1928  svntest.main.file_append(mu_path, 'appended mu text')
1929  svntest.main.file_append(rho_path, 'new appended text for rho')
1930
1931  # Add some files
1932  nu_path = sbox.ospath('A/B/F/nu')
1933  svntest.main.file_append(nu_path, "This is the file 'nu'\n")
1934  svntest.main.run_svn(None, 'add', nu_path)
1935  kappa_path = sbox.ospath('kappa')
1936  svntest.main.file_append(kappa_path, "This is the file 'kappa'\n")
1937  svntest.main.run_svn(None, 'add', kappa_path)
1938
1939  # Add a dir with two files
1940  I_path = sbox.ospath('A/C/I')
1941  os.mkdir(I_path)
1942  svntest.main.run_svn(None, 'add', I_path)
1943  upsilon_path = os.path.join(I_path, 'upsilon')
1944  svntest.main.file_append(upsilon_path, "This is the file 'upsilon'\n")
1945  svntest.main.run_svn(None, 'add', upsilon_path)
1946  zeta_path = os.path.join(I_path, 'zeta')
1947  svntest.main.file_append(zeta_path, "This is the file 'zeta'\n")
1948  svntest.main.run_svn(None, 'add', zeta_path)
1949
1950  # Created expected output tree for 'svn ci'
1951  expected_output = wc.State(wc_dir, {
1952    'A/mu'          : Item(verb='Sending'),
1953    'A/D/G/rho'     : Item(verb='Sending'),
1954    'A/B/F/nu'      : Item(verb='Adding'),
1955    'kappa'         : Item(verb='Adding'),
1956    'A/C/I'         : Item(verb='Adding'),
1957    'A/C/I/upsilon' : Item(verb='Adding'),
1958    'A/C/I/zeta'    : Item(verb='Adding'),
1959    })
1960
1961  # Create expected status tree.
1962  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1963  expected_status.add({
1964    'A/B/F/nu'      : Item(status='  ', wc_rev=2),
1965    'kappa'         : Item(status='  ', wc_rev=2),
1966    'A/C/I'         : Item(status='  ', wc_rev=2),
1967    'A/C/I/upsilon' : Item(status='  ', wc_rev=2),
1968    'A/C/I/zeta'    : Item(status='  ', wc_rev=2),
1969    })
1970  expected_status.tweak('A/mu', 'A/D/G/rho', wc_rev=2)
1971
1972  # Commit.
1973  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
1974                                        expected_status)
1975
1976  # Make a local mod to mu that will merge cleanly.
1977  backup_mu_path = os.path.join(wc_backup, 'A', 'mu')
1978  svntest.main.file_append(backup_mu_path, 'appended mu text')
1979
1980  # Create unversioned files and dir that will obstruct A/B/F/nu, kappa,
1981  # A/C/I, and A/C/I/upsilon coming from repos during update.
1982  # The obstructing nu has the same contents as  the repos, while kappa and
1983  # upsilon differ, which means the latter two should show as modified after
1984  # the forced update.
1985  nu_path = os.path.join(wc_backup, 'A', 'B', 'F', 'nu')
1986  svntest.main.file_append(nu_path, "This is the file 'nu'\n")
1987  kappa_path = os.path.join(wc_backup, 'kappa')
1988  svntest.main.file_append(kappa_path,
1989                           "This is the OBSTRUCTING file 'kappa'\n")
1990  I_path = os.path.join(wc_backup, 'A', 'C', 'I')
1991  os.mkdir(I_path)
1992  upsilon_path = os.path.join(I_path, 'upsilon')
1993  svntest.main.file_append(upsilon_path,
1994                           "This is the OBSTRUCTING file 'upsilon'\n")
1995
1996  # Create expected output tree for an update of the wc_backup.
1997  # mu and rho are run of the mill update operations; merge and update
1998  # respectively.
1999  # kappa, nu, I, and upsilon all 'E'xisted as unversioned items in the WC.
2000  # While the dir I does exist, zeta does not so it's just an add.
2001  expected_output = wc.State(wc_backup, {
2002    'A/mu'          : Item(status='G '),
2003    'A/D/G/rho'     : Item(status='U '),
2004    'kappa'         : Item(status='E '),
2005    'A/B/F/nu'      : Item(status='E '),
2006    'A/C/I'         : Item(status='E '),
2007    'A/C/I/upsilon' : Item(status='E '),
2008    'A/C/I/zeta'    : Item(status='A '),
2009    })
2010
2011  # Create expected output tree for an update of the wc_backup.
2012  #
2013  # - mu and rho are run of the mill update operations; merge and update
2014  #   respectively.
2015  #
2016  # - kappa, nu, I, and upsilon all 'E'xisted as unversioned items in the WC.
2017  #
2018  # - While the dir I does exist, I/zeta does not so it's just an add.
2019  expected_disk = svntest.main.greek_state.copy()
2020  expected_disk.add({
2021    'A/B/F/nu'      : Item("This is the file 'nu'\n"),
2022    'kappa'         : Item("This is the OBSTRUCTING file 'kappa'\n"),
2023    'A/C/I'         : Item(),
2024    'A/C/I/upsilon' : Item("This is the OBSTRUCTING file 'upsilon'\n"),
2025    'A/C/I/zeta'    : Item("This is the file 'zeta'\n"),
2026    })
2027  expected_disk.tweak('A/mu',
2028                      contents=expected_disk.desc['A/mu'].contents
2029                      + 'appended mu text')
2030  expected_disk.tweak('A/D/G/rho',
2031                      contents=expected_disk.desc['A/D/G/rho'].contents
2032                      + 'new appended text for rho')
2033
2034  # Create expected status tree for the update.  Since the obstructing
2035  # kappa and upsilon differ from the repos, they should show as modified.
2036  expected_status = svntest.actions.get_virginal_state(wc_backup, 2)
2037  expected_status.add({
2038    'A/B/F/nu'      : Item(status='  ', wc_rev=2),
2039    'A/C/I'         : Item(status='  ', wc_rev=2),
2040    'A/C/I/zeta'    : Item(status='  ', wc_rev=2),
2041    'kappa'         : Item(status='M ', wc_rev=2),
2042    'A/C/I/upsilon' : Item(status='M ', wc_rev=2),
2043    })
2044
2045  # Perform forced update and check the results in three ways.
2046  svntest.actions.run_and_verify_update(wc_backup,
2047                                        expected_output,
2048                                        expected_disk,
2049                                        expected_status,
2050                                        [], False,
2051                                        wc_backup, '--force')
2052
2053#----------------------------------------------------------------------
2054def forced_update_failures(sbox):
2055  "forced up fails with some types of obstructions"
2056
2057  sbox.build()
2058  wc_dir = sbox.wc_dir
2059
2060  # Make a backup copy of the working copy
2061  wc_backup = sbox.add_wc_path('backup')
2062  svntest.actions.duplicate_dir(wc_dir, wc_backup)
2063
2064  # Add a file
2065  nu_path = sbox.ospath('A/B/F/nu')
2066  svntest.main.file_append(nu_path, "This is the file 'nu'\n")
2067  svntest.main.run_svn(None, 'add', nu_path)
2068
2069  # Add a dir
2070  I_path = sbox.ospath('A/C/I')
2071  os.mkdir(I_path)
2072  svntest.main.run_svn(None, 'add', I_path)
2073
2074  # Created expected output tree for 'svn ci'
2075  expected_output = wc.State(wc_dir, {
2076    'A/B/F/nu'      : Item(verb='Adding'),
2077    'A/C/I'         : Item(verb='Adding'),
2078    })
2079
2080  # Create expected status tree.
2081  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2082  expected_status.add({
2083    'A/B/F/nu'      : Item(status='  ', wc_rev=2),
2084    'A/C/I'         : Item(status='  ', wc_rev=2),
2085    })
2086
2087  # Commit.
2088  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
2089                                        expected_status)
2090
2091  # Create an unversioned dir A/B/F/nu that will obstruct the file of the
2092  # same name coming from the repository.  Create an unversioned file A/C/I
2093  # that will obstruct the dir of the same name.
2094  nu_path = os.path.join(wc_backup, 'A', 'B', 'F', 'nu')
2095  os.mkdir(nu_path)
2096  I_path = os.path.join(wc_backup, 'A', 'C', 'I')
2097  svntest.main.file_append(I_path,
2098                           "This is the file 'I'...shouldn't I be a dir?\n")
2099
2100  # A forced update that tries to add a file when an unversioned directory
2101  # of the same name already exists should fail.
2102  #svntest.factory.make(sbox, """svn up --force $WC_DIR.backup/A/B/F""")
2103  #exit(0)
2104  backup_A_B_F = os.path.join(wc_backup, 'A', 'B', 'F')
2105
2106  # svn up --force $WC_DIR.backup/A/B/F
2107  expected_output = svntest.wc.State(wc_backup, {
2108    'A/B/F/nu'          : Item(status='  ', treeconflict='C'),
2109  })
2110
2111  expected_disk = svntest.main.greek_state.copy()
2112  expected_disk.add({
2113    'A/B/F/nu'          : Item(),
2114    'A/C/I'             :
2115    Item(contents="This is the file 'I'...shouldn't I be a dir?\n"),
2116  })
2117
2118  expected_status = actions.get_virginal_state(wc_backup, 1)
2119  expected_status.add({
2120    'A/B/F/nu'          : Item(status='D ', treeconflict='C', wc_rev='2'),
2121  })
2122  expected_status.tweak('A/B/F', wc_rev='2')
2123
2124  actions.run_and_verify_update(wc_backup, expected_output,
2125                                expected_disk, expected_status,
2126                                [], False,
2127                                '--force', backup_A_B_F)
2128
2129
2130  # A forced update that tries to add a directory when an unversioned file
2131  # of the same name already exists should fail.
2132  # svntest.factory.make(sbox, """
2133  #   svn up --force wc_dir_backup/A/C
2134  #   rm -rf wc_dir_backup/A/C/I wc_dir_backup/A/B/F/nu
2135  #   svn up wc_dir_backup
2136  #   svn up -r1 wc_dir_backup/A/C
2137  #   svn co url/A/C/I wc_dir_backup/A/C/I
2138  #   svn up --force wc_dir_backup/A/C
2139  #   """)
2140  # exit(0)
2141  url = sbox.repo_url
2142  wc_dir_backup = sbox.wc_dir + '.backup'
2143
2144  backup_A_B_F_nu = os.path.join(wc_dir_backup, 'A', 'B', 'F', 'nu')
2145  backup_A_C = os.path.join(wc_dir_backup, 'A', 'C')
2146  backup_A_C_I = os.path.join(wc_dir_backup, 'A', 'C', 'I')
2147  url_A_C_I = url + '/A/C/I'
2148
2149  # svn up --force wc_dir_backup/A/C
2150  expected_output = svntest.wc.State(wc_dir_backup, {
2151    'A/C/I'             : Item(status='  ', treeconflict='C'),
2152  })
2153
2154  expected_disk = svntest.main.greek_state.copy()
2155  expected_disk.add({
2156    'A/B/F/nu'          : Item(),
2157    'A/C/I'             :
2158    Item(contents="This is the file 'I'...shouldn't I be a dir?\n"),
2159  })
2160
2161  expected_status = actions.get_virginal_state(wc_dir_backup, 1)
2162  expected_status.add({
2163    'A/C/I'             : Item(status='D ', treeconflict='C', wc_rev=2),
2164    'A/B/F/nu'          : Item(status='D ', treeconflict='C', wc_rev=2),
2165  })
2166  expected_status.tweak('A/C', 'A/B/F', wc_rev='2')
2167
2168  actions.run_and_verify_update(wc_dir_backup, expected_output,
2169                                expected_disk, expected_status,
2170                                [], False,
2171                                '--force', backup_A_C)
2172
2173  # rm -rf wc_dir_backup/A/C/I wc_dir_backup/A/B/F/nu
2174  os.remove(backup_A_C_I)
2175  svntest.main.safe_rmtree(backup_A_B_F_nu)
2176
2177  svntest.main.run_svn(None, 'revert', backup_A_C_I, backup_A_B_F_nu)
2178
2179  # svn up wc_dir_backup
2180  expected_output = svntest.wc.State(wc_dir_backup, {
2181  })
2182
2183  expected_disk.tweak('A/B/F/nu', contents="This is the file 'nu'\n")
2184  expected_disk.tweak('A/C/I', contents=None)
2185
2186  expected_status.tweak(wc_rev='2', status='  ')
2187  expected_status.tweak('A/C/I', 'A/B/F/nu', treeconflict=None)
2188
2189  actions.run_and_verify_update(wc_dir_backup, expected_output,
2190                                expected_disk, expected_status)
2191
2192  # svn up -r1 wc_dir_backup/A/C
2193  expected_output = svntest.wc.State(wc_dir_backup, {
2194    'A/C/I'             : Item(status='D '),
2195  })
2196
2197  expected_disk.remove('A/C/I')
2198
2199  expected_status.remove('A/C/I')
2200  expected_status.tweak('A/C', wc_rev='1')
2201
2202  actions.run_and_verify_update(wc_dir_backup, expected_output,
2203                                expected_disk, expected_status,
2204                                [], False,
2205                                '-r1', backup_A_C)
2206
2207  # svn co url/A/C/I wc_dir_backup/A/C/I
2208  expected_output = svntest.wc.State(wc_dir_backup, {})
2209
2210  expected_disk = svntest.wc.State(wc_dir, {})
2211
2212  actions.run_and_verify_checkout(url_A_C_I, backup_A_C_I,
2213                                  expected_output, expected_disk)
2214
2215  # svn up --force wc_dir_backup/A/C
2216  expected_output = svntest.wc.State(wc_dir_backup, {
2217    'A/C/I'             : Item(verb='Skipped'),
2218  })
2219
2220  actions.run_and_verify_update(wc_dir_backup, expected_output, None, None,
2221                                [], False,
2222                                '--force', backup_A_C)
2223
2224
2225#----------------------------------------------------------------------
2226# Test for issue #2556. The tests maps a virtual drive to a working copy
2227# and tries some basic update, commit and status actions on the virtual
2228# drive.
2229@SkipUnless(svntest.main.is_os_windows)
2230def update_wc_on_windows_drive(sbox):
2231  "update wc on the root of a Windows (virtual) drive"
2232
2233  def find_the_next_available_drive_letter():
2234    "find the first available drive"
2235
2236    # get the list of used drive letters, use some Windows specific function.
2237    try:
2238      import win32api
2239
2240      drives=win32api.GetLogicalDriveStrings()
2241      drives=drives.split('\000')
2242
2243      for d in range(ord('G'), ord('Z')+1):
2244        drive = chr(d)
2245        if not drive + ':\\' in drives:
2246          return drive
2247    except ImportError:
2248      # In ActiveState python x64 win32api is not available
2249      for d in range(ord('G'), ord('Z')+1):
2250        drive = chr(d)
2251        if not os.path.isdir(drive + ':\\'):
2252          return drive
2253
2254    return None
2255
2256  # just create an empty folder, we'll checkout later.
2257  sbox.build(create_wc = False)
2258  svntest.main.safe_rmtree(sbox.wc_dir)
2259  os.mkdir(sbox.wc_dir)
2260
2261  # create a virtual drive to the working copy folder
2262  drive = find_the_next_available_drive_letter()
2263  if drive is None:
2264    raise svntest.Skip('No drive letter available')
2265
2266  subprocess.call(['subst', drive +':', sbox.wc_dir])
2267  wc_dir = drive + ':/'
2268  was_cwd = os.getcwd()
2269
2270  try:
2271    svntest.actions.run_and_verify_svn(None, [],
2272                                       'checkout',
2273                                       sbox.repo_url, wc_dir)
2274
2275    # Make some local modifications
2276    mu_path = os.path.join(wc_dir, 'A', 'mu').replace(os.sep, '/')
2277    svntest.main.file_append(mu_path, '\nAppended text for mu')
2278    zeta_path = os.path.join(wc_dir, 'zeta').replace(os.sep, '/')
2279    svntest.main.file_append(zeta_path, "This is the file 'zeta'\n")
2280    svntest.main.run_svn(None, 'add', zeta_path)
2281
2282    # Commit.
2283    expected_output = svntest.wc.State(wc_dir, {
2284      'A/mu' : Item(verb='Sending'),
2285      'zeta' : Item(verb='Adding'),
2286      })
2287
2288    expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2289    expected_status.tweak('A/mu', wc_rev=2)
2290    expected_status.add({
2291    'zeta' : Item(status='  ', wc_rev=2),
2292    })
2293
2294    svntest.actions.run_and_verify_commit(wc_dir, expected_output,
2295                                          expected_status, [],
2296                                          wc_dir, zeta_path)
2297
2298    # Non recursive commit
2299    dir1_path = os.path.join(wc_dir, 'dir1').replace(os.sep, '/')
2300    os.mkdir(dir1_path)
2301    svntest.main.run_svn(None, 'add', '-N', dir1_path)
2302    file1_path = os.path.join(dir1_path, 'file1')
2303    svntest.main.file_append(file1_path, "This is the file 'file1'\n")
2304    svntest.main.run_svn(None, 'add', '-N', file1_path)
2305
2306    expected_output = svntest.wc.State(wc_dir, {
2307      'dir1' : Item(verb='Adding'),
2308      'dir1/file1' : Item(verb='Adding'),
2309      })
2310
2311    expected_status.add({
2312      'dir1' : Item(status='  ', wc_rev=3),
2313      'dir1/file1' : Item(status='  ', wc_rev=3),
2314      })
2315
2316    svntest.actions.run_and_verify_commit(wc_dir, expected_output,
2317                                          expected_status, [],
2318                                          '-N',
2319                                          wc_dir,
2320                                          dir1_path, file1_path)
2321
2322    # revert to previous revision to test update
2323    os.chdir(wc_dir)
2324
2325    expected_disk = svntest.main.greek_state.copy()
2326
2327    expected_output = svntest.wc.State('', {
2328      'A/mu' : Item(status='U '),
2329      'zeta' : Item(status='D '),
2330      'dir1' : Item(status='D '),
2331      })
2332
2333    expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2334
2335    svntest.actions.run_and_verify_update(wc_dir,
2336                                          expected_output,
2337                                          expected_disk,
2338                                          expected_status,
2339                                          [], False,
2340                                          '-r', '1', wc_dir)
2341
2342    os.chdir(was_cwd)
2343
2344    # update to the latest version, but use the relative path 'X:'
2345    wc_dir = drive + ":"
2346
2347    expected_output = svntest.wc.State(wc_dir, {
2348      'A/mu' : Item(status='U '),
2349      'zeta' : Item(status='A '),
2350      'dir1' : Item(status='A '),
2351      'dir1/file1' : Item(status='A '),
2352      })
2353
2354    expected_status = svntest.actions.get_virginal_state(wc_dir, 3)
2355    expected_status.add({
2356      'dir1' : Item(status='  ', wc_rev=3),
2357      'dir1/file1' : Item(status='  ', wc_rev=3),
2358      'zeta' : Item(status='  ', wc_rev=3),
2359      })
2360
2361    expected_disk.add({
2362      'zeta'    : Item("This is the file 'zeta'\n"),
2363      'dir1/file1': Item("This is the file 'file1'\n"),
2364      })
2365    expected_disk.tweak('A/mu', contents = expected_disk.desc['A/mu'].contents
2366                        + '\nAppended text for mu')
2367
2368    # Create expected status with 'H:iota' style paths
2369    expected_status_relative = svntest.wc.State('', {})
2370    expected_status_relative.add_state(wc_dir, expected_status, strict=True)
2371
2372    svntest.actions.run_and_verify_update(wc_dir,
2373                                          expected_output,
2374                                          expected_disk,
2375                                          expected_status_relative)
2376
2377  finally:
2378    os.chdir(was_cwd)
2379    # cleanup the virtual drive
2380    subprocess.call(['subst', '/D', drive +':'])
2381
2382# Issue #2618: "'Checksum mismatch' error when receiving
2383# update for replaced-with-history file".
2384def update_wc_with_replaced_file(sbox):
2385  "update wc containing a replaced-with-history file"
2386
2387  sbox.build()
2388  wc_dir = sbox.wc_dir
2389
2390  # Make a backup copy of the working copy.
2391  wc_backup = sbox.add_wc_path('backup')
2392  svntest.actions.duplicate_dir(wc_dir, wc_backup)
2393
2394  # we need a change in the repository
2395  iota_path = sbox.ospath('iota')
2396  mu_path = sbox.ospath('A/mu')
2397  iota_bu_path = os.path.join(wc_backup, 'iota')
2398  svntest.main.file_append(iota_bu_path, "New line in 'iota'\n")
2399  svntest.main.run_svn(None,
2400                       'ci', wc_backup, '-m', 'changed file')
2401
2402  # First, a replacement without history.
2403  svntest.main.run_svn(None, 'rm', iota_path)
2404  svntest.main.file_append(iota_path, "")
2405  svntest.main.run_svn(None, 'add', iota_path)
2406
2407  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2408  expected_status.tweak('iota', status='R ', wc_rev='1')
2409
2410  svntest.actions.run_and_verify_status(wc_dir, expected_status)
2411
2412  # Now update the wc.  The local replacement is a tree conflict with
2413  # the incoming edit on that deleted item.
2414  expected_output = svntest.wc.State(wc_dir, {
2415    'iota' : Item(status='  ', treeconflict='C'),
2416    })
2417
2418  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
2419  expected_status.add({
2420    'iota' : Item(status='R ', wc_rev='2', treeconflict='C'),
2421    })
2422
2423  expected_disk = svntest.main.greek_state.copy()
2424  expected_disk.tweak('iota', contents="")
2425
2426  svntest.actions.run_and_verify_update(wc_dir,
2427                                        expected_output,
2428                                        expected_disk,
2429                                        expected_status)
2430
2431  # Make us a working copy with a 'replace-with-history' file.
2432  svntest.main.run_svn(None, 'revert', iota_path)
2433
2434  expected_output = svntest.wc.State(wc_dir, {
2435    'iota' : Item(status='U '),
2436    })
2437
2438  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2439
2440  expected_disk = svntest.main.greek_state.copy()
2441
2442  svntest.actions.run_and_verify_update(wc_dir,
2443                                        expected_output,
2444                                        expected_disk,
2445                                        expected_status,
2446                                        [], False,
2447                                        wc_dir, '-r1')
2448
2449  svntest.main.run_svn(None, 'rm', iota_path)
2450  svntest.main.run_svn(None, 'cp', mu_path, iota_path)
2451
2452  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2453  expected_status.tweak('iota', status='R ', copied='+', wc_rev='-')
2454
2455  svntest.actions.run_and_verify_status(wc_dir, expected_status)
2456
2457  # Now update the wc.  The local replacement is a tree conflict with
2458  # the incoming edit on that deleted item.
2459  expected_output = svntest.wc.State(wc_dir, {
2460    'iota' : Item(status='  ', treeconflict='C'),
2461    })
2462
2463  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
2464  expected_status.add({
2465    'iota' : Item(status='R ', wc_rev='-', treeconflict='C', copied='+'),
2466    })
2467
2468  expected_disk = svntest.main.greek_state.copy()
2469  expected_disk.tweak('iota', contents="This is the file 'mu'.\n")
2470
2471  svntest.actions.run_and_verify_update(wc_dir,
2472                                        expected_output,
2473                                        expected_disk,
2474                                        expected_status)
2475
2476#----------------------------------------------------------------------
2477def update_with_obstructing_additions(sbox):
2478  "update handles obstructing paths scheduled for add"
2479
2480  sbox.build()
2481  wc_dir = sbox.wc_dir
2482
2483  # Make a backup copy of the working copy
2484  wc_backup = sbox.add_wc_path('backup')
2485  svntest.actions.duplicate_dir(wc_dir, wc_backup)
2486
2487  # Add files and dirs to the repos via the first WC.  Each of these
2488  # will be added to the backup WC via an update:
2489  #
2490  #  A/B/upsilon:   Identical to the file scheduled for addition in
2491  #                 the backup WC.
2492  #
2493  #  A/C/nu:        A "normal" add, won't exist in the backup WC.
2494  #
2495  #  A/D/kappa:     Textual and property conflict with the file scheduled
2496  #                 for addition in the backup WC.
2497  #
2498  #  A/D/epsilon:   Textual conflict with the file scheduled for addition.
2499  #
2500  #  A/D/zeta:      Prop conflict with the file scheduled for addition.
2501  #
2502  #                 Three new dirs that will also be scheduled for addition:
2503  #  A/D/H/I:         No props on either WC or REPOS.
2504  #  A/D/H/I/J:       Prop conflict with the scheduled add.
2505  #  A/D/H/I/K:       Same (mergeable) prop on WC and REPOS.
2506  #
2507  #  A/D/H/I/K/xi:  Identical to the file scheduled for addition in
2508  #                 the backup WC. No props.
2509  #
2510  #  A/D/H/I/L:     A "normal" dir add, won't exist in the backup WC.
2511  #
2512  #  A/D/H/I/J/eta: Conflicts with the file scheduled for addition in
2513  #                 the backup WC.  No props.
2514  upsilon_path = sbox.ospath('A/B/upsilon')
2515  svntest.main.file_append(upsilon_path, "This is the file 'upsilon'\n")
2516  nu_path = sbox.ospath('A/C/nu')
2517  svntest.main.file_append(nu_path, "This is the file 'nu'\n")
2518  kappa_path = sbox.ospath('A/D/kappa')
2519  svntest.main.file_append(kappa_path, "This is REPOS file 'kappa'\n")
2520  epsilon_path = sbox.ospath('A/D/epsilon')
2521  svntest.main.file_append(epsilon_path, "This is REPOS file 'epsilon'\n")
2522  zeta_path = sbox.ospath('A/D/zeta')
2523  svntest.main.file_append(zeta_path, "This is the file 'zeta'\n")
2524  I_path = sbox.ospath('A/D/H/I')
2525  os.mkdir(I_path)
2526  J_path = os.path.join(I_path, 'J')
2527  os.mkdir(J_path)
2528  K_path = os.path.join(I_path, 'K')
2529  os.mkdir(K_path)
2530  L_path = os.path.join(I_path, 'L')
2531  os.mkdir(L_path)
2532  xi_path = os.path.join(K_path, 'xi')
2533  svntest.main.file_append(xi_path, "This is the file 'xi'\n")
2534  eta_path = os.path.join(J_path, 'eta')
2535  svntest.main.file_append(eta_path, "This is REPOS file 'eta'\n")
2536
2537  svntest.main.run_svn(None, 'add', upsilon_path, nu_path,
2538                       kappa_path, epsilon_path, zeta_path, I_path)
2539
2540  # Set props that will conflict with scheduled adds.
2541  svntest.main.run_svn(None, 'propset', 'propname1', 'propval-REPOS',
2542                       kappa_path)
2543  svntest.main.run_svn(None, 'propset', 'propname1', 'propval-REPOS',
2544                       zeta_path)
2545  svntest.main.run_svn(None, 'propset', 'propname1', 'propval-REPOS',
2546                       J_path)
2547
2548  # Set prop that will match with scheduled add.
2549  svntest.main.run_svn(None, 'propset', 'propname1', 'propval-SAME',
2550                       epsilon_path)
2551  svntest.main.run_svn(None, 'propset', 'propname1', 'propval-SAME',
2552                       K_path)
2553
2554  # Created expected output tree for 'svn ci'
2555  expected_output = wc.State(wc_dir, {
2556    'A/B/upsilon'   : Item(verb='Adding'),
2557    'A/C/nu'        : Item(verb='Adding'),
2558    'A/D/kappa'     : Item(verb='Adding'),
2559    'A/D/epsilon'   : Item(verb='Adding'),
2560    'A/D/zeta'      : Item(verb='Adding'),
2561    'A/D/H/I'       : Item(verb='Adding'),
2562    'A/D/H/I/J'     : Item(verb='Adding'),
2563    'A/D/H/I/J/eta' : Item(verb='Adding'),
2564    'A/D/H/I/K'     : Item(verb='Adding'),
2565    'A/D/H/I/K/xi'  : Item(verb='Adding'),
2566    'A/D/H/I/L'     : Item(verb='Adding'),
2567    })
2568
2569  # Create expected status tree.
2570  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2571  expected_status.add({
2572    'A/B/upsilon'   : Item(status='  ', wc_rev=2),
2573    'A/C/nu'        : Item(status='  ', wc_rev=2),
2574    'A/D/kappa'     : Item(status='  ', wc_rev=2),
2575    'A/D/epsilon'   : Item(status='  ', wc_rev=2),
2576    'A/D/zeta'      : Item(status='  ', wc_rev=2),
2577    'A/D/H/I'       : Item(status='  ', wc_rev=2),
2578    'A/D/H/I/J'     : Item(status='  ', wc_rev=2),
2579    'A/D/H/I/J/eta' : Item(status='  ', wc_rev=2),
2580    'A/D/H/I/K'     : Item(status='  ', wc_rev=2),
2581    'A/D/H/I/K/xi'  : Item(status='  ', wc_rev=2),
2582    'A/D/H/I/L'     : Item(status='  ', wc_rev=2),
2583    })
2584
2585  # Commit.
2586  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
2587                                        expected_status)
2588
2589  # Create various paths scheduled for addition which will obstruct
2590  # the adds coming from the repos.
2591  upsilon_backup_path = os.path.join(wc_backup, 'A', 'B', 'upsilon')
2592  svntest.main.file_append(upsilon_backup_path,
2593                           "This is the file 'upsilon'\n")
2594  kappa_backup_path = os.path.join(wc_backup, 'A', 'D', 'kappa')
2595  svntest.main.file_append(kappa_backup_path,
2596                           "This is WC file 'kappa'\n")
2597  epsilon_backup_path = os.path.join(wc_backup, 'A', 'D', 'epsilon')
2598  svntest.main.file_append(epsilon_backup_path,
2599                           "This is WC file 'epsilon'\n")
2600  zeta_backup_path = os.path.join(wc_backup, 'A', 'D', 'zeta')
2601  svntest.main.file_append(zeta_backup_path, "This is the file 'zeta'\n")
2602  I_backup_path = os.path.join(wc_backup, 'A', 'D', 'H', 'I')
2603  os.mkdir(I_backup_path)
2604  J_backup_path = os.path.join(I_backup_path, 'J')
2605  os.mkdir(J_backup_path)
2606  K_backup_path = os.path.join(I_backup_path, 'K')
2607  os.mkdir(K_backup_path)
2608  xi_backup_path = os.path.join(K_backup_path, 'xi')
2609  svntest.main.file_append(xi_backup_path, "This is the file 'xi'\n")
2610  eta_backup_path = os.path.join(J_backup_path, 'eta')
2611  svntest.main.file_append(eta_backup_path, "This is WC file 'eta'\n")
2612
2613  svntest.main.run_svn(None, 'add', upsilon_backup_path, kappa_backup_path,
2614                       epsilon_backup_path, zeta_backup_path, I_backup_path)
2615
2616  # Set prop that will conflict with add from repos.
2617  svntest.main.run_svn(None, 'propset', 'propname1', 'propval-WC',
2618                       kappa_backup_path)
2619  svntest.main.run_svn(None, 'propset', 'propname1', 'propval-WC',
2620                       zeta_backup_path)
2621  svntest.main.run_svn(None, 'propset', 'propname1', 'propval-WC',
2622                       J_backup_path)
2623
2624  # Set prop that will match add from repos.
2625  svntest.main.run_svn(None, 'propset', 'propname1', 'propval-SAME',
2626                       epsilon_backup_path)
2627  svntest.main.run_svn(None, 'propset', 'propname1', 'propval-SAME',
2628                       K_backup_path)
2629
2630  # Create expected output tree for an update of the wc_backup.
2631  expected_output = wc.State(wc_backup, {
2632    'A/B/upsilon'   : Item(status='E '),
2633    'A/C/nu'        : Item(status='A '),
2634    'A/D/H/I'       : Item(status='E '),
2635    'A/D/H/I/J'     : Item(status='EC'),
2636    'A/D/H/I/J/eta' : Item(status='C '),
2637    'A/D/H/I/K'     : Item(status='EG'),
2638    'A/D/H/I/K/xi'  : Item(status='E '),
2639    'A/D/H/I/L'     : Item(status='A '),
2640    'A/D/kappa'     : Item(status='CC'),
2641    'A/D/epsilon'   : Item(status='CG'),
2642    'A/D/zeta'      : Item(status='EC'),
2643    })
2644
2645  # Create expected disk for update of wc_backup.
2646  expected_disk = svntest.main.greek_state.copy()
2647  expected_disk.add({
2648    'A/B/upsilon'   : Item("This is the file 'upsilon'\n"),
2649    'A/C/nu'        : Item("This is the file 'nu'\n"),
2650    'A/D/H/I'       : Item(),
2651    'A/D/H/I/J'     : Item(props={'propname1' : 'propval-WC'}),
2652    'A/D/H/I/J/eta' : Item("\n".join(["<<<<<<< .mine",
2653                                      "This is WC file 'eta'",
2654                                      "||||||| .r0",
2655                                      "=======",
2656                                      "This is REPOS file 'eta'",
2657                                      ">>>>>>> .r2",
2658                                      ""])),
2659    'A/D/H/I/K'     : Item(props={'propname1' : 'propval-SAME'}),
2660    'A/D/H/I/K/xi'  : Item("This is the file 'xi'\n"),
2661    'A/D/H/I/L'     : Item(),
2662    'A/D/kappa'     : Item("\n".join(["<<<<<<< .mine",
2663                                      "This is WC file 'kappa'",
2664                                      "||||||| .r0",
2665                                      "=======",
2666                                      "This is REPOS file 'kappa'",
2667                                      ">>>>>>> .r2",
2668                                      ""]),
2669                           props={'propname1' : 'propval-WC'}),
2670    'A/D/epsilon'     : Item("\n".join(["<<<<<<< .mine",
2671                                        "This is WC file 'epsilon'",
2672                                        "||||||| .r0",
2673                                        "=======",
2674                                        "This is REPOS file 'epsilon'",
2675                                        ">>>>>>> .r2",
2676                                        ""]),
2677                             props={'propname1' : 'propval-SAME'}),
2678    'A/D/zeta'   : Item("This is the file 'zeta'\n",
2679                        props={'propname1' : 'propval-WC'}),
2680    })
2681
2682  # Create expected status tree for the update.  Since the obstructing
2683  # kappa and upsilon differ from the repos, they should show as modified.
2684  expected_status = svntest.actions.get_virginal_state(wc_backup, 2)
2685  expected_status.add({
2686    'A/B/upsilon'   : Item(status='  ', wc_rev=2),
2687    'A/C/nu'        : Item(status='  ', wc_rev=2),
2688    'A/D/H/I'       : Item(status='  ', wc_rev=2),
2689    'A/D/H/I/J'     : Item(status=' C', wc_rev=2),
2690    'A/D/H/I/J/eta' : Item(status='C ', wc_rev=2),
2691    'A/D/H/I/K'     : Item(status='  ', wc_rev=2),
2692    'A/D/H/I/K/xi'  : Item(status='  ', wc_rev=2),
2693    'A/D/H/I/L'     : Item(status='  ', wc_rev=2),
2694    'A/D/kappa'     : Item(status='CC', wc_rev=2),
2695    'A/D/epsilon'   : Item(status='C ', wc_rev=2),
2696    'A/D/zeta'      : Item(status=' C', wc_rev=2),
2697    })
2698
2699  # "Extra" files that we expect to result from the conflicts.
2700  extra_files = ['eta\.r0', 'eta\.r2', 'eta\.mine',
2701                 'kappa\.r0', 'kappa\.r2', 'kappa\.mine',
2702                 'epsilon\.r0', 'epsilon\.r2', 'epsilon\.mine',
2703                 'kappa.prej', 'zeta.prej', 'dir_conflicts.prej']
2704
2705  # Perform forced update and check the results in three
2706  # ways (including props).
2707  svntest.actions.run_and_verify_update(wc_backup,
2708                                        expected_output,
2709                                        expected_disk,
2710                                        expected_status,
2711                                        [], True,
2712                                        '--adds-as-modification', wc_backup,
2713                                        extra_files=extra_files)
2714
2715  # Some obstructions are still not permitted:
2716  #
2717  # Test that file and dir obstructions scheduled for addition *with*
2718  # history fail when update tries to add the same path.
2719
2720  # URL to URL copy of A/D/G to A/M.
2721  G_URL = sbox.repo_url + '/A/D/G'
2722  M_URL = sbox.repo_url + '/A/M'
2723  svntest.actions.run_and_verify_svn(None, [],
2724                                     'cp', G_URL, M_URL, '-m', '')
2725
2726  # WC to WC copy of A/D/H to A/M, M now scheduled for addition with
2727  # history in WC and pending addition from the repos.
2728  H_path = sbox.ospath('A/D/H')
2729  A_path = sbox.ospath('A')
2730  M_path = sbox.ospath('A/M')
2731
2732  svntest.actions.run_and_verify_svn(None, [],
2733                                     'cp', H_path, M_path)
2734
2735  # URL to URL copy of A/D/H/omega to omicron.
2736  omega_URL = sbox.repo_url + '/A/D/H/omega'
2737  omicron_URL = sbox.repo_url + '/omicron'
2738  svntest.actions.run_and_verify_svn(None, [],
2739                                     'cp', omega_URL, omicron_URL,
2740                                     '-m', '')
2741
2742  # WC to WC copy of A/D/H/chi to omicron, omicron now scheduled for
2743  # addition with history in WC and pending addition from the repos.
2744  chi_path = sbox.ospath('A/D/H/chi')
2745  omicron_path = sbox.ospath('omicron')
2746
2747  svntest.actions.run_and_verify_svn(None, [],
2748                                     'cp', chi_path,
2749                                     omicron_path)
2750
2751  # Try to update M's Parent.
2752  expected_output = wc.State(A_path, {
2753    'M'      : Item(status='  ', treeconflict='C'),
2754    'M/rho'  : Item(status='  ', treeconflict='A'),
2755    'M/pi'   : Item(status='  ', treeconflict='A'),
2756    'M/tau'  : Item(status='  ', treeconflict='A'),
2757    })
2758
2759  expected_disk = svntest.main.greek_state.copy()
2760  expected_disk.add({
2761    'A/B/upsilon'   : Item("This is the file 'upsilon'\n"),
2762    'A/C/nu'        : Item("This is the file 'nu'\n"),
2763    'A/D/H/I'       : Item(),
2764    'A/D/H/I/J'     : Item(),
2765    'A/D/H/I/J/eta' : Item("This is REPOS file 'eta'\n"),
2766    'A/D/H/I/K'     : Item(),
2767    'A/D/H/I/K/xi'  : Item("This is the file 'xi'\n"),
2768    'A/D/H/I/L'     : Item(),
2769    'A/D/kappa'     : Item("This is REPOS file 'kappa'\n"),
2770    'A/D/epsilon'   : Item("This is REPOS file 'epsilon'\n"),
2771    'A/D/gamma'     : Item("This is the file 'gamma'.\n"),
2772    'A/D/zeta'      : Item("This is the file 'zeta'\n"),
2773    'A/M/I'         : Item(),
2774    'A/M/I/J'       : Item(),
2775    'A/M/I/J/eta'   : Item("This is REPOS file 'eta'\n"),
2776    'A/M/I/K'       : Item(),
2777    'A/M/I/K/xi'    : Item("This is the file 'xi'\n"),
2778    'A/M/I/L'       : Item(),
2779    'A/M/chi'       : Item("This is the file 'chi'.\n"),
2780    'A/M/psi'       : Item("This is the file 'psi'.\n"),
2781    'A/M/omega'     : Item("This is the file 'omega'.\n"),
2782    'omicron'       : Item("This is the file 'chi'.\n"),
2783    })
2784
2785  expected_status = svntest.actions.get_virginal_state(wc_dir, 4)
2786  expected_status.tweak('', 'iota', wc_rev=1)
2787  expected_status.add({
2788    'A/B/upsilon'   : Item(status='  ', wc_rev=4),
2789    'A/C/nu'        : Item(status='  ', wc_rev=4),
2790    'A/D/kappa'     : Item(status='  ', wc_rev=4),
2791    'A/D/epsilon'   : Item(status='  ', wc_rev=4),
2792    'A/D/gamma'     : Item(status='  ', wc_rev=4),
2793    'A/D/zeta'      : Item(status='  ', wc_rev=4),
2794    'A/D/H/I'       : Item(status='  ', wc_rev=4),
2795    'A/D/H/I/J'     : Item(status='  ', wc_rev=4),
2796    'A/D/H/I/J/eta' : Item(status='  ', wc_rev=4),
2797    'A/D/H/I/K'     : Item(status='  ', wc_rev=4),
2798    'A/D/H/I/K/xi'  : Item(status='  ', wc_rev=4),
2799    'A/D/H/I/L'     : Item(status='  ', wc_rev=4),
2800    'A/M'           : Item(status='R ', copied='+', wc_rev='-',
2801                           treeconflict='C'),
2802    'A/M/I'         : Item(status='A ', copied='+', wc_rev='-',
2803                           entry_status='  '), # New op_root
2804    'A/M/I/J'       : Item(status='  ', copied='+', wc_rev='-'),
2805    'A/M/I/J/eta'   : Item(status='  ', copied='+', wc_rev='-'),
2806    'A/M/I/K'       : Item(status='  ', copied='+', wc_rev='-'),
2807    'A/M/I/K/xi'    : Item(status='  ', copied='+', wc_rev='-'),
2808    'A/M/I/L'       : Item(status='  ', copied='+', wc_rev='-'),
2809    'A/M/chi'       : Item(status='  ', copied='+', wc_rev='-'),
2810    'A/M/psi'       : Item(status='  ', copied='+', wc_rev='-'),
2811    'A/M/omega'     : Item(status='  ', copied='+', wc_rev='-'),
2812    'omicron'       : Item(status='A ', copied='+', wc_rev='-'),
2813
2814    # Inserted under the tree conflict
2815    'A/M/pi'            : Item(status='D ', wc_rev='4'),
2816    'A/M/rho'           : Item(status='D ', wc_rev='4'),
2817    'A/M/tau'           : Item(status='D ', wc_rev='4'),
2818    })
2819
2820  svntest.actions.run_and_verify_update(wc_dir, expected_output,
2821                                        expected_disk, expected_status,
2822                                        [], False,
2823                                        '--adds-as-modification',
2824                                        A_path)
2825
2826  # Resolve the tree conflict.
2827  svntest.main.run_svn(None, 'resolve', '--accept', 'working', M_path)
2828
2829  # Try to update omicron's parent, non-recusively so as not to
2830  # try and update M first.
2831  expected_output = wc.State(wc_dir, {
2832    'omicron'   : Item(status='  ', treeconflict='C'),
2833    })
2834
2835  expected_status.tweak('', 'iota', status='  ', wc_rev=4)
2836  expected_status.tweak('omicron', status='R ', copied='+', wc_rev='-',
2837                        treeconflict='C')
2838  expected_status.tweak('A/M', treeconflict=None)
2839
2840  svntest.actions.run_and_verify_update(wc_dir, expected_output,
2841                                        expected_disk, expected_status,
2842                                        [], False,
2843                                        wc_dir, '-N', '--adds-as-modification')
2844
2845  # Resolve the tree conflict.
2846  svntest.main.run_svn(None, 'resolved', omicron_path)
2847
2848  expected_output = wc.State(wc_dir, { })
2849
2850  expected_status.tweak('omicron', treeconflict=None)
2851
2852  # Again, --force shouldn't matter.
2853  svntest.actions.run_and_verify_update(wc_dir, expected_output,
2854                                        expected_disk, expected_status,
2855                                        [], False,
2856                                        omicron_path, '-N', '--force')
2857
2858# Test for issue #2022: Update shouldn't touch conflicted files.
2859def update_conflicted(sbox):
2860  "update conflicted files"
2861  sbox.build()
2862  wc_dir = sbox.wc_dir
2863  iota_path = sbox.ospath('iota')
2864  lambda_path = sbox.ospath('A/B/lambda')
2865  mu_path = sbox.ospath('A/mu')
2866  D_path = sbox.ospath('A/D')
2867  pi_path = sbox.ospath('A/D/G/pi')
2868
2869  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2870
2871  # Make some modifications to the files and a dir, creating r2.
2872  svntest.main.file_append(iota_path, 'Original appended text for iota\n')
2873
2874  svntest.main.run_svn(None, 'propset', 'prop', 'val', lambda_path)
2875
2876  svntest.main.file_append(mu_path, 'Original appended text for mu\n')
2877
2878  svntest.main.run_svn(None, 'propset', 'prop', 'val', mu_path)
2879  svntest.main.run_svn(None, 'propset', 'prop', 'val', D_path)
2880
2881  expected_output = svntest.wc.State(wc_dir, {
2882    'iota' : Item(verb='Sending'),
2883    'A/mu': Item(verb='Sending'),
2884    'A/B/lambda': Item(verb='Sending'),
2885    'A/D': Item(verb='Sending'),
2886    })
2887
2888  expected_status.tweak('iota', 'A/mu', 'A/B/lambda', 'A/D', wc_rev=2)
2889
2890  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
2891                                        expected_status)
2892
2893  # Do another change to each path that we will need later.
2894  # Also, change a file below A/D in the path.
2895  svntest.main.file_append(iota_path, 'Another line for iota\n')
2896  svntest.main.file_append(mu_path, 'Another line for mu\n')
2897  svntest.main.file_append(lambda_path, 'Another line for lambda\n')
2898
2899  svntest.main.run_svn(None, 'propset', 'prop', 'val2', D_path)
2900
2901  svntest.main.file_append(pi_path, 'Another line for pi\n')
2902
2903  expected_status.tweak('iota', 'A/mu', 'A/B/lambda', 'A/D', 'A/D/G/pi',
2904                        wc_rev=3)
2905
2906  expected_output.add({
2907    'A/D/G/pi': Item(verb='Sending')})
2908
2909  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
2910                                        expected_status)
2911
2912  # Go back to revision 1.
2913  expected_output = svntest.wc.State(wc_dir, {
2914    'iota' : Item(status='U '),
2915    'A/B/lambda' : Item(status='UU'),
2916    'A/mu' : Item(status='UU'),
2917    'A/D': Item(status=' U'),
2918    'A/D/G/pi': Item(status='U '),
2919    })
2920
2921  expected_disk = svntest.main.greek_state.copy()
2922
2923  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2924
2925  svntest.actions.run_and_verify_update(wc_dir,
2926                                        expected_output,
2927                                        expected_disk,
2928                                        expected_status,
2929                                        [], True,
2930                                        '-r1', wc_dir)
2931
2932  # Create modifications conflicting with rev 2.
2933  svntest.main.file_append(iota_path, 'Conflicting appended text for iota\n')
2934  svntest.main.run_svn(None, 'propset', 'prop', 'conflictval', lambda_path)
2935  svntest.main.file_append(mu_path, 'Conflicting appended text for mu\n')
2936  svntest.main.run_svn(None, 'propset', 'prop', 'conflictval', mu_path)
2937  svntest.main.run_svn(None, 'propset', 'prop', 'conflictval', D_path)
2938
2939  # Update to revision 2, expecting conflicts.
2940  expected_output = svntest.wc.State(wc_dir, {
2941    'iota': Item(status='C '),
2942    'A/B/lambda': Item(status=' C'),
2943    'A/mu': Item(status='CC'),
2944    'A/D': Item(status=' C'),
2945    })
2946
2947  expected_disk.tweak('iota',
2948                      contents="\n".join(["This is the file 'iota'.",
2949                                          "<<<<<<< .mine",
2950                                          "Conflicting appended text for iota",
2951                                          "||||||| .r1",
2952                                          "=======",
2953                                          "Original appended text for iota",
2954                                          ">>>>>>> .r2",
2955                                          ""]))
2956  expected_disk.tweak('A/mu',
2957                      contents="\n".join(["This is the file 'mu'.",
2958                                          "<<<<<<< .mine",
2959                                          "Conflicting appended text for mu",
2960                                          "||||||| .r1",
2961                                          "=======",
2962                                          "Original appended text for mu",
2963                                          ">>>>>>> .r2",
2964                                          ""]),
2965                      props={'prop': 'conflictval'})
2966  expected_disk.tweak('A/B/lambda', 'A/D', props={'prop': 'conflictval'})
2967
2968  expected_status.tweak(wc_rev=2)
2969  expected_status.tweak('iota', status='C ')
2970  expected_status.tweak('A/B/lambda', 'A/D', status=' C')
2971  expected_status.tweak('A/mu', status='CC')
2972
2973  extra_files = [ 'iota.r1', 'iota.r2', 'iota.mine',
2974                  'mu.r1', 'mu.r2', 'mu.mine', 'mu.prej',
2975                  'lambda.prej',
2976                  'dir_conflicts.prej']
2977
2978  svntest.actions.run_and_verify_update(wc_dir,
2979                                        expected_output,
2980                                        expected_disk,
2981                                        expected_status,
2982                                        [], True,
2983                                        '-r2', wc_dir,
2984                                        extra_files=extra_files+[])
2985
2986  # Now, update to HEAD, which should skip all the conflicted files, but
2987  # still update the pi file.
2988  expected_output = svntest.wc.State(wc_dir, {
2989    'iota' : Item(verb='Skipped'),
2990    'A/B/lambda' : Item(verb='Skipped'),
2991    'A/mu' : Item(verb='Skipped'),
2992    'A/D' : Item(verb='Skipped'),
2993    })
2994
2995  expected_status.tweak(wc_rev=3)
2996  expected_status.tweak('iota', 'A/B/lambda', 'A/mu', 'A/D', wc_rev=2)
2997  # We no longer update descendants of a prop-conflicted dir.
2998  expected_status.tweak('A/D/G',
2999                        'A/D/G/pi',
3000                        'A/D/G/rho',
3001                        'A/D/G/tau',
3002                        'A/D/H',
3003                        'A/D/H/chi',
3004                        'A/D/H/omega',
3005                        'A/D/H/psi',
3006                        'A/D/gamma', wc_rev=2)
3007
3008  svntest.actions.run_and_verify_update(wc_dir,
3009                                        expected_output,
3010                                        expected_disk,
3011                                        expected_status,
3012                                        [], True,
3013                                        extra_files=extra_files)
3014
3015#----------------------------------------------------------------------
3016@SkipUnless(server_has_mergeinfo)
3017def mergeinfo_update_elision(sbox):
3018  "mergeinfo does not elide after update"
3019
3020  # No mergeinfo elision is performed when doing updates.  So updates may
3021  # result in equivalent mergeinfo on a path and it's nearest working copy
3022  # parent with explicit mergeinfo.  This is currently permitted and
3023  # honestly we could probably do without this test(?).
3024
3025  sbox.build()
3026  wc_dir = sbox.wc_dir
3027
3028  # Some paths we'll care about
3029  alpha_COPY_path = sbox.ospath('A/B_COPY/E/alpha')
3030  alpha_path  = sbox.ospath('A/B/E/alpha')
3031  B_COPY_path = sbox.ospath('A/B_COPY')
3032  E_COPY_path = sbox.ospath('A/B_COPY/E')
3033  beta_path   = sbox.ospath('A/B/E/beta')
3034  lambda_path = sbox.ospath('A/B/lambda')
3035
3036  # Make a branch A/B_COPY
3037  expected_stdout =  verify.UnorderedOutput([
3038     "A         " + B_COPY_path + "\n",
3039     "A         " + sbox.ospath('A/B_COPY/lambda') + "\n",
3040     "A         " + sbox.ospath('A/B_COPY/E') + "\n",
3041     "A         " + sbox.ospath('A/B_COPY/E/alpha') + "\n",
3042     "A         " + sbox.ospath('A/B_COPY/E/beta') + "\n",
3043     "A         " + sbox.ospath('A/B_COPY/F') + "\n",
3044    ])
3045  svntest.actions.run_and_verify_svn(expected_stdout, [], 'copy',
3046                                     sbox.repo_url + "/A/B", B_COPY_path)
3047
3048  expected_output = wc.State(wc_dir, {'A/B_COPY' : Item(verb='Adding')})
3049
3050  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3051  expected_status.add({
3052    "A/B_COPY"         : Item(status='  ', wc_rev=2),
3053    "A/B_COPY/lambda"  : Item(status='  ', wc_rev=2),
3054    "A/B_COPY/E"       : Item(status='  ', wc_rev=2),
3055    "A/B_COPY/E/alpha" : Item(status='  ', wc_rev=2),
3056    "A/B_COPY/E/beta"  : Item(status='  ', wc_rev=2),
3057    "A/B_COPY/F"       : Item(status='  ', wc_rev=2),})
3058
3059  svntest.actions.run_and_verify_commit(wc_dir,
3060                                        expected_output,
3061                                        expected_status)
3062
3063  # Make some changes under A/B
3064
3065  # r3 - modify and commit A/B/E/beta
3066  svntest.main.file_write(beta_path, "New content")
3067
3068  expected_output = wc.State(wc_dir, {'A/B/E/beta' : Item(verb='Sending')})
3069
3070  expected_status.tweak('A/B/E/beta', wc_rev=3)
3071
3072  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3073                                        expected_status)
3074
3075  # r4 - modify and commit A/B/lambda
3076  svntest.main.file_write(lambda_path, "New content")
3077
3078  expected_output = wc.State(wc_dir, {'A/B/lambda' : Item(verb='Sending')})
3079
3080  expected_status.tweak('A/B/lambda', wc_rev=4)
3081
3082  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3083                                        expected_status)
3084
3085  # r5 - modify and commit A/B/E/alpha
3086  svntest.main.file_write(alpha_path, "New content")
3087
3088  expected_output = wc.State(wc_dir, {'A/B/E/alpha' : Item(verb='Sending')})
3089
3090  expected_status.tweak('A/B/E/alpha', wc_rev=5)
3091
3092  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3093                                        expected_status)
3094
3095  # Merge r2:5 into A/B_COPY
3096  expected_output = wc.State(B_COPY_path, {
3097    'lambda'  : Item(status='U '),
3098    'E/alpha' : Item(status='U '),
3099    'E/beta'  : Item(status='U '),
3100    })
3101
3102  expected_mergeinfo_output = wc.State(B_COPY_path, {
3103    '' : Item(status=' U'),
3104    })
3105
3106  expected_elision_output = wc.State(B_COPY_path, {
3107    })
3108
3109  expected_merge_status = wc.State(B_COPY_path, {
3110    ''        : Item(status=' M', wc_rev=2),
3111    'lambda'  : Item(status='M ', wc_rev=2),
3112    'E'       : Item(status='  ', wc_rev=2),
3113    'E/alpha' : Item(status='M ', wc_rev=2),
3114    'E/beta'  : Item(status='M ', wc_rev=2),
3115    'F'       : Item(status='  ', wc_rev=2),
3116    })
3117
3118  expected_merge_disk = wc.State('', {
3119    ''        : Item(props={SVN_PROP_MERGEINFO : '/A/B:3-5'}),
3120    'lambda'  : Item("New content"),
3121    'E'       : Item(),
3122    'E/alpha' : Item("New content"),
3123    'E/beta'  : Item("New content"),
3124    'F'       : Item(),
3125    })
3126
3127  expected_skip = wc.State(B_COPY_path, { })
3128
3129  svntest.actions.run_and_verify_merge(B_COPY_path, '2', '5',
3130                                       sbox.repo_url + '/A/B', None,
3131                                       expected_output,
3132                                       expected_mergeinfo_output,
3133                                       expected_elision_output,
3134                                       expected_merge_disk,
3135                                       expected_merge_status,
3136                                       expected_skip,
3137                                       check_props=True)
3138
3139  # r6 - Commit the merge
3140  expected_output = wc.State(wc_dir,
3141                             {'A/B_COPY'         : Item(verb='Sending'),
3142                              'A/B_COPY/E/alpha' : Item(verb='Sending'),
3143                              'A/B_COPY/E/beta'  : Item(verb='Sending'),
3144                              'A/B_COPY/lambda'  : Item(verb='Sending')})
3145
3146  expected_status.tweak('A/B_COPY',         wc_rev=6)
3147  expected_status.tweak('A/B_COPY/E/alpha', wc_rev=6)
3148  expected_status.tweak('A/B_COPY/E/beta',  wc_rev=6)
3149  expected_status.tweak('A/B_COPY/lambda',  wc_rev=6)
3150
3151  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3152                                        expected_status)
3153
3154  # Update WC back to r5, A/COPY_B is at it's pre-merge state again
3155  expected_output = wc.State(wc_dir,
3156                             {'A/B_COPY'         : Item(status=' U'),
3157                              'A/B_COPY/E/alpha' : Item(status='U '),
3158                              'A/B_COPY/E/beta'  : Item(status='U '),
3159                              'A/B_COPY/lambda'  : Item(status='U '),})
3160
3161  expected_status.tweak(wc_rev=5)
3162
3163  expected_disk = svntest.main.greek_state.copy()
3164  expected_disk.add({
3165    'A/B_COPY'         : Item(),
3166    'A/B_COPY/lambda'  : Item("This is the file 'lambda'.\n"),
3167    'A/B_COPY/E'       : Item(),
3168    'A/B_COPY/E/alpha' : Item("This is the file 'alpha'.\n"),
3169    'A/B_COPY/E/beta'  : Item("This is the file 'beta'.\n"),
3170    'A/B_COPY/F'       : Item(),
3171    })
3172  expected_disk.tweak('A/B/lambda',  contents="New content")
3173  expected_disk.tweak('A/B/E/alpha', contents="New content")
3174  expected_disk.tweak('A/B/E/beta',  contents="New content")
3175
3176  svntest.actions.run_and_verify_update(wc_dir,
3177                                        expected_output,
3178                                        expected_disk,
3179                                        expected_status,
3180                                        [], True,
3181                                        '-r', '5', wc_dir)
3182
3183  # Merge r2:5 to A/B_COPY/E/alpha
3184  expected_output = wc.State(alpha_COPY_path, {
3185    'alpha' : Item(status='U '),
3186    })
3187  expected_skip = wc.State(alpha_COPY_path, { })
3188
3189  # run_and_verify_merge doesn't support merging to a file WCPATH
3190  # so use run_and_verify_svn.
3191  svntest.actions.run_and_verify_svn(expected_merge_output([[3,5]],
3192                                     ['U    ' + alpha_COPY_path + '\n',
3193                                      ' U   ' + alpha_COPY_path + '\n']),
3194                                     [], 'merge', '-r2:5',
3195                                     sbox.repo_url + '/A/B/E/alpha',
3196                                     alpha_COPY_path)
3197
3198
3199  expected_alpha_status = wc.State(alpha_COPY_path, {
3200    ''        : Item(status='MM', wc_rev=5),
3201    })
3202
3203  svntest.actions.run_and_verify_status(alpha_COPY_path,
3204                                        expected_alpha_status)
3205
3206  svntest.actions.run_and_verify_svn(["/A/B/E/alpha:3-5\n"], [],
3207                                     'propget', SVN_PROP_MERGEINFO,
3208                                     alpha_COPY_path)
3209
3210  # Update WC.  The local mergeinfo (r3-5) on A/B_COPY/E/alpha is
3211  # identical to that on added to A/B_COPY by the update, but update
3212  # doesn't support elision so this redundancy is permitted.
3213  expected_output = wc.State(wc_dir, {
3214    'A/B_COPY/lambda'  : Item(status='U '),
3215    'A/B_COPY/E/alpha' : Item(status='G '),
3216    'A/B_COPY/E/beta'  : Item(status='U '),
3217    'A/B_COPY'         : Item(status=' U'),
3218    })
3219
3220  expected_disk.tweak('A/B_COPY', props={SVN_PROP_MERGEINFO : '/A/B:3-5'})
3221  expected_disk.tweak('A/B_COPY/lambda', contents="New content")
3222  expected_disk.tweak('A/B_COPY/E/beta', contents="New content")
3223  expected_disk.tweak('A/B_COPY/E/alpha', contents="New content",
3224                      props={SVN_PROP_MERGEINFO : '/A/B/E/alpha:3-5'})
3225
3226  expected_status.tweak(wc_rev=6)
3227  expected_status.tweak('A/B_COPY/E/alpha', status=' M')
3228
3229  svntest.actions.run_and_verify_update(wc_dir,
3230                                        expected_output,
3231                                        expected_disk,
3232                                        expected_status,
3233                                        [], True)
3234
3235  # Now test that an updated target's mergeinfo can itself elide.
3236  # r7 - modify and commit A/B/E/alpha
3237  svntest.main.file_write(alpha_path, "More new content")
3238  expected_output = wc.State(wc_dir, {
3239    'A/B/E/alpha' : Item(verb='Sending'),
3240    'A/B_COPY/E/alpha' : Item(verb='Sending')})
3241  expected_status.tweak('A/B/E/alpha', 'A/B_COPY/E/alpha', status='  ',
3242                        wc_rev=7)
3243  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3244                                        expected_status)
3245
3246  # Update A to get all paths to the same working revision.
3247  svntest.actions.run_and_verify_svn(exp_noop_up_out(7), [],
3248                                     'up', wc_dir)
3249
3250  # Merge r6:7 into A/B_COPY/E
3251  expected_output = wc.State(E_COPY_path, {
3252    'alpha' : Item(status='U '),
3253    })
3254
3255  expected_mergeinfo_output = wc.State(E_COPY_path, {
3256    ''      : Item(status=' G'),
3257    'alpha' : Item(status=' U'),
3258    })
3259
3260  expected_elision_output = wc.State(E_COPY_path, {
3261    'alpha' : Item(status=' U'),
3262    })
3263
3264  expected_merge_status = wc.State(E_COPY_path, {
3265    ''        : Item(status=' M', wc_rev=7),
3266    'alpha' : Item(status='MM', wc_rev=7),
3267    'beta'  : Item(status='  ', wc_rev=7),
3268    })
3269
3270  expected_merge_disk = wc.State('', {
3271    ''        : Item(props={SVN_PROP_MERGEINFO : '/A/B/E:3-5,7'}),
3272    'alpha' : Item("More new content"),
3273    'beta'  : Item("New content"),
3274    })
3275
3276  expected_skip = wc.State(E_COPY_path, { })
3277
3278  svntest.actions.run_and_verify_merge(E_COPY_path, '6', '7',
3279                                       sbox.repo_url + '/A/B/E', None,
3280                                       expected_output,
3281                                       expected_mergeinfo_output,
3282                                       expected_elision_output,
3283                                       expected_merge_disk,
3284                                       expected_merge_status,
3285                                       expected_skip,
3286                                       check_props=True)
3287
3288  # r8 - Commit the merge
3289  svntest.actions.run_and_verify_svn(exp_noop_up_out(7),
3290                                     [], 'update', wc_dir)
3291
3292  expected_output = wc.State(wc_dir,
3293                             {'A/B_COPY/E'       : Item(verb='Sending'),
3294                              'A/B_COPY/E/alpha' : Item(verb='Sending')})
3295
3296  expected_status.tweak(wc_rev=7)
3297  expected_status.tweak('A/B_COPY/E', 'A/B_COPY/E/alpha', wc_rev=8)
3298
3299  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3300                                        expected_status)
3301
3302  # Update A/COPY_B/E back to r7
3303  expected_output = wc.State(wc_dir, {
3304    'A/B_COPY/E/alpha' : Item(status='UU'),
3305    'A/B_COPY/E'       : Item(status=' U'),
3306    })
3307
3308  expected_status.tweak(wc_rev=7)
3309
3310  expected_disk.tweak('A/B_COPY',
3311                      props={SVN_PROP_MERGEINFO : '/A/B:3-5'})
3312  expected_disk.tweak('A/B/E/alpha', contents="More new content")
3313  expected_disk.tweak('A/B_COPY/E/alpha', contents="New content")
3314
3315  svntest.actions.run_and_verify_update(wc_dir,
3316                                        expected_output,
3317                                        expected_disk,
3318                                        expected_status,
3319                                        [], True,
3320                                        '-r', '7', E_COPY_path)
3321
3322  # Merge r6:7 to A/B_COPY
3323  expected_output = wc.State(B_COPY_path, {
3324    'E/alpha' : Item(status='U '),
3325    })
3326
3327  expected_mergeinfo_output = wc.State(B_COPY_path, {
3328    ''        : Item(status=' U'),
3329    'E/alpha' : Item(status=' U'),
3330    })
3331
3332  expected_elision_output = wc.State(B_COPY_path, {
3333    'E/alpha' : Item(status=' U'),
3334    })
3335
3336  expected_merge_status = wc.State(B_COPY_path, {
3337    ''        : Item(status=' M', wc_rev=7),
3338    'lambda'  : Item(status='  ', wc_rev=7),
3339    'E'       : Item(status='  ', wc_rev=7),
3340    'E/alpha' : Item(status='MM', wc_rev=7),
3341    'E/beta'  : Item(status='  ', wc_rev=7),
3342    'F'       : Item(status='  ', wc_rev=7),
3343    })
3344
3345  expected_merge_disk = wc.State('', {
3346    ''        : Item(props={SVN_PROP_MERGEINFO : '/A/B:3-5,7'}),
3347    'lambda'  : Item("New content"),
3348    'E'       : Item(),
3349    'E/alpha' : Item("More new content"),
3350    'E/beta'  : Item("New content"),
3351    'F'       : Item(),
3352    })
3353
3354  expected_skip = wc.State(B_COPY_path, { })
3355
3356  svntest.actions.run_and_verify_merge(B_COPY_path, '6', '7',
3357                                       sbox.repo_url + '/A/B', None,
3358                                       expected_output,
3359                                       expected_mergeinfo_output,
3360                                       expected_elision_output,
3361                                       expected_merge_disk,
3362                                       expected_merge_status,
3363                                       expected_skip,
3364                                       [], True, True)
3365
3366  # Update just A/B_COPY/E.  The mergeinfo (r3-5,7) reset on
3367  # A/B_COPY/E by the udpate is identical to the local info on
3368  # A/B_COPY, so should elide, leaving no mereginfo on E.
3369  expected_output = wc.State(wc_dir, {
3370    'A/B_COPY/E/alpha' : Item(status='GG'),
3371    'A/B_COPY/E/'      : Item(status=' U'),
3372    })
3373
3374  expected_status.tweak('A/B_COPY', status=' M', wc_rev=7)
3375  expected_status.tweak('A/B_COPY/E', status='  ', wc_rev=8)
3376  expected_status.tweak('A/B_COPY/E/alpha', wc_rev=8)
3377  expected_status.tweak('A/B_COPY/E/beta', wc_rev=8)
3378
3379  expected_disk.tweak('A/B_COPY',
3380                      props={SVN_PROP_MERGEINFO : '/A/B:3-5,7'})
3381  expected_disk.tweak('A/B_COPY/E',
3382                      props={SVN_PROP_MERGEINFO : '/A/B/E:3-5,7'})
3383  expected_disk.tweak('A/B_COPY/E/alpha', contents="More new content",
3384                      props={})
3385
3386  svntest.actions.run_and_verify_update(wc_dir,
3387                                        expected_output,
3388                                        expected_disk,
3389                                        expected_status,
3390                                        [], True,
3391                                        E_COPY_path)
3392
3393
3394#----------------------------------------------------------------------
3395# Very obscure bug: Issue #2977.
3396# Let's say there's a revision with
3397#   $ svn mv b c
3398#   $ svn mv a b
3399#   $ svn ci
3400# and a later revision that modifies b.  We then try a fresh checkout.  If
3401# the server happens to send us 'b' first, then when it later gets 'c'
3402# (with a copyfrom of 'b') it might try to use the 'b' in the wc as the
3403# copyfrom base.  This is wrong, because 'b' was changed later; however,
3404# due to a bug, the setting of svn:entry:committed-rev on 'b' is not being
3405# properly seen by the client, and it chooses the wrong base.  Corruption!
3406#
3407# Note that because this test depends on the order that the server sends
3408# changes, it is very fragile; even changing the file names can avoid
3409# triggering the bug.
3410
3411def update_copied_from_replaced_and_changed(sbox):
3412  "update chooses right copyfrom for double move"
3413
3414  sbox.build()
3415  wc_dir = sbox.wc_dir
3416
3417  fn1_relpath = 'A/B/E/aardvark'
3418  fn2_relpath = 'A/B/E/alpha'
3419  fn3_relpath = 'A/B/E/beta'
3420  fn1_path = sbox.ospath(fn1_relpath)
3421  fn2_path = sbox.ospath(fn2_relpath)
3422  fn3_path = sbox.ospath(fn3_relpath)
3423
3424  # Move fn2 to fn1
3425  svntest.actions.run_and_verify_svn(None, [],
3426                                     'mv', fn2_path, fn1_path)
3427
3428  # Move fn3 to fn2
3429  svntest.actions.run_and_verify_svn(None, [],
3430                                     'mv', fn3_path, fn2_path)
3431
3432  # Commit that change, creating r2.
3433  expected_output = svntest.wc.State(wc_dir, {
3434    fn1_relpath : Item(verb='Adding'),
3435    fn2_relpath : Item(verb='Replacing'),
3436    fn3_relpath : Item(verb='Deleting'),
3437    })
3438
3439  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3440  expected_status.remove(fn2_relpath, fn3_relpath)
3441  expected_status.add({
3442    fn1_relpath : Item(status='  ', wc_rev=2),
3443    fn2_relpath : Item(status='  ', wc_rev=2),
3444    })
3445
3446  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3447                                        expected_status)
3448
3449  # Modify fn2.
3450  fn2_final_contents = "I have new contents for the middle file."
3451  svntest.main.file_write(fn2_path, fn2_final_contents)
3452
3453  # Commit the changes, creating r3.
3454  expected_output = svntest.wc.State(wc_dir, {
3455    fn2_relpath : Item(verb='Sending'),
3456    })
3457
3458  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3459  expected_status.remove(fn2_relpath, fn3_relpath)
3460  expected_status.add({
3461    fn1_relpath : Item(status='  ', wc_rev=2),
3462    fn2_relpath : Item(status='  ', wc_rev=3),
3463    })
3464
3465  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3466                                        expected_status)
3467
3468  # Go back to r1.
3469  expected_output = svntest.wc.State(wc_dir, {
3470    fn1_relpath: Item(status='D '),
3471    fn2_relpath: Item(status='A ', prev_status='D '), # D then A
3472    fn3_relpath: Item(status='A '),
3473    })
3474
3475  # Create expected disk tree for the update to rev 0
3476  expected_disk = svntest.main.greek_state.copy()
3477
3478  # Do the update and check the results.
3479  svntest.actions.run_and_verify_update(wc_dir,
3480                                        expected_output,
3481                                        expected_disk,
3482                                        None,
3483                                        [], False,
3484                                        '-r', '1', wc_dir)
3485
3486  # And back up to 3 again.
3487  expected_output = svntest.wc.State(wc_dir, {
3488    fn1_relpath: Item(status='A '),
3489    fn2_relpath: Item(status='A ', prev_status='D '), # D then A
3490    fn3_relpath: Item(status='D '),
3491    })
3492
3493  # Create expected disk tree for the update to rev 0
3494  expected_disk = svntest.main.greek_state.copy()
3495  expected_disk.add({
3496    fn1_relpath : Item("This is the file 'alpha'.\n"),
3497    })
3498  expected_disk.tweak(fn2_relpath, contents=fn2_final_contents)
3499  expected_disk.remove(fn3_relpath)
3500
3501  # reuse old expected_status, but at r3
3502  expected_status.tweak(wc_rev=3)
3503
3504  svntest.actions.run_and_verify_update(wc_dir,
3505                                        expected_output,
3506                                        expected_disk,
3507                                        expected_status)
3508
3509#----------------------------------------------------------------------
3510# Regression test: ra_neon assumes that you never delete a property on
3511# a newly-added file, which is wrong if it's add-with-history.
3512def update_copied_and_deleted_prop(sbox):
3513  "updating a copied file with a deleted property"
3514
3515  sbox.build()
3516  wc_dir = sbox.wc_dir
3517  iota_path = sbox.ospath('iota')
3518  iota2_path = sbox.ospath('iota2')
3519
3520  # Add a property on iota
3521  svntest.actions.run_and_verify_svn(None, [],
3522                                     'propset', 'foo', 'bar', iota_path)
3523  # Commit that change, creating r2.
3524  expected_output = svntest.wc.State(wc_dir, {
3525    'iota' : Item(verb='Sending'),
3526    })
3527
3528  expected_status_mixed = svntest.actions.get_virginal_state(wc_dir, 1)
3529  expected_status_mixed.tweak('iota', wc_rev=2)
3530
3531  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3532                                        expected_status_mixed)
3533
3534  # Copy iota to iota2 and delete the property on it.
3535  svntest.actions.run_and_verify_svn(None, [],
3536                                     'copy', iota_path, iota2_path)
3537  svntest.actions.run_and_verify_svn(None, [],
3538                                     'propdel', 'foo', iota2_path)
3539
3540  # Commit that change, creating r3.
3541  expected_output = svntest.wc.State(wc_dir, {
3542    'iota2' : Item(verb='Adding'),
3543    })
3544
3545  expected_status_mixed.add({
3546    'iota2' : Item(status='  ', wc_rev=3),
3547    })
3548
3549  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3550                                        expected_status_mixed)
3551
3552  # Update the whole wc, verifying disk as well.
3553  expected_output = svntest.wc.State(wc_dir, { })
3554
3555  expected_disk_r3 = svntest.main.greek_state.copy()
3556  expected_disk_r3.add({
3557    'iota2' : Item("This is the file 'iota'.\n"),
3558    })
3559  expected_disk_r3.tweak('iota', props={'foo':'bar'})
3560
3561  expected_status_r3 = expected_status_mixed.copy()
3562  expected_status_r3.tweak(wc_rev=3)
3563
3564  svntest.actions.run_and_verify_update(wc_dir,
3565                                        expected_output,
3566                                        expected_disk_r3,
3567                                        expected_status_r3,
3568                                        check_props=True)
3569
3570  # Now go back to r2.
3571  expected_output = svntest.wc.State(wc_dir, {'iota2': Item(status='D ')})
3572
3573  expected_disk_r2 = expected_disk_r3.copy()
3574  expected_disk_r2.remove('iota2')
3575
3576  expected_status_r2 = expected_status_r3.copy()
3577  expected_status_r2.tweak(wc_rev=2)
3578  expected_status_r2.remove('iota2')
3579
3580  svntest.actions.run_and_verify_update(wc_dir,
3581                                        expected_output,
3582                                        expected_disk_r2,
3583                                        expected_status_r2,
3584                                        [], True,
3585                                        "-r2", wc_dir)
3586
3587  # And finally, back to r3, getting an add-with-history-and-property-deleted
3588  expected_output = svntest.wc.State(wc_dir, {'iota2': Item(status='A ')})
3589
3590  svntest.actions.run_and_verify_update(wc_dir,
3591                                        expected_output,
3592                                        expected_disk_r3,
3593                                        expected_status_r3,
3594                                        check_props=True)
3595
3596#----------------------------------------------------------------------
3597
3598def update_output_with_conflicts(rev, target, paths=None, resolved=False):
3599  """Return the expected output for an update of TARGET to revision REV, in
3600     which all of the PATHS are updated and conflicting.
3601
3602     If PATHS is None, it means [TARGET].  The output is a list of lines.
3603  """
3604  if paths is None:
3605    paths = [target]
3606
3607  lines = ["Updating '%s':\n" % target]
3608  for path in paths:
3609    lines += ['C    %s\n' % path]
3610  lines += ['Updated to revision %d.\n' % rev]
3611  if resolved:
3612    for path in paths:
3613      lines += ["Merge conflicts in '%s' marked as resolved.\n" % path]
3614    lines += svntest.main.summary_of_conflicts(text_resolved=len(paths))
3615  else:
3616    lines += svntest.main.summary_of_conflicts(text_conflicts=len(paths))
3617  return lines
3618
3619def update_output_with_conflicts_resolved(rev, target, paths=None):
3620  """Like update_output_with_conflicts(), but where all of the conflicts are
3621     resolved within the update.
3622  """
3623  lines = update_output_with_conflicts(rev, target, paths, resolved=True)
3624  return lines
3625
3626#----------------------------------------------------------------------
3627
3628def update_accept_conflicts(sbox):
3629  "update --accept automatic conflict resolution"
3630
3631  sbox.build()
3632  wc_dir = sbox.wc_dir
3633
3634  # Make a few local mods to files which will be committed
3635  iota_path = sbox.ospath('iota')
3636  lambda_path = sbox.ospath('A/B/lambda')
3637  mu_path = sbox.ospath('A/mu')
3638  alpha_path = sbox.ospath('A/B/E/alpha')
3639  beta_path = sbox.ospath('A/B/E/beta')
3640  pi_path = sbox.ospath('A/D/G/pi')
3641  p_i_path = sbox.ospath('A/D/G/p; i')
3642  rho_path = sbox.ospath('A/D/G/rho')
3643
3644  # Rename pi to "p; i" so we can exercise SVN_EDITOR's handling of paths with
3645  # special characters
3646  sbox.simple_move('A/D/G/pi', 'A/D/G/p; i')
3647  sbox.simple_commit()
3648  sbox.simple_update()
3649
3650  # Make a backup copy of the working copy
3651  wc_backup = sbox.add_wc_path('backup')
3652  svntest.actions.duplicate_dir(wc_dir, wc_backup)
3653
3654  svntest.main.file_append(lambda_path, 'Their appended text for lambda\n')
3655  svntest.main.file_append(iota_path, 'Their appended text for iota\n')
3656  svntest.main.file_append(mu_path, 'Their appended text for mu\n')
3657  svntest.main.file_append(alpha_path, 'Their appended text for alpha\n')
3658  svntest.main.file_append(beta_path, 'Their appended text for beta\n')
3659  svntest.main.file_append(p_i_path, 'Their appended text for pi\n')
3660  svntest.main.file_append(rho_path, 'Their appended text for rho\n')
3661
3662  # Make a few local mods to files which will be conflicted
3663  iota_path_backup = os.path.join(wc_backup, 'iota')
3664  lambda_path_backup = os.path.join(wc_backup, 'A', 'B', 'lambda')
3665  mu_path_backup = os.path.join(wc_backup, 'A', 'mu')
3666  alpha_path_backup = os.path.join(wc_backup, 'A', 'B', 'E', 'alpha')
3667  beta_path_backup = os.path.join(wc_backup, 'A', 'B', 'E', 'beta')
3668  p_i_path_backup = os.path.join(wc_backup, 'A', 'D', 'G', 'p; i')
3669  rho_path_backup = os.path.join(wc_backup, 'A', 'D', 'G', 'rho')
3670  svntest.main.file_append(iota_path_backup,
3671                           'My appended text for iota\n')
3672  svntest.main.file_append(lambda_path_backup,
3673                           'My appended text for lambda\n')
3674  svntest.main.file_append(mu_path_backup,
3675                           'My appended text for mu\n')
3676  svntest.main.file_append(alpha_path_backup,
3677                           'My appended text for alpha\n')
3678  svntest.main.file_append(beta_path_backup,
3679                           'My appended text for beta\n')
3680  svntest.main.file_append(p_i_path_backup,
3681                           'My appended text for pi\n')
3682  svntest.main.file_append(rho_path_backup,
3683                           'My appended text for rho\n')
3684
3685  # Created expected output tree for 'svn ci'
3686  expected_output = svntest.wc.State(wc_dir, {
3687    'iota' : Item(verb='Sending'),
3688    'A/B/lambda' : Item(verb='Sending'),
3689    'A/mu' : Item(verb='Sending'),
3690    'A/B/E/alpha': Item(verb='Sending'),
3691    'A/B/E/beta': Item(verb='Sending'),
3692    'A/D/G/p; i' : Item(verb='Sending'),
3693    'A/D/G/rho' : Item(verb='Sending'),
3694    })
3695
3696  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
3697  expected_status.tweak('iota', wc_rev=3)
3698  expected_status.tweak('A/B/lambda', wc_rev=3)
3699  expected_status.tweak('A/mu', wc_rev=3)
3700  expected_status.tweak('A/B/E/alpha', wc_rev=3)
3701  expected_status.tweak('A/B/E/beta', wc_rev=3)
3702  expected_status.rename({'A/D/G/pi': 'A/D/G/p; i'})
3703  expected_status.tweak('A/D/G/p; i', wc_rev=3)
3704  expected_status.tweak('A/D/G/rho', wc_rev=3)
3705
3706  # Commit.
3707  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3708                                        expected_status)
3709
3710  # Now we'll update each of our 5 files in wc_backup; each one will get
3711  # conflicts, and we'll handle each with a different --accept option.
3712
3713  # Setup SVN_EDITOR and SVN_MERGE for --accept={edit,launch}.
3714  svntest.main.use_editor('append_foo')
3715
3716  # iota: no accept option
3717  # Just leave the conflicts alone, since run_and_verify_svn already uses
3718  # the --non-interactive option.
3719  svntest.actions.run_and_verify_svn(update_output_with_conflicts(
3720                                       3, iota_path_backup),
3721                                     [],
3722                                     'update', iota_path_backup)
3723
3724  # lambda: --accept=postpone
3725  # Just leave the conflicts alone.
3726  svntest.actions.run_and_verify_svn(update_output_with_conflicts(
3727                                       3, lambda_path_backup),
3728                                     [],
3729                                     'update', '--accept=postpone',
3730                                     lambda_path_backup)
3731
3732  # mu: --accept=base
3733  # Accept the pre-update base file.
3734  svntest.actions.run_and_verify_svn(update_output_with_conflicts_resolved(
3735                                       3, mu_path_backup),
3736                                     [],
3737                                     'update', '--accept=base',
3738                                     mu_path_backup)
3739
3740  # alpha: --accept=mine
3741  # Accept the user's working file.
3742  svntest.actions.run_and_verify_svn(update_output_with_conflicts_resolved(
3743                                       3, alpha_path_backup),
3744                                     [],
3745                                     'update', '--accept=mine-full',
3746                                     alpha_path_backup)
3747
3748  # beta: --accept=theirs
3749  # Accept their file.
3750  svntest.actions.run_and_verify_svn(update_output_with_conflicts_resolved(
3751                                       3, beta_path_backup),
3752                                     [],
3753                                     'update', '--accept=theirs-full',
3754                                     beta_path_backup)
3755
3756  # pi: --accept=edit
3757  # Run editor and accept the edited file. The merge tool will leave
3758  # conflicts in place, so expect a message on stderr, but expect
3759  # svn to exit with an exit code of 0.
3760  svntest.actions.run_and_verify_svn2(update_output_with_conflicts_resolved(
3761                                        3, p_i_path_backup),
3762                                      "system(.*) returned.*", 0,
3763                                      'update', '--accept=edit',
3764                                      '--force-interactive',
3765                                      p_i_path_backup)
3766
3767  # rho: --accept=launch
3768  # Run the external merge tool, it should leave conflict markers in place.
3769  svntest.actions.run_and_verify_svn(update_output_with_conflicts(
3770                                       3, rho_path_backup),
3771                                     [],
3772                                     'update', '--accept=launch',
3773                                     '--force-interactive',
3774                                     rho_path_backup)
3775
3776  # Set the expected disk contents for the test
3777  expected_disk = svntest.main.greek_state.copy()
3778
3779  expected_disk.tweak('iota', contents=("This is the file 'iota'.\n"
3780                                        '<<<<<<< .mine\n'
3781                                        'My appended text for iota\n'
3782                                        '||||||| .r2\n'
3783                                        '=======\n'
3784                                        'Their appended text for iota\n'
3785                                        '>>>>>>> .r3\n'))
3786  expected_disk.tweak('A/B/lambda', contents=("This is the file 'lambda'.\n"
3787                                              '<<<<<<< .mine\n'
3788                                              'My appended text for lambda\n'
3789                                              '||||||| .r2\n'
3790                                              '=======\n'
3791                                              'Their appended text for lambda\n'
3792                                              '>>>>>>> .r3\n'))
3793  expected_disk.tweak('A/mu', contents="This is the file 'mu'.\n")
3794  expected_disk.tweak('A/B/E/alpha', contents=("This is the file 'alpha'.\n"
3795                                               'My appended text for alpha\n'))
3796  expected_disk.tweak('A/B/E/beta', contents=("This is the file 'beta'.\n"
3797                                              'Their appended text for beta\n'))
3798  expected_disk.rename({'A/D/G/pi': 'A/D/G/p; i'})
3799  expected_disk.tweak('A/D/G/p; i', contents=("This is the file 'pi'.\n"
3800                                              '<<<<<<< .mine\n'
3801                                              'My appended text for pi\n'
3802                                              '||||||| .r2\n'
3803                                              '=======\n'
3804                                              'Their appended text for pi\n'
3805                                              '>>>>>>> .r3\n'
3806                                              'foo\n'))
3807  expected_disk.tweak('A/D/G/rho', contents=("This is the file 'rho'.\n"
3808                                             '<<<<<<< .mine\n'
3809                                             'My appended text for rho\n'
3810                                             '||||||| .r2\n'
3811                                             '=======\n'
3812                                             'Their appended text for rho\n'
3813                                             '>>>>>>> .r3\n'
3814                                             'foo\n'))
3815
3816  # Set the expected extra files for the test
3817  extra_files = ['iota.*\.r2', 'iota.*\.r3', 'iota.*\.mine',
3818                 'lambda.*\.r2', 'lambda.*\.r3', 'lambda.*\.mine',
3819                 'rho.*\.r2', 'rho.*\.r3', 'rho.*\.mine']
3820
3821  # Set the expected status for the test
3822  expected_status = svntest.actions.get_virginal_state(wc_backup, 3)
3823  expected_status.rename({'A/D/G/pi': 'A/D/G/p; i'})
3824  expected_status.tweak('iota', 'A/B/lambda', 'A/mu',
3825                        'A/B/E/alpha', 'A/B/E/beta',
3826                        'A/D/G/p; i', 'A/D/G/rho', wc_rev=3)
3827  expected_status.tweak('iota', status='C ')
3828  expected_status.tweak('A/B/lambda', status='C ')
3829  expected_status.tweak('A/mu', status='M ')
3830  expected_status.tweak('A/B/E/alpha', status='M ')
3831  expected_status.tweak('A/B/E/beta', status='  ')
3832  expected_status.tweak('A/D/G/p; i', status='M ')
3833  expected_status.tweak('A/D/G/rho', status='C ')
3834
3835  # Set the expected output for the test
3836  expected_output = wc.State(wc_backup, {})
3837
3838  # Do the update and check the results in three ways.
3839  svntest.actions.run_and_verify_update(wc_backup,
3840                                        expected_output,
3841                                        expected_disk,
3842                                        expected_status,
3843                                        extra_files=extra_files)
3844
3845
3846#----------------------------------------------------------------------
3847
3848
3849def update_uuid_changed(sbox):
3850  "update fails when repos uuid changed"
3851
3852  # read_only=False, since we don't want to run setuuid on the (shared)
3853  # pristine repository.
3854  sbox.build(read_only = False)
3855
3856  wc_dir = sbox.wc_dir
3857  repo_dir = sbox.repo_dir
3858
3859  uuid_before = svntest.actions.get_wc_uuid(wc_dir)
3860
3861  # Change repository's uuid.
3862  svntest.actions.run_and_verify_svnadmin(None, [],
3863                                          'setuuid', repo_dir)
3864
3865  # 'update' detected the new uuid...
3866  svntest.actions.run_and_verify_svn(None, '.*UUID.*',
3867                                     'update', wc_dir)
3868
3869  # ...and didn't overwrite the old uuid.
3870  uuid_after = svntest.actions.get_wc_uuid(wc_dir)
3871  if uuid_before != uuid_after:
3872    raise svntest.Failure
3873
3874
3875#----------------------------------------------------------------------
3876
3877# Issue #1672: if an update deleting a dir prop is interrupted (by a
3878# local obstruction, for example) then restarting the update will not
3879# delete the prop, causing the wc to become out of sync with the
3880# repository.
3881def restarted_update_should_delete_dir_prop(sbox):
3882  "restarted update should delete dir prop"
3883  sbox.build()
3884  wc_dir = sbox.wc_dir
3885
3886  A_path = sbox.ospath('A')
3887  zeta_path = os.path.join(A_path, 'zeta')
3888
3889  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
3890
3891  # Commit a propset on A.
3892  svntest.main.run_svn(None, 'propset', 'prop', 'val', A_path)
3893
3894  expected_output = svntest.wc.State(wc_dir, {
3895    'A': Item(verb='Sending'),
3896    })
3897
3898  expected_status.tweak('A', wc_rev=2)
3899
3900  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
3901                                        expected_status)
3902
3903  # Create a second working copy.
3904  ### Does this hack still work with wc-ng?
3905  other_wc = sbox.add_wc_path('other')
3906  svntest.actions.duplicate_dir(wc_dir, other_wc)
3907
3908  other_A_path = os.path.join(other_wc, 'A')
3909  other_zeta_path = os.path.join(other_wc, 'A', 'zeta')
3910
3911  # In the second working copy, delete A's prop and add a new file.
3912  svntest.main.run_svn(None, 'propdel', 'prop', other_A_path)
3913  svntest.main.file_write(other_zeta_path, 'New file\n')
3914  svntest.main.run_svn(None, 'add', other_zeta_path)
3915
3916  expected_output = svntest.wc.State(other_wc, {
3917    'A': Item(verb='Sending'),
3918    'A/zeta' : Item(verb='Adding'),
3919    })
3920
3921  expected_status = svntest.actions.get_virginal_state(other_wc, 1)
3922  expected_status.tweak('A', wc_rev=3)
3923  expected_status.add({
3924    'A/zeta' : Item(status='  ', wc_rev=3),
3925    })
3926
3927  svntest.actions.run_and_verify_commit(other_wc, expected_output,
3928                                        expected_status)
3929
3930  # Back in the first working copy, create an obstructing path and
3931  # update. The update will flag a tree conflict.
3932  svntest.main.file_write(zeta_path, 'Obstructing file\n')
3933
3934  #svntest.factory.make(sbox, 'svn up')
3935  #exit(0)
3936  # svn up
3937  expected_output = svntest.wc.State(wc_dir, {
3938    'A'                 : Item(status=' U'),
3939    'A/zeta'            : Item(status='  ', treeconflict='C'),
3940  })
3941
3942  expected_disk = svntest.main.greek_state.copy()
3943  expected_disk.add({
3944    'A/zeta'            : Item(contents="Obstructing file\n"),
3945  })
3946
3947  expected_status = actions.get_virginal_state(wc_dir, 3)
3948  expected_status.add({
3949    'A/zeta'            : Item(status='D ', treeconflict='C', wc_rev='3'),
3950  })
3951
3952  actions.run_and_verify_update(wc_dir, expected_output, expected_disk,
3953                                expected_status)
3954
3955  # Now, delete the obstructing path and rerun the update.
3956  os.unlink(zeta_path)
3957
3958  svntest.main.run_svn(None, 'revert', zeta_path)
3959
3960  expected_output = svntest.wc.State(wc_dir, {
3961    })
3962
3963  expected_disk = svntest.main.greek_state.copy()
3964  expected_disk.tweak('A', props = {})
3965  expected_disk.add({
3966    'A/zeta' : Item("New file\n"),
3967    })
3968
3969  expected_status = svntest.actions.get_virginal_state(wc_dir, 3)
3970  expected_status.add({
3971    'A/zeta' : Item(status='  ', wc_rev=3),
3972    })
3973
3974  svntest.actions.run_and_verify_update(wc_dir,
3975                                        expected_output,
3976                                        expected_disk,
3977                                        expected_status,
3978                                        check_props = True)
3979
3980#----------------------------------------------------------------------
3981
3982# Detect tree conflicts among files and directories,
3983# edited or deleted in a deep directory structure.
3984#
3985# See use cases 1-3 in notes/tree-conflicts/use-cases.txt for background.
3986
3987# convenience definitions
3988leaf_edit = svntest.deeptrees.deep_trees_leaf_edit
3989tree_del = svntest.deeptrees.deep_trees_tree_del
3990leaf_del = svntest.deeptrees.deep_trees_leaf_del
3991
3992disk_after_leaf_edit = svntest.deeptrees.deep_trees_after_leaf_edit
3993disk_after_leaf_del = svntest.deeptrees.deep_trees_after_leaf_del
3994disk_after_tree_del = svntest.deeptrees.deep_trees_after_tree_del
3995
3996deep_trees_conflict_output = svntest.deeptrees.deep_trees_conflict_output
3997deep_trees_conflict_output_skipped = \
3998    svntest.deeptrees.deep_trees_conflict_output_skipped
3999deep_trees_status_local_tree_del = \
4000    svntest.deeptrees.deep_trees_status_local_tree_del
4001deep_trees_status_local_leaf_edit = \
4002    svntest.deeptrees.deep_trees_status_local_leaf_edit
4003
4004DeepTreesTestCase = svntest.deeptrees.DeepTreesTestCase
4005
4006
4007def tree_conflicts_on_update_1_1(sbox):
4008  "tree conflicts 1.1: tree del, leaf edit on update"
4009
4010  # use case 1, as in notes/tree-conflicts/use-cases.txt
4011  # 1.1) local tree delete, incoming leaf edit
4012
4013  sbox.build()
4014
4015  expected_output = deep_trees_conflict_output.copy()
4016  expected_output.add({
4017    'DDF/D1/D2'         : Item(status='  ', treeconflict='U'),
4018    'DDF/D1/D2/gamma'   : Item(status='  ', treeconflict='U'),
4019    'DD/D1/D2'          : Item(status='  ', treeconflict='U'),
4020    'DD/D1/D2/epsilon'  : Item(status='  ', treeconflict='A'),
4021    'DDD/D1/D2'         : Item(status='  ', treeconflict='U'),
4022    'DDD/D1/D2/D3'      : Item(status='  ', treeconflict='U'),
4023    'DDD/D1/D2/D3/zeta' : Item(status='  ', treeconflict='A'),
4024    'D/D1/delta'        : Item(status='  ', treeconflict='A'),
4025    'DF/D1/beta'        : Item(status='  ', treeconflict='U'),
4026  })
4027
4028  expected_disk = svntest.wc.State('', {
4029    'F'               : Item(),
4030    'D'               : Item(),
4031    'DF'              : Item(),
4032    'DD'              : Item(),
4033    'DDF'             : Item(),
4034    'DDD'             : Item(),
4035  })
4036  # The files delta, epsilon, and zeta are incoming additions, but since
4037  # they are all within locally deleted trees they should also be schedule
4038  # for deletion.
4039  expected_status = deep_trees_status_local_tree_del.copy()
4040  expected_status.add({
4041    'D/D1/delta'        : Item(status='D '),
4042    'DD/D1/D2/epsilon'  : Item(status='D '),
4043    'DDD/D1/D2/D3/zeta' : Item(status='D '),
4044    })
4045
4046  # Update to the target rev.
4047  expected_status.tweak(wc_rev=3)
4048
4049  expected_info = {
4050    'F/alpha' : {
4051      'Tree conflict' :
4052        '^local file delete, incoming file edit upon update'
4053        + ' Source  left: .file.*/F/alpha@2'
4054        + ' Source right: .file.*/F/alpha@3$',
4055    },
4056    'DF/D1' : {
4057      'Tree conflict' :
4058        '^local dir delete, incoming dir edit upon update'
4059        + ' Source  left: .dir.*/DF/D1@2'
4060        + ' Source right: .dir.*/DF/D1@3$',
4061    },
4062    'DDF/D1' : {
4063      'Tree conflict' :
4064        '^local dir delete, incoming dir edit upon update'
4065        + ' Source  left: .dir.*/DDF/D1@2'
4066        + ' Source right: .dir.*/DDF/D1@3$',
4067    },
4068    'D/D1' : {
4069      'Tree conflict' :
4070        '^local dir delete, incoming dir edit upon update'
4071        + ' Source  left: .dir.*/D/D1@2'
4072        + ' Source right: .dir.*/D/D1@3$',
4073    },
4074    'DD/D1' : {
4075      'Tree conflict' :
4076        '^local dir delete, incoming dir edit upon update'
4077        + ' Source  left: .dir.*/DD/D1@2'
4078        + ' Source right: .dir.*/DD/D1@3$',
4079    },
4080    'DDD/D1' : {
4081      'Tree conflict' :
4082        '^local dir delete, incoming dir edit upon update'
4083        + ' Source  left: .dir.*/DDD/D1@2'
4084        + ' Source right: .dir.*/DDD/D1@3$',
4085    },
4086  }
4087
4088  svntest.deeptrees.deep_trees_run_tests_scheme_for_update(sbox,
4089    [ DeepTreesTestCase("local_tree_del_incoming_leaf_edit",
4090                        tree_del,
4091                        leaf_edit,
4092                        expected_output,
4093                        expected_disk,
4094                        expected_status,
4095                        expected_info = expected_info) ] )
4096
4097
4098def tree_conflicts_on_update_1_2(sbox):
4099  "tree conflicts 1.2: tree del, leaf del on update"
4100
4101  # 1.2) local tree delete, incoming leaf delete
4102
4103  sbox.build()
4104
4105  expected_output = deep_trees_conflict_output.copy()
4106  expected_output.add({
4107    'DDD/D1/D2'         : Item(status='  ', treeconflict='U'),
4108    'DDD/D1/D2/D3'      : Item(status='  ', treeconflict='D'),
4109    'DF/D1/beta'        : Item(status='  ', treeconflict='D'),
4110    'DD/D1/D2'          : Item(status='  ', treeconflict='D'),
4111    'DDF/D1/D2'         : Item(status='  ', treeconflict='U'),
4112    'DDF/D1/D2/gamma'   : Item(status='  ', treeconflict='D'),
4113  })
4114
4115  expected_disk = svntest.wc.State('', {
4116    'F'               : Item(),
4117    'D'               : Item(),
4118    'DF'              : Item(),
4119    'DD'              : Item(),
4120    'DDF'             : Item(),
4121    'DDD'             : Item(),
4122  })
4123
4124  expected_status = deep_trees_status_local_tree_del.copy()
4125
4126  # Expect the incoming leaf deletes to actually occur.  Even though they
4127  # are within (or in the case of F/alpha and D/D1 are the same as) the
4128  # trees locally scheduled for deletion we must still delete them and
4129  # update the scheduled for deletion items to the target rev.  Otherwise
4130  # once the conflicts are resolved we still have a mixed-rev WC we can't
4131  # commit without updating...which, you guessed it, raises tree conflicts
4132  # again, repeat ad infinitum - see issue #3334.
4133  #
4134  # Update to the target rev.
4135  expected_status.tweak(wc_rev=3)
4136  expected_status.tweak('F/alpha',
4137                        'D/D1',
4138                        status='! ', wc_rev=None)
4139  # Remove the incoming deletes from status and disk.
4140  expected_status.remove('DD/D1/D2',
4141                         'DDD/D1/D2/D3',
4142                         'DDF/D1/D2/gamma',
4143                         'DF/D1/beta')
4144
4145  expected_info = {
4146    'F/alpha' : {
4147      'Tree conflict' :
4148        '^local file delete, incoming file delete or move upon update'
4149        + ' Source  left: .file.*/F/alpha@2'
4150        + ' Source right: .none.*(/F/alpha@3)?$',
4151    },
4152    'DF/D1' : {
4153      'Tree conflict' :
4154        '^local dir delete, incoming dir edit upon update'
4155        + ' Source  left: .dir.*/DF/D1@2'
4156        + ' Source right: .dir.*/DF/D1@3$',
4157    },
4158    'DDF/D1' : {
4159      'Tree conflict' :
4160        '^local dir delete, incoming dir edit upon update'
4161        + ' Source  left: .dir.*/DDF/D1@2'
4162        + ' Source right: .dir.*/DDF/D1@3$',
4163    },
4164    'D/D1' : {
4165      'Tree conflict' :
4166        '^local dir delete, incoming dir delete or move upon update'
4167        + ' Source  left: .dir.*/D/D1@2'
4168        + ' Source right: .none.*(/D/D1@3)?$',
4169    },
4170    'DD/D1' : {
4171      'Tree conflict' :
4172        '^local dir delete, incoming dir edit upon update'
4173        + ' Source  left: .dir.*/DD/D1@2'
4174        + ' Source right: .dir.*/DD/D1@3$',
4175    },
4176    'DDD/D1' : {
4177      'Tree conflict' :
4178        '^local dir delete, incoming dir edit upon update'
4179        + ' Source  left: .dir.*/DDD/D1@2'
4180        + ' Source right: .dir.*/DDD/D1@3$',
4181    },
4182  }
4183
4184  svntest.deeptrees.deep_trees_run_tests_scheme_for_update(sbox,
4185    [ DeepTreesTestCase("local_tree_del_incoming_leaf_del",
4186                        tree_del,
4187                        leaf_del,
4188                        expected_output,
4189                        expected_disk,
4190                        expected_status,
4191                        expected_info = expected_info) ] )
4192
4193
4194def tree_conflicts_on_update_2_1(sbox):
4195  "tree conflicts 2.1: leaf edit, tree del on update"
4196
4197  # use case 2, as in notes/tree-conflicts/use-cases.txt
4198  # 2.1) local leaf edit, incoming tree delete
4199
4200  expected_output = deep_trees_conflict_output
4201
4202  expected_disk = disk_after_leaf_edit
4203
4204  expected_status = deep_trees_status_local_leaf_edit.copy()
4205  # Adjust the status of the roots of the six subtrees scheduled for deletion
4206  # during the update.  Since these are all tree conflicts, they will all be
4207  # scheduled for addition as copies with history - see Issue #3334.
4208  expected_status.tweak(
4209    'D/D1',
4210    'F/alpha',
4211    'DD/D1',
4212    'DF/D1',
4213    'DDD/D1',
4214    'DDF/D1',
4215    status='A ', copied='+', wc_rev='-')
4216  # See the status of all the paths *under* the above six subtrees.  Only the
4217  # roots of the added subtrees show as schedule 'A', these childs paths show
4218  # only that history is scheduled with the commit.
4219  expected_status.tweak(
4220    'DD/D1/D2',
4221    'DDD/D1/D2',
4222    'DDD/D1/D2/D3',
4223    'DF/D1/beta',
4224    'DDF/D1/D2',
4225    'DDF/D1/D2/gamma',
4226    copied='+', wc_rev='-')
4227
4228  expected_info = {
4229    'F/alpha' : {
4230      'Tree conflict' :
4231        '^local file edit, incoming file delete or move upon update'
4232        + ' Source  left: .file.*/F/alpha@2'
4233        + ' Source right: .none.*(/F/alpha@3)?$',
4234    },
4235    'DF/D1' : {
4236      'Tree conflict' :
4237        '^local dir edit, incoming dir delete or move upon update'
4238        + ' Source  left: .dir.*/DF/D1@2'
4239        + ' Source right: .none.*(/DF/D1@3)?$',
4240    },
4241    'DDF/D1' : {
4242      'Tree conflict' :
4243        '^local dir edit, incoming dir delete or move upon update'
4244        + ' Source  left: .dir.*/DDF/D1@2'
4245        + ' Source right: .none.*(/DDF/D1@3)?$',
4246    },
4247    'D/D1' : {
4248      'Tree conflict' :
4249        '^local dir edit, incoming dir delete or move upon update'
4250        + ' Source  left: .dir.*/D/D1@2'
4251        + ' Source right: .none.*(/D/D1@3)?$',
4252    },
4253    'DD/D1' : {
4254      'Tree conflict' :
4255        '^local dir edit, incoming dir delete or move upon update'
4256        + ' Source  left: .dir.*/DD/D1@2'
4257        + ' Source right: .none.*(/DD/D1@3)?$',
4258    },
4259    'DDD/D1' : {
4260      'Tree conflict' :
4261        '^local dir edit, incoming dir delete or move upon update'
4262        + ' Source  left: .dir.*/DDD/D1@2'
4263        + ' Source right: .none.*(/DDD/D1@3)?$',
4264    },
4265  }
4266
4267  ### D/D1/delta is locally-added during leaf_edit. when tree_del executes,
4268  ### it will delete D/D1, and the update reschedules local D/D1 for
4269  ### local-copy from its original revision. however, right now, we cannot
4270  ### denote that delta is a local-add rather than a child of that D/D1 copy.
4271  ### thus, it appears in the status output as a (M)odified child.
4272  svntest.deeptrees.deep_trees_run_tests_scheme_for_update(sbox,
4273    [ DeepTreesTestCase("local_leaf_edit_incoming_tree_del",
4274                        leaf_edit,
4275                        tree_del,
4276                        expected_output,
4277                        expected_disk,
4278                        expected_status,
4279                        expected_info = expected_info) ] )
4280
4281
4282
4283def tree_conflicts_on_update_2_2(sbox):
4284  "tree conflicts 2.2: leaf del, tree del on update"
4285
4286  # 2.2) local leaf delete, incoming tree delete
4287
4288  ### Current behaviour fails to show conflicts when deleting
4289  ### a directory tree that has modifications. (Will be solved
4290  ### when dirs_same_p() is implemented)
4291  expected_output = deep_trees_conflict_output
4292
4293  expected_disk = svntest.wc.State('', {
4294    'DDF/D1/D2'       : Item(),
4295    'F'               : Item(),
4296    'D'               : Item(),
4297    'DF/D1'           : Item(),
4298    'DD/D1'           : Item(),
4299    'DDD/D1/D2'       : Item(),
4300  })
4301
4302  expected_status = svntest.deeptrees.deep_trees_virginal_state.copy()
4303  expected_status.add({'' : Item()})
4304  expected_status.tweak(contents=None, status='  ', wc_rev=3)
4305  # Tree conflicts.
4306  expected_status.tweak(
4307    'D/D1',
4308    'F/alpha',
4309    'DD/D1',
4310    'DF/D1',
4311    'DDD/D1',
4312    'DDF/D1',
4313    treeconflict='C', wc_rev=2)
4314
4315  # Expect the incoming tree deletes and the local leaf deletes to mean
4316  # that all deleted paths are *really* gone, not simply scheduled for
4317  # deletion.
4318  expected_status.tweak('DD/D1', 'DF/D1', 'DDF/D1', 'DDD/D1',
4319                        status='A ', copied='+', treeconflict='C',
4320                        wc_rev='-')
4321  expected_status.tweak('DDF/D1/D2', 'DDD/D1/D2',
4322                        copied='+', wc_rev='-')
4323  expected_status.tweak('DD/D1/D2',  'DF/D1/beta', 'DDD/D1/D2/D3',
4324                        'DDF/D1/D2/gamma',
4325                        status='D ', copied='+', wc_rev='-')
4326  expected_status.tweak('F/alpha', 'D/D1',
4327                        status='! ', treeconflict='C', wc_rev=None)
4328
4329  expected_info = {
4330    'F/alpha' : {
4331      'Tree conflict' :
4332        '^local file delete, incoming file delete or move upon update'
4333        + ' Source  left: .file.*/F/alpha@2'
4334        + ' Source right: .none.*(/F/alpha@3)?$',
4335    },
4336    'DF/D1' : {
4337      'Tree conflict' :
4338        '^local dir edit, incoming dir delete or move upon update'
4339        + ' Source  left: .dir.*/DF/D1@2'
4340        + ' Source right: .none.*(/DF/D1@3)?$',
4341    },
4342    'DDF/D1' : {
4343      'Tree conflict' :
4344        '^local dir edit, incoming dir delete or move upon update'
4345        + ' Source  left: .dir.*/DDF/D1@2'
4346        + ' Source right: .none.*(/DDF/D1@3)?$',
4347    },
4348    'D/D1' : {
4349      'Tree conflict' :
4350        '^local dir delete, incoming dir delete or move upon update'
4351        + ' Source  left: .dir.*/D/D1@2'
4352        + ' Source right: .none.*(/D/D1@3)?$',
4353    },
4354    'DD/D1' : {
4355      'Tree conflict' :
4356        '^local dir edit, incoming dir delete or move upon update'
4357        + ' Source  left: .dir.*/DD/D1@2'
4358        + ' Source right: .none.*(/DD/D1@3)?$',
4359    },
4360    'DDD/D1' : {
4361      'Tree conflict' :
4362        '^local dir edit, incoming dir delete or move upon update'
4363        + ' Source  left: .dir.*/DDD/D1@2'
4364        + ' Source right: .none.*(/DDD/D1@3)?$',
4365    },
4366  }
4367
4368  svntest.deeptrees.deep_trees_run_tests_scheme_for_update(sbox,
4369    [ DeepTreesTestCase("local_leaf_del_incoming_tree_del",
4370                        leaf_del,
4371                        tree_del,
4372                        expected_output,
4373                        expected_disk,
4374                        expected_status,
4375                        expected_info = expected_info) ] )
4376
4377
4378#----------------------------------------------------------------------
4379# Test for issue #3329 'Update throws error when skipping some tree
4380# conflicts'
4381#
4382# Marked as XFail until issue #3329 is resolved.
4383@Issue(3329)
4384def tree_conflicts_on_update_2_3(sbox):
4385  "tree conflicts 2.3: skip on 2nd update"
4386
4387  # Test that existing tree conflicts are skipped
4388
4389  expected_output = deep_trees_conflict_output_skipped
4390
4391  expected_disk = disk_after_leaf_edit
4392
4393  expected_status = deep_trees_status_local_leaf_edit.copy()
4394
4395  # Adjust the status of the roots of the six subtrees scheduled for deletion
4396  # during the update.  Since these are all tree conflicts, they will all be
4397  # scheduled for addition as copies with history - see Issue #3334.
4398  expected_status.tweak(
4399    'D/D1',
4400    'F/alpha',
4401    'DD/D1',
4402    'DF/D1',
4403    'DDD/D1',
4404    'DDF/D1',
4405    status='A ', copied='+', wc_rev='-')
4406  # See the status of all the paths *under* the above six subtrees.  Only the
4407  # roots of the added subtrees show as schedule 'A', these child paths show
4408  # only that history is scheduled with the commit.
4409  expected_status.tweak(
4410    'DD/D1/D2',
4411    'DDD/D1/D2',
4412    'DDD/D1/D2/D3',
4413    'DF/D1/beta',
4414    'DDF/D1/D2',
4415    'DDF/D1/D2/gamma',
4416    copied='+', wc_rev='-')
4417
4418  # Paths where output should be a single 'Skipped' message.
4419  skip_paths = [
4420    'D/D1',
4421    'F/alpha',
4422    'DDD/D1',
4423    'DDD/D1/D2/D3',
4424    ]
4425
4426  # This is where the test fails.  Repeat updates on '', 'D', 'F', or
4427  # 'DDD' report no skips.
4428  chdir_skip_paths = [
4429    ('D', 'D1'),
4430    ('F', 'alpha'),
4431    ('DDD', 'D1'),
4432    ('', ['D/D1', 'F/alpha', 'DD/D1', 'DF/D1', 'DDD/D1', 'DDF/D1']),
4433    ]
4434  # Note: We don't step *into* a directory that's deleted in the repository.
4435  # E.g. ('DDD/D1/D2', '') would correctly issue a "path does not
4436  # exist" error, because at that point it can't know about the
4437  # tree-conflict on DDD/D1. ('D/D1', '') likewise, as tree-conflict
4438  # information is stored in the parent of a victim directory.
4439
4440  svntest.deeptrees.deep_trees_skipping_on_update(sbox,
4441    DeepTreesTestCase("local_leaf_edit_incoming_tree_del_skipping",
4442                      leaf_edit,
4443                      tree_del,
4444                      expected_output,
4445                      expected_disk,
4446                      expected_status),
4447                                                skip_paths,
4448                                                chdir_skip_paths)
4449
4450
4451def tree_conflicts_on_update_3(sbox):
4452  "tree conflicts 3: tree del, tree del on update"
4453
4454  # use case 3, as in notes/tree-conflicts/use-cases.txt
4455  # local tree delete, incoming tree delete
4456
4457  expected_output = deep_trees_conflict_output
4458
4459  expected_disk = svntest.wc.State('', {
4460    'F'               : Item(),
4461    'D'               : Item(),
4462    'DF'              : Item(),
4463    'DD'              : Item(),
4464    'DDF'             : Item(),
4465    'DDD'             : Item(),
4466  })
4467  expected_status = deep_trees_status_local_tree_del.copy()
4468
4469  # Expect the incoming tree deletes and the local tree deletes to mean
4470  # that all deleted paths are *really* gone, not simply scheduled for
4471  # deletion.
4472  expected_status.tweak('F/alpha',
4473                        'D/D1',
4474                        'DD/D1',
4475                        'DF/D1',
4476                        'DDD/D1',
4477                        'DDF/D1',
4478                        status='! ', wc_rev=None)
4479  # Remove from expected status and disk everything below the deleted paths.
4480  expected_status.remove('DD/D1/D2',
4481                         'DF/D1/beta',
4482                         'DDD/D1/D2',
4483                         'DDD/D1/D2/D3',
4484                         'DDF/D1/D2',
4485                         'DDF/D1/D2/gamma',)
4486
4487  expected_info = {
4488    'F/alpha' : {
4489      'Tree conflict' :
4490        '^local file delete, incoming file delete or move upon update'
4491        + ' Source  left: .file.*/F/alpha@2'
4492        + ' Source right: .none.*(/F/alpha@3)?$',
4493    },
4494    'DF/D1' : {
4495      'Tree conflict' :
4496        '^local dir delete, incoming dir delete or move upon update'
4497        + ' Source  left: .dir.*/DF/D1@2'
4498        + ' Source right: .none.*(/DF/D1@3)?$',
4499    },
4500    'DDF/D1' : {
4501      'Tree conflict' :
4502        '^local dir delete, incoming dir delete or move upon update'
4503        + ' Source  left: .dir.*/DDF/D1@2'
4504        + ' Source right: .none.*(/DDF/D1@3)?$',
4505    },
4506    'D/D1' : {
4507      'Tree conflict' :
4508        '^local dir delete, incoming dir delete or move upon update'
4509        + ' Source  left: .dir.*/D/D1@2'
4510        + ' Source right: .none.*(/D/D1@3)?$',
4511    },
4512    'DD/D1' : {
4513      'Tree conflict' :
4514        '^local dir delete, incoming dir delete or move upon update'
4515        + ' Source  left: .dir.*/DD/D1@2'
4516        + ' Source right: .none.*(/DD/D1@3)?$',
4517    },
4518    'DDD/D1' : {
4519      'Tree conflict' :
4520        '^local dir delete, incoming dir delete or move upon update'
4521        + ' Source  left: .dir.*/DDD/D1@2'
4522        + ' Source right: .none.*(/DDD/D1@3)?$',
4523    },
4524  }
4525
4526  svntest.deeptrees.deep_trees_run_tests_scheme_for_update(sbox,
4527    [ DeepTreesTestCase("local_tree_del_incoming_tree_del",
4528                        tree_del,
4529                        tree_del,
4530                        expected_output,
4531                        expected_disk,
4532                        expected_status,
4533                        expected_info = expected_info) ] )
4534
4535# Issue #3334: a modify-on-deleted tree conflict should leave the node
4536# updated to the target revision but still scheduled for deletion.
4537def tree_conflict_uc1_update_deleted_tree(sbox):
4538  "tree conflicts on update UC1, update deleted tree"
4539  sbox.build()
4540  wc_dir = sbox.wc_dir
4541
4542  from svntest.actions import run_and_verify_svn, run_and_verify_resolve
4543  from svntest.actions import run_and_verify_update, run_and_verify_commit
4544  from svntest.verify import AnyOutput
4545
4546  """A directory tree 'D1' should end up exactly the same in these two
4547  scenarios:
4548
4549  New scenario:
4550  [[[
4551    svn checkout -r1             # in which D1 has its original state
4552    svn delete D1
4553    svn update -r2               # update revs & bases to r2
4554    svn resolve --accept=mine    # keep the local, deleted version
4555  ]]]
4556
4557  Existing scenario:
4558  [[[
4559    svn checkout -r2             # in which D1 is already modified
4560    svn delete D1
4561  ]]]
4562  """
4563
4564  A = sbox.ospath('A')
4565
4566  def modify_dir(dir):
4567    """Make some set of local modifications to an existing tree:
4568    A prop change, add a child, delete a child, change a child."""
4569    run_and_verify_svn(AnyOutput, [], 'propset', 'p', 'v', dir)
4570
4571    path = os.path.join(dir, 'new_file')
4572    svntest.main.file_write(path, "This is the file 'new_file'.\n")
4573    svntest.actions.run_and_verify_svn(None, [], 'add', path)
4574
4575    path = os.path.join(dir, 'C', 'N')
4576    os.mkdir(path)
4577    path2 = os.path.join(dir, 'C', 'N', 'nu')
4578    svntest.main.file_write(path2, "This is the file 'nu'.\n")
4579    svntest.actions.run_and_verify_svn(None, [], 'add', path)
4580
4581    path = os.path.join(dir, 'B', 'lambda')
4582    svntest.actions.run_and_verify_svn(None, [], 'delete', path)
4583
4584    path = os.path.join(dir, 'B', 'E', 'alpha')
4585    svntest.main.file_append(path, "An extra line.\n")
4586
4587  # Prep for both scenarios
4588  modify_dir(A)
4589  run_and_verify_svn(AnyOutput, [], 'ci', A, '-m', 'modify_dir')
4590  run_and_verify_svn(AnyOutput, [], 'up', wc_dir)
4591
4592  # Existing scenario
4593  wc2 = sbox.add_wc_path('wc2')
4594  A2 = os.path.join(wc2, 'A')
4595  svntest.actions.duplicate_dir(sbox.wc_dir, wc2)
4596  run_and_verify_svn(AnyOutput, [], 'delete', A2)
4597
4598  # New scenario (starts at the revision before the committed mods)
4599  run_and_verify_svn(AnyOutput, [], 'up', A, '-r1')
4600  run_and_verify_svn(AnyOutput, [], 'delete', A)
4601
4602  expected_output = None
4603  expected_disk = None
4604  expected_status = None
4605
4606  run_and_verify_update(A, expected_output, expected_disk, expected_status)
4607  run_and_verify_resolve([A], '--recursive', '--accept=working', A)
4608
4609  resolved_status = svntest.wc.State('', {
4610      ''            : Item(status='  ', wc_rev=2),
4611      'A'           : Item(status='D ', wc_rev=2),
4612      'A/B'         : Item(status='D ', wc_rev=2),
4613      'A/B/E'       : Item(status='D ', wc_rev=2),
4614      'A/B/E/alpha' : Item(status='D ', wc_rev=2),
4615      'A/B/E/beta'  : Item(status='D ', wc_rev=2),
4616      'A/B/F'       : Item(status='D ', wc_rev=2),
4617      'A/mu'        : Item(status='D ', wc_rev=2),
4618      'A/C'         : Item(status='D ', wc_rev=2),
4619      'A/C/N'       : Item(status='D ', wc_rev=2),
4620      'A/C/N/nu'    : Item(status='D ', wc_rev=2),
4621      'A/D'         : Item(status='D ', wc_rev=2),
4622      'A/D/gamma'   : Item(status='D ', wc_rev=2),
4623      'A/D/G'       : Item(status='D ', wc_rev=2),
4624      'A/D/G/pi'    : Item(status='D ', wc_rev=2),
4625      'A/D/G/rho'   : Item(status='D ', wc_rev=2),
4626      'A/D/G/tau'   : Item(status='D ', wc_rev=2),
4627      'A/D/H'       : Item(status='D ', wc_rev=2),
4628      'A/D/H/chi'   : Item(status='D ', wc_rev=2),
4629      'A/D/H/omega' : Item(status='D ', wc_rev=2),
4630      'A/D/H/psi'   : Item(status='D ', wc_rev=2),
4631      'A/new_file'  : Item(status='D ', wc_rev=2),
4632      'iota'        : Item(status='  ', wc_rev=2),
4633      })
4634
4635  # The status of the new and old scenarios should be identical.
4636  expected_status = resolved_status.copy()
4637  expected_status.wc_dir = wc2
4638
4639  svntest.actions.run_and_verify_status(wc2, expected_status)
4640
4641  expected_status = resolved_status.copy()
4642  expected_status.wc_dir = wc_dir
4643
4644  svntest.actions.run_and_verify_status(wc_dir, expected_status)
4645
4646  # Just for kicks, try to commit.
4647  expected_output = svntest.wc.State(wc_dir, {
4648      'A'           : Item(verb='Deleting'),
4649      })
4650
4651  expected_status = svntest.wc.State(wc_dir, {
4652      ''            : Item(status='  ', wc_rev=2),
4653      'iota'        : Item(status='  ', wc_rev=2),
4654      })
4655
4656  run_and_verify_commit(wc_dir, expected_output, expected_status,
4657                        [], wc_dir, '-m', 'commit resolved tree')
4658
4659
4660# Issue #3334: a delete-onto-modified tree conflict should leave the node
4661# scheduled for re-addition.
4662@Issue(3334)
4663def tree_conflict_uc2_schedule_re_add(sbox):
4664  "tree conflicts on update UC2, schedule re-add"
4665  sbox.build()
4666  saved_cwd = os.getcwd()
4667  os.chdir(sbox.wc_dir)
4668
4669  from svntest.actions import run_and_verify_svn, run_and_verify_resolve
4670  from svntest.actions import run_and_verify_update
4671  from svntest.verify import AnyOutput
4672
4673  """A directory tree 'D1' should end up exactly the same in these two
4674  scenarios:
4675
4676  New scenario:
4677  [[[
4678    svn checkout -r1             # in which D1 exists
4679    modify_d1                    # make local mods in D1
4680    svn update -r2               # tries to delete D1
4681    svn resolve --accept=mine    # keep the local, re-added version
4682  ]]]
4683
4684  Existing scenario:
4685  [[[
4686    svn checkout -r2             # in which D1 does not exist
4687    svn copy -r1 D1 .            # make a pristine copy of D1@1
4688    modify_d1                    # make local mods in D1
4689  ]]]
4690
4691  where modify_d1 makes property changes to D1 itself and/or
4692  adds/deletes/modifies any of D1's children.
4693  """
4694
4695  dir = 'A'  # an existing tree in the WC and repos
4696  dir_url = sbox.repo_url + '/' + dir
4697
4698  def modify_dir(dir):
4699    """Make some set of local modifications to an existing tree:
4700    A prop change, add a child, delete a child, change a child."""
4701    run_and_verify_svn(AnyOutput, [],
4702                       'propset', 'p', 'v', dir)
4703    path = os.path.join(dir, 'new_file')
4704    svntest.main.file_write(path, "This is the file 'new_file'.\n")
4705    svntest.actions.run_and_verify_svn(None, [], 'add', path)
4706
4707    path = os.path.join(dir, 'B', 'lambda')
4708    svntest.actions.run_and_verify_svn(None, [], 'delete', path)
4709
4710    path = os.path.join(dir, 'B', 'E', 'alpha')
4711    svntest.main.file_append(path, "An extra line.\n")
4712
4713  # Prepare the repos so that a later 'update' has an incoming deletion:
4714  # Delete the dir in the repos, making r2
4715  run_and_verify_svn(AnyOutput, [],
4716                     '-m', '', 'delete', dir_url)
4717
4718  # Existing scenario
4719  os.chdir(saved_cwd)
4720  wc2 = sbox.add_wc_path('wc2')
4721  dir2 = os.path.join(wc2, dir)
4722  svntest.actions.duplicate_dir(sbox.wc_dir, wc2)
4723  run_and_verify_svn(AnyOutput, [], 'up', wc2)
4724  run_and_verify_svn(AnyOutput, [], 'copy', dir_url + '@1', dir2)
4725  modify_dir(dir2)
4726
4727  # New scenario
4728  # (The dir is already checked out.)
4729  os.chdir(sbox.wc_dir)
4730  modify_dir(dir)
4731
4732  expected_output = None
4733  expected_disk = None
4734  expected_status = None
4735  run_and_verify_update('A', expected_output, expected_disk, expected_status)
4736  run_and_verify_resolve([dir], '--recursive', '--accept=working', dir)
4737
4738  os.chdir(saved_cwd)
4739
4740  def get_status(dir):
4741    expected_status = svntest.wc.State(dir, {
4742      ''            : Item(status='  ', wc_rev='2'),
4743      'A'           : Item(status='A ', wc_rev='-', copied='+'),
4744      'A/B'         : Item(status='  ', wc_rev='-', copied='+'),
4745      'A/B/lambda'  : Item(status='D ', wc_rev='-', copied='+'),
4746      'A/B/E'       : Item(status='  ', wc_rev='-', copied='+'),
4747      'A/B/E/alpha' : Item(status='M ', wc_rev='-', copied='+'),
4748      'A/B/E/beta'  : Item(status='  ', wc_rev='-', copied='+'),
4749      'A/B/F'       : Item(status='  ', wc_rev='-', copied='+'),
4750      'A/mu'        : Item(status='  ', wc_rev='-', copied='+'),
4751      'A/C'         : Item(status='  ', wc_rev='-', copied='+'),
4752      'A/D'         : Item(status='  ', wc_rev='-', copied='+'),
4753      'A/D/gamma'   : Item(status='  ', wc_rev='-', copied='+'),
4754      'A/D/G'       : Item(status='  ', wc_rev='-', copied='+'),
4755      'A/D/G/pi'    : Item(status='  ', wc_rev='-', copied='+'),
4756      'A/D/G/rho'   : Item(status='  ', wc_rev='-', copied='+'),
4757      'A/D/G/tau'   : Item(status='  ', wc_rev='-', copied='+'),
4758      'A/D/H'       : Item(status='  ', wc_rev='-', copied='+'),
4759      'A/D/H/chi'   : Item(status='  ', wc_rev='-', copied='+'),
4760      'A/D/H/omega' : Item(status='  ', wc_rev='-', copied='+'),
4761      'A/D/H/psi'   : Item(status='  ', wc_rev='-', copied='+'),
4762      'A/new_file'  : Item(status='A ', wc_rev=0),
4763      'iota'        : Item(status='  ', wc_rev=2),
4764    })
4765    return expected_status
4766
4767  # The status of the new and old scenarios should be identical...
4768  expected_status = get_status(wc2)
4769  ### The following fails, as of Apr 6, 2010. The problem is that A/new_file
4770  ### has been *added* within a copy, yet the wc_db datastore cannot
4771  ### differentiate this from a copied-child. As a result, new_file is
4772  ### reported as a (M)odified node, rather than (A)dded.
4773  svntest.actions.run_and_verify_status(wc2, expected_status)
4774
4775  # ...except for the revision of the root of the WC and iota, because
4776  # above 'A' was the target of the update, not the WC root.
4777  expected_status = get_status(sbox.wc_dir)
4778  expected_status.tweak('', 'iota', wc_rev=1)
4779  svntest.actions.run_and_verify_status(sbox.wc_dir, expected_status)
4780
4781  ### Do we need to do more to confirm we got what we want here?
4782
4783#----------------------------------------------------------------------
4784def set_deep_depth_on_target_with_shallow_children(sbox):
4785  "infinite --set-depth adds shallow children"
4786
4787  # Regardless of what depth the update target is at, if it has shallow
4788  # subtrees and we update --set-depth infinity, these shallow subtrees
4789  # should be populated.
4790  #
4791  # See http://svn.haxx.se/dev/archive-2009-04/0344.shtml.
4792
4793  sbox.build()
4794  wc_dir = sbox.wc_dir
4795
4796  # Some paths we'll care about
4797  A_path = sbox.ospath('A')
4798  B_path = sbox.ospath('A/B')
4799  D_path = sbox.ospath('A/D')
4800
4801  # Trim the tree: Set A/B to depth empty and A/D to depth immediates.
4802  expected_output = svntest.wc.State(wc_dir, {
4803    'A/B/E'       : Item(status='D '),
4804    'A/B/lambda'  : Item(status='D '),
4805    'A/B/F'       : Item(status='D '),
4806    })
4807
4808  expected_disk = svntest.main.greek_state.copy()
4809  expected_disk.remove('A/B/F',
4810                       'A/B/lambda',
4811                       'A/B/E',
4812                       'A/B/E/alpha',
4813                       'A/B/E/beta')
4814
4815  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
4816  expected_status.remove('A/B/F',
4817                         'A/B/lambda',
4818                         'A/B/E',
4819                         'A/B/E/alpha',
4820                         'A/B/E/beta')
4821
4822  svntest.actions.run_and_verify_update(wc_dir,
4823                                        expected_output,
4824                                        expected_disk,
4825                                        expected_status,
4826                                        [], True,
4827                                        '--set-depth', 'empty',
4828                                        B_path)
4829
4830  expected_output = svntest.wc.State(wc_dir, {
4831    'A/D/G/pi'    : Item(status='D '),
4832    'A/D/G/rho'   : Item(status='D '),
4833    'A/D/G/tau'   : Item(status='D '),
4834    'A/D/H/chi'   : Item(status='D '),
4835    'A/D/H/omega' : Item(status='D '),
4836    'A/D/H/psi'   : Item(status='D '),
4837    })
4838
4839  expected_status.remove('A/D/G/pi',
4840                         'A/D/G/rho',
4841                         'A/D/G/tau',
4842                         'A/D/H/chi',
4843                         'A/D/H/omega',
4844                         'A/D/H/psi')
4845
4846  expected_disk.remove('A/D/G/pi',
4847                       'A/D/G/rho',
4848                       'A/D/G/tau',
4849                       'A/D/H/chi',
4850                       'A/D/H/omega',
4851                       'A/D/H/psi')
4852
4853  svntest.actions.run_and_verify_update(wc_dir,
4854                                        expected_output,
4855                                        expected_disk,
4856                                        expected_status,
4857                                        [], True,
4858                                        '--set-depth', 'immediates',
4859                                        D_path)
4860
4861  # Now update A with --set-depth infinity.  All the subtrees we
4862  # removed above should come back.
4863  expected_output = svntest.wc.State(wc_dir, {
4864    'A/B/lambda'  : Item(status='A '),
4865    'A/B/F'       : Item(status='A '),
4866    'A/B/E'       : Item(status='A '),
4867    'A/B/E/alpha' : Item(status='A '),
4868    'A/B/E/beta'  : Item(status='A '),
4869    'A/D/G/pi'    : Item(status='A '),
4870    'A/D/G/rho'   : Item(status='A '),
4871    'A/D/G/tau'   : Item(status='A '),
4872    'A/D/H/chi'   : Item(status='A '),
4873    'A/D/H/omega' : Item(status='A '),
4874    'A/D/H/psi'   : Item(status='A '),
4875    })
4876
4877  expected_disk = svntest.main.greek_state.copy()
4878
4879  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
4880
4881  svntest.actions.run_and_verify_update(wc_dir,
4882                                        expected_output,
4883                                        expected_disk,
4884                                        expected_status,
4885                                        [], True,
4886                                        '--set-depth', 'infinity',
4887                                        A_path)
4888
4889#----------------------------------------------------------------------
4890
4891def update_wc_of_dir_to_rev_not_containing_this_dir(sbox):
4892  "update wc of dir to rev not containing this dir"
4893
4894  sbox.build()
4895
4896  # Create working copy of 'A' directory
4897  A_url = sbox.repo_url + "/A"
4898  other_wc_dir = sbox.add_wc_path("other")
4899  svntest.actions.run_and_verify_svn(None, [], "co", A_url, other_wc_dir)
4900
4901  # Delete 'A' directory from repository
4902  svntest.actions.run_and_verify_svn(None, [], "rm", A_url, "-m", "")
4903
4904  # Try to update working copy of 'A' directory
4905  svntest.actions.run_and_verify_svn(None,
4906                                     "svn: E160005: Target path '/A' does not exist",
4907                                     "up", other_wc_dir)
4908
4909#----------------------------------------------------------------------
4910# Test for issue #3569 svn update --depth <DEPTH> allows making a working
4911# copy incomplete.
4912@Issue(3569)
4913def update_empty_hides_entries(sbox):
4914  "svn up --depth empty hides entries for next update"
4915  sbox.build()
4916  wc_dir = sbox.wc_dir
4917
4918  expected_disk_empty = []
4919  expected_status_empty = []
4920
4921  expected_disk = svntest.main.greek_state.copy()
4922  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
4923
4924  # Update to revision 0 - Removes all files from WC
4925  svntest.actions.run_and_verify_update(wc_dir,
4926                                        None,
4927                                        expected_disk_empty,
4928                                        expected_status_empty,
4929                                        [], True,
4930                                        '-r', '0',
4931                                        wc_dir)
4932
4933  # Now update back to HEAD
4934  svntest.actions.run_and_verify_update(wc_dir,
4935                                        None,
4936                                        expected_disk,
4937                                        expected_status,
4938                                        [], True,
4939                                        wc_dir)
4940
4941  # Update to revision 0 - Removes all files from WC
4942  svntest.actions.run_and_verify_update(wc_dir,
4943                                        None,
4944                                        expected_disk_empty,
4945                                        expected_status_empty,
4946                                        [], True,
4947                                        '-r', '0',
4948                                        wc_dir)
4949
4950  # Update the directory itself back to HEAD
4951  svntest.actions.run_and_verify_update(wc_dir,
4952                                        None,
4953                                        expected_disk_empty,
4954                                        expected_status_empty,
4955                                        [], True,
4956                                        '--depth', 'empty',
4957                                        wc_dir)
4958
4959  # Now update the rest back to head
4960
4961  # This operation is currently a NO-OP, because the WC-Crawler
4962  # tells the repository that it contains a full tree of the HEAD
4963  # revision.
4964  svntest.actions.run_and_verify_update(wc_dir,
4965                                        None,
4966                                        expected_disk,
4967                                        expected_status,
4968                                        check_props=True)
4969
4970#----------------------------------------------------------------------
4971# Test for issue #3573 'local non-inheritable mergeinfo changes not
4972# properly merged with updated mergeinfo'
4973@SkipUnless(server_has_mergeinfo)
4974def mergeinfo_updates_merge_with_local_mods(sbox):
4975  "local mergeinfo changes are merged with updates"
4976
4977  # Copy A to A_COPY in r2, and make some changes to A_COPY in r3-r6.
4978  sbox.build()
4979  wc_dir = sbox.wc_dir
4980  expected_disk, expected_status = set_up_branch(sbox)
4981
4982  # Some paths we'll care about
4983  A_path      = sbox.ospath('A')
4984  A_COPY_path = sbox.ospath('A_COPY')
4985
4986  # Merge -c3 from A to A_COPY at --depth empty, commit as r7.
4987  ###
4988  ### No, we are not checking the merge output for these simple
4989  ### merges.  This is already covered *TO DEATH* in merge_tests.py.
4990  ###
4991  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
4992  svntest.actions.run_and_verify_svn(None, [],
4993                                     'merge', '-c3', '--depth', 'empty',
4994                                     sbox.repo_url + '/A', A_COPY_path)
4995  svntest.actions.run_and_verify_svn(None, [], 'ci', '-m',
4996                                     'Merge r3 from A to A_COPY at depth empty',
4997                                     wc_dir)
4998  # Merge -c5 from A to A_COPY (at default --depth infinity), commit as r8.
4999  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
5000  svntest.actions.run_and_verify_svn(None, [],
5001                                     'merge', '-c5',
5002                                     sbox.repo_url + '/A', A_COPY_path)
5003  svntest.actions.run_and_verify_svn(None, [], 'ci', '-m',
5004                                     'Merge r5 from A to A_COPY', wc_dir)
5005
5006  # Update WC to r7, repeat merge of -c3 from A to A_COPY but this
5007  # time do it at --depth infinity.  Confirm that the mergeinfo
5008  # on A_COPY is no longer inheritable.
5009  svntest.actions.run_and_verify_svn(None, [], 'up', '-r7', wc_dir)
5010  svntest.actions.run_and_verify_svn(None, [],
5011                                     'merge', '-c3', '--depth', 'infinity',
5012                                     sbox.repo_url + '/A', A_COPY_path)
5013  svntest.actions.run_and_verify_svn([A_COPY_path + " - /A:3\n"], [],
5014                                     'pg', SVN_PROP_MERGEINFO, '-R',
5015                                     A_COPY_path)
5016
5017  # Update the WC (to r8), the mergeinfo on A_COPY should now have both
5018  # the local mod from the uncommitted merge (/A:3* --> /A:3) and the change
5019  # brought down by the update (/A:3* --> /A:3*,5) leaving us with /A:3,5.
5020  ### This was failing because of issue #3573.  The local mergeinfo change
5021  ### is reverted, leaving '/A:3*,5' on A_COPY.
5022  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
5023  svntest.actions.run_and_verify_svn([A_COPY_path + " - /A:3,5\n"], [],
5024                                     'pg', SVN_PROP_MERGEINFO, '-R',
5025                                     A_COPY_path)
5026
5027#----------------------------------------------------------------------
5028# A regression test for a 1.7-dev crash upon updating a WC to a different
5029# revision when it contained an excluded dir.
5030def update_with_excluded_subdir(sbox):
5031  """update with an excluded subdir"""
5032  sbox.build()
5033
5034  wc_dir = sbox.wc_dir
5035
5036  G = os.path.join(sbox.ospath('A/D/G'))
5037
5038  # Make the directory 'G' excluded.
5039  expected_output = svntest.wc.State(wc_dir, {
5040    'A/D/G' : Item(status='D '),
5041    })
5042  expected_disk = svntest.main.greek_state.copy()
5043  expected_disk.remove('A/D/G', 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
5044  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
5045  expected_status.remove('A/D/G', 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
5046  svntest.actions.run_and_verify_update(wc_dir, expected_output,
5047                                        expected_disk, expected_status,
5048                                        [], False,
5049                                        '--set-depth=exclude', G)
5050
5051  # Commit a new revision so there is something to update to.
5052  svntest.main.run_svn(None, 'mkdir', '-m', '', sbox.repo_url + '/New')
5053
5054  # Test updating the WC.
5055  expected_output = svntest.wc.State(wc_dir, {
5056    'New' : Item(status='A ') })
5057  expected_disk.add({
5058    'New' : Item() })
5059  expected_status.add({
5060    'New' : Item(status='  ') })
5061  expected_status.tweak(wc_rev=2)
5062  svntest.actions.run_and_verify_update(wc_dir, expected_output,
5063                                        expected_disk, expected_status)
5064
5065#----------------------------------------------------------------------
5066# Test for issue #3471 'svn up touches file w/ lock & svn:keywords property'
5067@Issue(3471)
5068def update_with_file_lock_and_keywords_property_set(sbox):
5069  """update with file lock & keywords property set"""
5070  sbox.build()
5071
5072  wc_dir = sbox.wc_dir
5073
5074  mu_path = sbox.ospath('A/mu')
5075  svntest.main.file_append(mu_path, '$Id$')
5076  svntest.main.run_svn(None, 'ps', 'svn:keywords', 'Id', mu_path)
5077  svntest.main.run_svn(None, 'lock', mu_path)
5078  mu_ts_before_update = os.path.getmtime(mu_path)
5079
5080  # Make sure we are at a different timestamp to really notice a mtime change
5081  time.sleep(1.1)
5082
5083  # Issue #3471 manifests itself here; The timestamp of 'mu' gets updated
5084  # to the time of the last "svn up".
5085  sbox.simple_update()
5086  mu_ts_after_update = os.path.getmtime(mu_path)
5087  if (mu_ts_before_update != mu_ts_after_update):
5088    logger.warn("The timestamp of 'mu' before and after update does not match.")
5089    raise svntest.Failure
5090
5091#----------------------------------------------------------------------
5092# Updating a nonexistent or deleted path should be a successful no-op,
5093# when there is no incoming change.  In trunk@1035343, such an update
5094# within a copied directory triggered an assertion failure.
5095@Issue(3807)
5096def update_nonexistent_child_of_copy(sbox):
5097  """update a nonexistent child of a copied dir"""
5098  sbox.build()
5099  os.chdir(sbox.wc_dir)
5100
5101  svntest.main.run_svn(None, 'copy', 'A', 'A2')
5102
5103  # Try updating a nonexistent path in the copied dir.
5104  expected_output = svntest.wc.State('A2', {
5105    'nonexistent'             : Item(verb='Skipped'),
5106  })
5107  svntest.actions.run_and_verify_update(os.path.join('A2', 'nonexistent'),
5108                                        expected_output, None, None)
5109
5110  # Try updating a deleted path in the copied dir.
5111  svntest.main.run_svn(None, 'delete', os.path.join('A2', 'mu'))
5112
5113  expected_output = svntest.wc.State('A2', {
5114    'mu'             : Item(verb='Skipped'),
5115  })
5116  svntest.actions.run_and_verify_update(os.path.join('A2', 'mu'),
5117                                        expected_output, None, None)
5118  if os.path.exists('A2/mu'):
5119    raise svntest.Failure("A2/mu improperly revived")
5120
5121@Issue(3807)
5122def revive_children_of_copy(sbox):
5123  """undelete a child of a copied dir"""
5124  sbox.build()
5125  os.chdir(sbox.wc_dir)
5126
5127  chi2_path = os.path.join('A2/D/H/chi')
5128  psi2_path = os.path.join('A2/D/H/psi')
5129
5130  svntest.main.run_svn(None, 'copy', 'A', 'A2')
5131  svntest.main.run_svn(None, 'rm', chi2_path)
5132  os.unlink(psi2_path)
5133
5134  svntest.main.run_svn(None, 'revert', chi2_path, psi2_path)
5135  if not os.path.exists(chi2_path):
5136    raise svntest.Failure('chi unexpectedly non-existent')
5137  if not os.path.exists(psi2_path):
5138    raise svntest.Failure('psi unexpectedly non-existent')
5139
5140@SkipUnless(svntest.main.is_os_windows)
5141def skip_access_denied(sbox):
5142  """access denied paths should be skipped"""
5143
5144  # We need something to lock the file. 'msvcrt' looks common on Windows
5145  try:
5146    import msvcrt
5147  except ImportError:
5148    raise svntest.Skip('python msvcrt library not available')
5149
5150  sbox.build()
5151  wc_dir = sbox.wc_dir
5152
5153  iota = sbox.ospath('iota')
5154
5155  svntest.main.file_write(iota, 'Q')
5156  sbox.simple_commit()
5157  sbox.simple_update() # Update to r2
5158
5159  # Open iota for writing to keep an handle open
5160  f = open(iota, 'w')
5161
5162  # Write new text of exactly the same size to avoid the early out
5163  # on a different size without properties.
5164  f.write('R')
5165  f.flush()
5166
5167  # And lock the first byte of the file
5168  msvcrt.locking(f.fileno(), 1, 1)
5169
5170  expected_output = svntest.wc.State(wc_dir, {
5171    'iota' : Item(verb='Skipped'),
5172    })
5173
5174  # Create expected status tree: iota isn't updated
5175  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
5176  expected_status.tweak('iota', status='M ', wc_rev=2)
5177
5178  # And now check that update skips the path
5179  # *and* status shows the path as modified.
5180  svntest.actions.run_and_verify_update(wc_dir,
5181                                        expected_output,
5182                                        None,
5183                                        expected_status,
5184                                        [], False,
5185                                        wc_dir, '-r', '1')
5186
5187  f.close()
5188
5189def update_to_HEAD_plus_1(sbox):
5190  "updating to HEAD+1 should fail"
5191
5192  sbox.build(read_only = True)
5193  wc_dir = sbox.wc_dir
5194
5195  # Attempt the update, expecting an error.  (Sometimes the error
5196  # strings says "No such revision", sometimes "No such target
5197  # revision".)
5198  svntest.actions.run_and_verify_update(wc_dir,
5199                                        None, None, None,
5200                                        ".*E160006.*No such.*revision.*",
5201                                        False,
5202                                        wc_dir, '-r', '2')
5203
5204  other_wc = sbox.add_wc_path('other')
5205  other_url = sbox.repo_url + '/A'
5206  svntest.actions.run_and_verify_svn(None, [],
5207                                     'co', other_url, other_wc)
5208  svntest.actions.run_and_verify_update(other_wc,
5209                                        None, None, None,
5210                                        ".*E160006.*No such.*revision.*",
5211                                        False,
5212                                        other_wc, '-r', '2')
5213
5214def update_moved_dir_leaf_del(sbox):
5215  "update locally moved dir with leaf del"
5216  sbox.build()
5217  wc_dir = sbox.wc_dir
5218
5219  svntest.main.run_svn(False, 'rm', '-m', 'remove /A/B/E/alpha',
5220                       sbox.repo_url + "/A/B/E/alpha")
5221  sbox.simple_move("A/B/E", "A/B/E2")
5222
5223  # Produce a tree conflict by updating the working copy to the
5224  # revision which removed A/B/E/alpha. The deletion collides with
5225  # the local move of A/B/E to A/B/E2.
5226  expected_output = svntest.wc.State(wc_dir, {
5227    'A/B/E'       : Item(status='  ', treeconflict='C'),
5228    'A/B/E/alpha' : Item(status='  ', treeconflict='D'),
5229  })
5230  expected_disk = svntest.main.greek_state.copy()
5231  expected_disk.remove('A/B/E/alpha', 'A/B/E/beta', 'A/B/E')
5232  expected_disk.add({
5233    'A/B/E2'           : Item(),
5234    'A/B/E2/alpha'     : Item(contents="This is the file 'alpha'.\n"),
5235    'A/B/E2/beta'      : Item(contents="This is the file 'beta'.\n"),
5236  })
5237  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
5238  expected_status.add({
5239    'A/B/E2'            : Item(status='A ', copied='+', wc_rev='-',
5240                               moved_from='A/B/E'),
5241    'A/B/E2/beta'       : Item(status='  ', copied='+', wc_rev='-'),
5242    'A/B/E2/alpha'      : Item(status='  ', copied='+', wc_rev='-'),
5243  })
5244  expected_status.remove('A/B/E/alpha')
5245  expected_status.tweak('A/B/E', status='D ', treeconflict='C',
5246                        moved_to='A/B/E2')
5247  expected_status.tweak('A/B/E/beta', status='D ')
5248  svntest.actions.run_and_verify_update(wc_dir,
5249                                        expected_output,
5250                                        expected_disk,
5251                                        expected_status,
5252                                        check_props=True)
5253
5254  # Now resolve the conflict, using --accept=mine-conflict applying
5255  # the update to A/B/E2
5256  svntest.actions.run_and_verify_svn(None, [],
5257                                     'resolve',
5258                                     '--accept=mine-conflict',
5259                                     sbox.ospath('A/B/E'))
5260  expected_status.tweak('A/B/E', treeconflict=None)
5261  expected_status.remove('A/B/E2/alpha')
5262  svntest.actions.run_and_verify_status(wc_dir, expected_status)
5263
5264@Issue(3144,3630)
5265# Like break_moved_dir_edited_leaf_del, but with --accept=mine-conflict
5266def update_moved_dir_edited_leaf_del(sbox):
5267  "update locally moved dir with edited leaf del"
5268  sbox.build()
5269  wc_dir = sbox.wc_dir
5270
5271  svntest.main.run_svn(False, 'rm', '-m', 'remove /A/B/E/alpha',
5272                       sbox.repo_url + "/A/B/E/alpha")
5273  sbox.simple_move("A/B/E", "A/B/E2")
5274  svntest.main.file_write(sbox.ospath('A/B/E2/alpha'),
5275                          "This is a changed 'alpha'.\n")
5276
5277  # Produce a tree conflict by updating the working copy to the
5278  # revision which removed A/B/E/alpha. The deletion collides with
5279  # the local move of A/B/E to A/B/E2.
5280  expected_output = svntest.wc.State(wc_dir, {
5281    'A/B/E'       : Item(status='  ', treeconflict='C'),
5282    'A/B/E/alpha' : Item(status='  ', treeconflict='D'),
5283  })
5284  expected_disk = svntest.main.greek_state.copy()
5285  expected_disk.remove('A/B/E/alpha', 'A/B/E/beta', 'A/B/E')
5286  expected_disk.add({
5287    'A/B/E2'           : Item(),
5288    'A/B/E2/alpha'     : Item(contents="This is a changed 'alpha'.\n"),
5289    'A/B/E2/beta'      : Item(contents="This is the file 'beta'.\n"),
5290  })
5291  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
5292  expected_status.add({
5293    'A/B/E2'            : Item(status='A ', copied='+', wc_rev='-',
5294                               moved_from='A/B/E'),
5295    'A/B/E2/beta'       : Item(status='  ', copied='+', wc_rev='-'),
5296    'A/B/E2/alpha'      : Item(status='M ', copied='+', wc_rev='-'),
5297  })
5298  expected_status.remove('A/B/E/alpha')
5299  expected_status.tweak('A/B/E', status='D ', treeconflict='C',
5300                        moved_to='A/B/E2')
5301  expected_status.tweak('A/B/E/beta', status='D ')
5302  svntest.actions.run_and_verify_update(wc_dir,
5303                                        expected_output,
5304                                        expected_disk,
5305                                        expected_status,
5306                                        check_props=True)
5307
5308  # Now resolve the conflict, using --accept=mine-conflict.
5309  # This should apply the update to A/B/E2, and flag a tree
5310  # conflict on A/B/E2/alpha (incoming delete vs. local edit)
5311  svntest.actions.run_and_verify_svn(None, [],
5312                                     'resolve',
5313                                     '--accept=mine-conflict',
5314                                     sbox.ospath('A/B/E'))
5315  expected_status.tweak('A/B/E', treeconflict=None)
5316  expected_status.tweak('A/B/E2/alpha', status='A ', copied='+', wc_rev='-',
5317                        entry_status='  ', treeconflict='C')
5318  svntest.actions.run_and_verify_status(wc_dir, expected_status)
5319
5320def update_moved_dir_file_add(sbox):
5321  "update locally moved dir with incoming file"
5322  sbox.build()
5323  wc_dir = sbox.wc_dir
5324  foo_path = "A/B/E/foo"
5325  foo_content = "This is the file 'foo'.\n"
5326
5327  svntest.main.file_write(sbox.ospath(foo_path), foo_content, 'wb')
5328  sbox.simple_add(foo_path)
5329  sbox.simple_commit()
5330  # update to go back in time, before the last commit
5331  svntest.main.run_svn(False, 'update', '-r', '1', wc_dir)
5332  sbox.simple_move("A/B/E", "A/B/E2")
5333
5334  # Produce a tree conflict by updating the working copy to the
5335  # revision which created A/B/E/foo. The addition collides with
5336  # the local move of A/B/E to A/B/E2.
5337  expected_output = svntest.wc.State(wc_dir, {
5338    'A/B/E'       : Item(status='  ', treeconflict='C'),
5339    'A/B/E/foo'   : Item(status='  ', treeconflict='A'),
5340  })
5341  expected_disk = svntest.main.greek_state.copy()
5342  expected_disk.remove('A/B/E/alpha', 'A/B/E/beta', 'A/B/E')
5343  expected_disk.add({
5344    'A/B/E2'           : Item(),
5345    'A/B/E2/alpha'     : Item(contents="This is the file 'alpha'.\n"),
5346    'A/B/E2/beta'      : Item(contents="This is the file 'beta'.\n"),
5347  })
5348  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
5349  expected_status.add({
5350    'A/B/E/foo'         : Item(status='D ', wc_rev='2'),
5351    'A/B/E2'            : Item(status='A ', copied='+', wc_rev='-',
5352                               moved_from='A/B/E'),
5353    'A/B/E2/beta'       : Item(status='  ', copied='+', wc_rev='-'),
5354    'A/B/E2/alpha'      : Item(status='  ', copied='+', wc_rev='-'),
5355  })
5356  expected_status.tweak('A/B/E', status='D ', treeconflict='C',
5357                        moved_to='A/B/E2')
5358  expected_status.tweak('A/B/E/alpha', status='D ')
5359  expected_status.tweak('A/B/E/beta', status='D ')
5360  svntest.actions.run_and_verify_update(wc_dir,
5361                                        expected_output,
5362                                        expected_disk,
5363                                        expected_status,
5364                                        check_props=True)
5365
5366  # Now resolve the conflict, using --accept=mine-conflict.
5367  # This should apply the update to A/B/E2, adding A/B/E2/foo.
5368  svntest.actions.run_and_verify_svn(None, [],
5369                                     'resolve',
5370                                     '--accept=mine-conflict',
5371                                     sbox.ospath('A/B/E'))
5372  # the incoming file should auto-merge
5373  expected_status.tweak('A/B/E', treeconflict=None)
5374  expected_status.add({
5375    'A/B/E2/foo'        : Item(status='  ', copied='+', wc_rev='-'),
5376  })
5377  svntest.actions.run_and_verify_status(wc_dir, expected_status)
5378
5379
5380def update_moved_dir_dir_add(sbox):
5381  "update locally moved dir with incoming dir"
5382  sbox.build()
5383  wc_dir = sbox.wc_dir
5384  foo_path = "A/B/E/foo"
5385  bar_path = "A/B/E/foo/bar"
5386  bar_content = "This is the file 'bar'.\n"
5387
5388  sbox.simple_mkdir(foo_path)
5389  svntest.main.file_write(sbox.ospath(bar_path), bar_content, 'wb')
5390  sbox.simple_add(bar_path)
5391  sbox.simple_commit()
5392  # update to go back in time, before the last commit
5393  svntest.main.run_svn(False, 'update', '-r', '1', wc_dir)
5394  sbox.simple_move("A/B/E", "A/B/E2")
5395
5396  # the incoming file should auto-merge
5397  expected_output = svntest.wc.State(wc_dir, {
5398      'A/B/E'         : Item(status='  ', treeconflict='C'),
5399      'A/B/E/foo'     : Item(status='  ', treeconflict='A'),
5400      'A/B/E/foo/bar' : Item(status='  ', treeconflict='A'),
5401  })
5402  expected_disk = svntest.main.greek_state.copy()
5403  expected_disk.remove('A/B/E/alpha', 'A/B/E/beta', 'A/B/E')
5404  expected_disk.add({
5405    'A/B/E2'           : Item(),
5406    'A/B/E2/alpha'     : Item(contents="This is the file 'alpha'.\n"),
5407    'A/B/E2/beta'      : Item(contents="This is the file 'beta'.\n"),
5408  })
5409  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
5410  expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta', status='D ')
5411  expected_status.tweak('A/B/E', treeconflict='C', moved_to='A/B/E2')
5412  expected_status.add({
5413    'A/B/E/foo'         : Item(status='D ', wc_rev='2'),
5414    'A/B/E/foo/bar'     : Item(status='D ', wc_rev='2'),
5415    'A/B/E2'            : Item(status='A ', copied='+', wc_rev='-',
5416                               moved_from='A/B/E'),
5417    'A/B/E2/beta'       : Item(status='  ', copied='+', wc_rev='-'),
5418    'A/B/E2/alpha'      : Item(status='  ', copied='+', wc_rev='-'),
5419  })
5420  svntest.actions.run_and_verify_update(wc_dir,
5421                                        expected_output,
5422                                        expected_disk,
5423                                        expected_status,
5424                                        check_props=True)
5425  svntest.actions.run_and_verify_svn(None, [],
5426                                     'resolve',
5427                                     '--recursive',
5428                                     '--accept=mine-conflict', wc_dir)
5429  expected_status.tweak(treeconflict=None)
5430  expected_status.add({
5431    'A/B/E2/foo'        : Item(status='  ', copied='+', wc_rev='-'),
5432    'A/B/E2/foo/bar'    : Item(status='  ', copied='+', wc_rev='-'),
5433  })
5434  svntest.actions.run_and_verify_status(wc_dir, expected_status)
5435
5436@Issue(4037)
5437def update_moved_dir_file_move(sbox):
5438  "update locally moved dir with incoming file move"
5439  sbox.build()
5440  wc_dir = sbox.wc_dir
5441
5442  sbox.simple_move("A/B/E/alpha", "A/B/F/alpha")
5443  sbox.simple_commit()
5444  # update to go back in time, before the previous commit
5445  svntest.main.run_svn(False, 'update', '-r', '1', wc_dir)
5446  sbox.simple_move("A/B/E", "A/B/E2")
5447
5448  # The incoming "move" creates a tree-conflict as an incoming change
5449  # in a local move.  We don't yet track moves on the server so we
5450  # don't recognise the incoming change as a move.
5451  expected_output = svntest.wc.State(wc_dir, {
5452    'A/B/E'       : Item(status='  ', treeconflict='C'),
5453    'A/B/E/alpha' : Item(status='  ', treeconflict='D'),
5454    'A/B/F/alpha' : Item(status='A '),
5455  })
5456  expected_disk = svntest.main.greek_state.copy()
5457  expected_disk.remove('A/B/E/alpha', 'A/B/E/beta', 'A/B/E')
5458  expected_disk.add({
5459    'A/B/E2'           : Item(),
5460    'A/B/E2/alpha'     : Item(contents="This is the file 'alpha'.\n"),
5461    'A/B/E2/beta'      : Item(contents="This is the file 'beta'.\n"),
5462    'A/B/F/alpha'      : Item(contents="This is the file 'alpha'.\n"),
5463  })
5464  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
5465  expected_status.remove('A/B/E/alpha')
5466  expected_status.tweak('A/B/E', status='D ', treeconflict='C',
5467                        moved_to='A/B/E2')
5468  expected_status.tweak('A/B/E/beta', status='D ')
5469  expected_status.add({
5470    'A/B/F/alpha'       : Item(status='  ', wc_rev='2'),
5471    'A/B/E2'            : Item(status='A ', copied='+', wc_rev='-',
5472                               moved_from='A/B/E'),
5473    'A/B/E2/alpha'      : Item(status='  ', copied='+', wc_rev='-'),
5474    'A/B/E2/beta'       : Item(status='  ', copied='+', wc_rev='-'),
5475  })
5476  svntest.actions.run_and_verify_update(wc_dir,
5477                                        expected_output,
5478                                        expected_disk,
5479                                        expected_status,
5480                                        check_props=True)
5481
5482  # The incoming change is a delete as we don't yet track server-side
5483  # moves.  Resolving the tree-conflict as "mine-conflict" applies the
5484  # delete to the move destination.
5485  svntest.actions.run_and_verify_svn(None, [],
5486                                     'resolve',
5487                                     '--accept=mine-conflict',
5488                                     sbox.ospath('A/B/E'))
5489
5490  expected_status.tweak('A/B/E', treeconflict=None)
5491  expected_status.remove('A/B/E2/alpha')
5492  svntest.actions.run_and_verify_status(wc_dir, expected_status)
5493
5494
5495@Issue(3144,3630)
5496def update_move_text_mod(sbox):
5497  "text mod to moved files"
5498
5499  sbox.build()
5500  wc_dir = sbox.wc_dir
5501  svntest.main.file_append(sbox.ospath('A/B/lambda'), "modified\n")
5502  svntest.main.file_append(sbox.ospath('A/B/E/beta'), "modified\n")
5503  sbox.simple_commit()
5504  sbox.simple_update(revision=1)
5505
5506  sbox.simple_move("A/B/E", "A/E2")
5507  sbox.simple_move("A/B/lambda", "A/lambda2")
5508
5509  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
5510  expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta', 'A/B/lambda',
5511                        status='D ')
5512  expected_status.tweak('A/B/E',  moved_to='A/E2')
5513  expected_status.tweak('A/B/lambda', moved_to='A/lambda2')
5514  expected_status.add({
5515      'A/E2'        : Item(status='A ', copied='+', wc_rev='-',
5516                           moved_from='A/B/E'),
5517      'A/E2/alpha'  : Item(status='  ', copied='+', wc_rev='-'),
5518      'A/E2/beta'   : Item(status='  ', copied='+', wc_rev='-'),
5519      'A/lambda2'   : Item(status='A ', copied='+', wc_rev='-',
5520                           moved_from='A/B/lambda'),
5521      })
5522
5523  svntest.actions.run_and_verify_status(wc_dir, expected_status)
5524
5525  expected_output = svntest.wc.State(wc_dir, {
5526    'A/B/lambda' : Item(status='  ', treeconflict='C'),
5527    'A/B/E'      : Item(status='  ', treeconflict='C'),
5528    'A/B/E/beta' : Item(status='  ', treeconflict='U'),
5529  })
5530  expected_disk = svntest.main.greek_state.copy()
5531  expected_disk.remove('A/B/E/alpha', 'A/B/E/beta', 'A/B/E', 'A/B/lambda')
5532  expected_disk.add({
5533    'A/E2'        : Item(),
5534    'A/E2/alpha'  : Item(contents="This is the file 'alpha'.\n"),
5535    'A/E2/beta'   : Item(contents="This is the file 'beta'.\n"),
5536    'A/lambda2'   : Item(contents="This is the file 'lambda'.\n"),
5537  })
5538  expected_status.tweak(wc_rev=2)
5539  expected_status.tweak('A/B/E', 'A/B/lambda', treeconflict='C')
5540  expected_status.tweak('A/E2', 'A/E2/alpha', 'A/E2/beta', 'A/lambda2',
5541                        wc_rev='-')
5542  svntest.actions.run_and_verify_update(wc_dir,
5543                                        expected_output,
5544                                        expected_disk,
5545                                        expected_status,
5546                                        check_props=True)
5547
5548  svntest.actions.run_and_verify_svn(None, [],
5549                                     'resolve',
5550                                     '--recursive',
5551                                     '--accept=mine-conflict',
5552                                     wc_dir)
5553
5554  expected_status.tweak('A/B/E', 'A/B/lambda', treeconflict=None)
5555  svntest.actions.run_and_verify_status(wc_dir, expected_status)
5556
5557  expected_disk.tweak('A/E2/beta',
5558                      contents="This is the file 'beta'.\nmodified\n"),
5559  expected_disk.tweak('A/lambda2',
5560                      contents="This is the file 'lambda'.\nmodified\n"),
5561  svntest.actions.verify_disk(wc_dir, expected_disk, check_props = True)
5562
5563
5564@Issue(3144,3630)
5565def update_nested_move_text_mod(sbox):
5566  "text mod to moved file in moved dir"
5567
5568  sbox.build()
5569  wc_dir = sbox.wc_dir
5570  svntest.main.file_append(sbox.ospath('A/B/E/alpha'), "modified\n")
5571  sbox.simple_commit()
5572  sbox.simple_update(revision=1)
5573
5574  sbox.simple_move("A/B/E", "A/E2")
5575  sbox.simple_move("A/E2/alpha", "A/alpha2")
5576
5577  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
5578  expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta', status='D ')
5579  expected_status.tweak('A/B/E', moved_to='A/E2')
5580  expected_status.add({
5581      'A/E2'        : Item(status='A ', copied='+', wc_rev='-',
5582                           moved_from='A/B/E'),
5583      'A/E2/alpha'  : Item(status='D ', copied='+', wc_rev='-',
5584                           moved_to='A/alpha2'),
5585      'A/E2/beta'   : Item(status='  ', copied='+', wc_rev='-'),
5586      'A/alpha2'    : Item(status='A ', copied='+', wc_rev='-',
5587                           moved_from='A/E2/alpha'),
5588      })
5589
5590  svntest.actions.run_and_verify_status(wc_dir, expected_status)
5591
5592  expected_output = svntest.wc.State(wc_dir, {
5593    'A/B/E'       : Item(status='  ', treeconflict='C'),
5594    'A/B/E/alpha' : Item(status='  ', treeconflict='U'),
5595  })
5596  expected_disk = svntest.main.greek_state.copy()
5597  expected_disk.remove('A/B/E/alpha', 'A/B/E/beta', 'A/B/E')
5598  expected_disk.add({
5599    'A/E2'        : Item(),
5600    'A/E2/beta'   : Item(contents="This is the file 'beta'.\n"),
5601    'A/alpha2'    : Item(contents="This is the file 'alpha'.\n"),
5602  })
5603  expected_status.tweak(wc_rev=2)
5604  expected_status.tweak('A/B/E', treeconflict='C')
5605  expected_status.tweak('A/E2', 'A/E2/alpha', 'A/E2/beta', 'A/alpha2',
5606                        wc_rev='-')
5607  svntest.actions.run_and_verify_update(wc_dir,
5608                                        expected_output,
5609                                        expected_disk,
5610                                        expected_status,
5611                                        check_props=True)
5612
5613  svntest.actions.run_and_verify_svn(None, [],
5614                                     'resolve',
5615                                     '--recursive',
5616                                     '--accept=mine-conflict',
5617                                     wc_dir)
5618
5619  expected_status.tweak('A/B/E', treeconflict=None)
5620  svntest.actions.run_and_verify_status(wc_dir, expected_status)
5621
5622  expected_disk.tweak('A/alpha2',
5623                      contents="This is the file 'alpha'.\nmodified\n"),
5624  svntest.actions.verify_disk(wc_dir, expected_disk, check_props = True)
5625
5626def update_with_parents_and_exclude(sbox):
5627  "bring a subtree in over an excluded path"
5628
5629  sbox.build(read_only = True)
5630  wc_dir = sbox.wc_dir
5631
5632  # Now we are going to exclude A
5633  expected_output = svntest.wc.State(wc_dir, {
5634    'A' : Item(status='D '),
5635  })
5636
5637  expected_status = svntest.wc.State(wc_dir, {
5638    ''     : Item(status='  ', wc_rev='1'),
5639    'iota' : Item(status='  ', wc_rev='1'),
5640  })
5641
5642  svntest.actions.run_and_verify_update(wc_dir,
5643                                        expected_output,
5644                                        None,
5645                                        expected_status,
5646                                        [], False,
5647                                        '--set-depth', 'exclude',
5648                                        sbox.ospath('A'))
5649
5650  expected_output = svntest.wc.State(wc_dir, {
5651    'A'                 : Item(status='A '),
5652    'A/B'               : Item(status='A '),
5653    'A/B/F'             : Item(status='A '),
5654    'A/B/E'             : Item(status='A '),
5655    'A/B/E/beta'        : Item(status='A '),
5656    'A/B/E/alpha'       : Item(status='A '),
5657    'A/B/lambda'        : Item(status='A '),
5658  })
5659
5660  expected_status = svntest.wc.State(wc_dir, {
5661    ''                  : Item(status='  ', wc_rev='1'),
5662    'A'                 : Item(status='  ', wc_rev='1'),
5663    'A/B'               : Item(status='  ', wc_rev='1'),
5664    'A/B/F'             : Item(status='  ', wc_rev='1'),
5665    'A/B/E'             : Item(status='  ', wc_rev='1'),
5666    'A/B/E/beta'        : Item(status='  ', wc_rev='1'),
5667    'A/B/E/alpha'       : Item(status='  ', wc_rev='1'),
5668    'A/B/lambda'        : Item(status='  ', wc_rev='1'),
5669    'iota'              : Item(status='  ', wc_rev='1'),
5670  })
5671
5672  svntest.actions.run_and_verify_update(wc_dir,
5673                                        expected_output,
5674                                        None,
5675                                        expected_status,
5676                                        [], False,
5677                                        '--parents',
5678                                        sbox.ospath('A/B'))
5679
5680@Issue(4288)
5681def update_edit_delete_obstruction(sbox):
5682  "obstructions shouldn't cause update failures"
5683
5684  sbox.build()
5685  wc_dir = sbox.wc_dir
5686
5687  # r2
5688  sbox.simple_rm('A/B','iota')
5689  svntest.main.file_append(sbox.ospath('A/mu'), "File change")
5690  sbox.simple_propset('key', 'value', 'A/D', 'A/D/G')
5691  sbox.simple_commit()
5692
5693  # r3
5694  sbox.simple_mkdir('iota')
5695  sbox.simple_copy('A/D/gamma', 'A/B')
5696  sbox.simple_rm('A/D/H/chi')
5697  sbox.simple_commit()
5698
5699  sbox.simple_update('', 1)
5700
5701  # Create obstructions
5702  svntest.main.safe_rmtree(sbox.ospath('A/B'))
5703  svntest.main.file_append(sbox.ospath('A/B'), "Obstruction")
5704
5705  svntest.main.safe_rmtree(sbox.ospath('A/D'))
5706  svntest.main.file_append(sbox.ospath('A/D'), "Obstruction")
5707
5708  os.remove(sbox.ospath('iota'))
5709  os.mkdir(sbox.ospath('iota'))
5710
5711  os.remove(sbox.ospath('A/mu'))
5712  os.mkdir(sbox.ospath('A/mu'))
5713
5714  expected_status = svntest.wc.State(wc_dir, {
5715    ''                  : Item(status='  ', wc_rev='2'),
5716    'A'                 : Item(status='  ', wc_rev='2'),
5717    'A/mu'              : Item(status='~ ', treeconflict='C', wc_rev='2'),
5718    'A/D'               : Item(status='~ ', treeconflict='C', wc_rev='2'),
5719    'A/D/G'             : Item(status='! ', wc_rev='2'),
5720    'A/D/G/pi'          : Item(status='! ', wc_rev='2'),
5721    'A/D/G/tau'         : Item(status='! ', wc_rev='2'),
5722    'A/D/G/rho'         : Item(status='! ', wc_rev='2'),
5723    'A/D/H'             : Item(status='! ', wc_rev='2'),
5724    'A/D/H/omega'       : Item(status='! ', wc_rev='2'),
5725    'A/D/H/chi'         : Item(status='! ', wc_rev='2'),
5726    'A/D/H/psi'         : Item(status='! ', wc_rev='2'),
5727    'A/D/gamma'         : Item(status='! ', wc_rev='2'),
5728    'A/C'               : Item(status='  ', wc_rev='2'),
5729    'A/B'               : Item(status='~ ', treeconflict='C', wc_rev='-',
5730                               entry_status='A ', entry_copied='+'),
5731    'A/B/F'             : Item(status='! ', wc_rev='-', entry_copied='+'),
5732    'A/B/E'             : Item(status='! ', wc_rev='-', entry_copied='+'),
5733    'A/B/E/beta'        : Item(status='! ', wc_rev='-', entry_copied='+'),
5734    'A/B/E/alpha'       : Item(status='! ', wc_rev='-', entry_copied='+'),
5735    'A/B/lambda'        : Item(status='! ', wc_rev='-', entry_copied='+'),
5736    'iota'              : Item(status='~ ', treeconflict='C', wc_rev='-',
5737                               entry_status='A ', entry_copied='+'),
5738  })
5739  expected_disk = svntest.wc.State('', {
5740    'A/D'               : Item(contents="Obstruction", props={'key':'value'}),
5741    'A/C'               : Item(),
5742    'A/B'               : Item(contents="Obstruction"),
5743    'A/mu'              : Item(),
5744    'iota'              : Item(),
5745  })
5746
5747  expected_output = svntest.wc.State(wc_dir, {
5748    'iota'    : Item(status='  ', treeconflict='C'),
5749    'A/mu'    : Item(status='  ', treeconflict='C'),
5750    'A/D'     : Item(status='  ', treeconflict='C'),
5751    'A/D/G'   : Item(status='  ', treeconflict='U'),
5752    'A/B'     : Item(status='  ', treeconflict='C'),
5753  })
5754
5755  # And now update to delete B and iota
5756  svntest.actions.run_and_verify_update(wc_dir,
5757                                        expected_output,
5758                                        expected_disk,
5759                                        expected_status,
5760                                        [], True,
5761                                        '-r', '2', wc_dir)
5762
5763  # Cleanup obstructions
5764  os.remove(sbox.ospath('A/B'))
5765  os.remove(sbox.ospath('A/D'))
5766  os.rmdir(sbox.ospath('iota'))
5767  os.rmdir(sbox.ospath('A/mu'))
5768
5769  # Revert to remove working nodes and tree conflicts
5770  svntest.actions.run_and_verify_svn(None, [],
5771                                     'revert', '-R',
5772                                     sbox.ospath('A/B'),
5773                                     sbox.ospath('A/mu'),
5774                                     sbox.ospath('A/D'),
5775                                     sbox.ospath('iota'))
5776  sbox.simple_update('', 1)
5777
5778  # Now obstruct A (as parent of the changed node), and retry
5779  svntest.main.safe_rmtree(sbox.ospath('A'))
5780  svntest.main.file_append(sbox.ospath('A'), "Obstruction")
5781
5782  # And now update to delete B and iota
5783
5784  expected_output = svntest.wc.State(wc_dir, {
5785    'A'         : Item(status='  ', treeconflict='C'),
5786    'A/mu'      : Item(status='  ', treeconflict='U'),
5787    'A/D'       : Item(status='  ', treeconflict='U'),
5788    'A/D/G'     : Item(status='  ', treeconflict='U'),
5789    'A/D/H'     : Item(status='  ', treeconflict='U'),
5790    'A/D/H/chi' : Item(status='  ', treeconflict='D'),
5791    'A/B'       : Item(prev_status='  ', prev_treeconflict='D', # Replacement
5792                       status='  ', treeconflict='A'),
5793    'iota'      : Item(status='A ', prev_status='D '), # Replacement
5794  })
5795
5796  expected_disk = svntest.wc.State('', {
5797    'A'                 : Item(contents="Obstruction"),
5798    'iota'              : Item(),
5799  })
5800
5801  expected_status = svntest.wc.State(wc_dir, {
5802    ''            : Item(status='  ', wc_rev='3'),
5803    'A'           : Item(status='~ ', treeconflict='C', wc_rev='3'),
5804    'A/mu'        : Item(status='! ', wc_rev='3'),
5805    'A/D'         : Item(status='! ', wc_rev='3'),
5806    'A/D/G'       : Item(status='! ', wc_rev='3'),
5807    'A/D/G/rho'   : Item(status='! ', wc_rev='3'),
5808    'A/D/G/pi'    : Item(status='! ', wc_rev='3'),
5809    'A/D/G/tau'   : Item(status='! ', wc_rev='3'),
5810    'A/D/gamma'   : Item(status='! ', wc_rev='3'),
5811    'A/D/H'       : Item(status='! ', wc_rev='3'),
5812    'A/D/H/psi'   : Item(status='! ', wc_rev='3'),
5813    'A/D/H/omega' : Item(status='! ', wc_rev='3'),
5814    'A/C'         : Item(status='! ', wc_rev='3'),
5815    'A/B'         : Item(status='! ', wc_rev='3'),
5816    'iota'        : Item(status='  ', wc_rev='3'),
5817  })
5818
5819  svntest.actions.run_and_verify_update(wc_dir,
5820                                        expected_output,
5821                                        expected_disk,
5822                                        expected_status,
5823                                        [], True,
5824                                        '-r', '3', wc_dir)
5825
5826def update_deleted(sbox):
5827  "update a deleted tree"
5828
5829  sbox.build(read_only = True)
5830  wc_dir = sbox.wc_dir
5831  sbox.simple_rm('A')
5832
5833  expected_output = svntest.wc.State(wc_dir, {
5834  })
5835
5836  expected_status = svntest.wc.State(wc_dir, {
5837  })
5838
5839  # This runs an update anchored on A, which is deleted. The update editor
5840  # shouldn't look at the ACTUAL/WORKING data in this case, but in 1.7 it did.
5841  svntest.actions.run_and_verify_update(wc_dir,
5842                                        expected_output,
5843                                        None,
5844                                        None,
5845                                        [], True,
5846                                        sbox.ospath('A/B'))
5847
5848@Issue(3144,3630)
5849# Like update_moved_dir_edited_leaf_del, but with --accept=theirs-conflict
5850def break_moved_dir_edited_leaf_del(sbox):
5851  "break local move of dir with edited leaf del"
5852  sbox.build()
5853  wc_dir = sbox.wc_dir
5854
5855  svntest.main.run_svn(False, 'rm', '-m', 'remove /A/B/E/alpha',
5856                       sbox.repo_url + "/A/B/E/alpha")
5857  sbox.simple_move("A/B/E", "A/B/E2")
5858  svntest.main.file_write(sbox.ospath('A/B/E2/alpha'),
5859                          "This is a changed 'alpha'.\n")
5860
5861  # Produce a tree conflict by updating the working copy to the
5862  # revision which removed A/B/E/alpha. The deletion collides with
5863  # the local move of A/B/E to A/B/E2.
5864  expected_output = svntest.wc.State(wc_dir, {
5865    'A/B/E'       : Item(status='  ', treeconflict='C'),
5866    'A/B/E/alpha' : Item(status='  ', treeconflict='D'),
5867  })
5868  expected_disk = svntest.main.greek_state.copy()
5869  expected_disk.remove('A/B/E/alpha', 'A/B/E/beta', 'A/B/E')
5870  expected_disk.add({
5871    'A/B/E2'           : Item(),
5872    'A/B/E2/alpha'     : Item(contents="This is a changed 'alpha'.\n"),
5873    'A/B/E2/beta'      : Item(contents="This is the file 'beta'.\n"),
5874  })
5875  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
5876  expected_status.add({
5877    'A/B/E2'            : Item(status='A ', copied='+', wc_rev='-',
5878                               moved_from='A/B/E'),
5879    'A/B/E2/beta'       : Item(status='  ', copied='+', wc_rev='-'),
5880    'A/B/E2/alpha'      : Item(status='M ', copied='+', wc_rev='-'),
5881  })
5882  expected_status.remove('A/B/E/alpha')
5883  expected_status.tweak('A/B/E', status='D ', treeconflict='C',
5884                        moved_to='A/B/E2')
5885  expected_status.tweak('A/B/E/beta', status='D ')
5886  svntest.actions.run_and_verify_update(wc_dir,
5887                                        expected_output,
5888                                        expected_disk,
5889                                        expected_status,
5890                                        check_props=True)
5891
5892  # Now resolve the conflict, using --accept=working
5893  # This should break the move of A/B/E to A/B/E2, leaving A/B/E2
5894  # as a copy. The deletion of A/B/E is not reverted.
5895  svntest.actions.run_and_verify_svn(None, [],
5896                                     'resolve', '--recursive',
5897                                     '--accept=working', wc_dir)
5898  expected_status.tweak('A/B/E', treeconflict=None, moved_to=None)
5899  expected_status.tweak('A/B/E2', moved_from=None)
5900  svntest.actions.run_and_verify_status(wc_dir, expected_status)
5901
5902@Issue(3144,3630)
5903def break_moved_replaced_dir(sbox):
5904  "break local move of dir plus replace"
5905  sbox.build()
5906  wc_dir = sbox.wc_dir
5907
5908  svntest.main.run_svn(False, 'rm', '-m', 'remove /A/B/E/alpha',
5909                       sbox.repo_url + "/A/B/E/alpha")
5910  sbox.simple_move("A/B/E", "A/B/E2")
5911  svntest.main.file_write(sbox.ospath('A/B/E2/alpha'),
5912                          "This is a changed 'alpha'.\n")
5913
5914  # Locally replace A/B/E with something else
5915  sbox.simple_copy('A/D/H', 'A/B/E')
5916
5917  # Produce a tree conflict by updating the working copy to the
5918  # revision which removed A/B/E/alpha. The deletion collides with
5919  # the local move of A/B/E to A/B/E2.
5920  expected_output = svntest.wc.State(wc_dir, {
5921    'A/B/E'       : Item(status='  ', treeconflict='C'),
5922    'A/B/E/alpha' : Item(status='  ', treeconflict='D'),
5923  })
5924  expected_disk = svntest.main.greek_state.copy()
5925  expected_disk.remove('A/B/E/alpha', 'A/B/E/beta')
5926  expected_disk.add({
5927    'A/B/E/chi'        : Item(contents="This is the file 'chi'.\n"),
5928    'A/B/E/psi'        : Item(contents="This is the file 'psi'.\n"),
5929    'A/B/E/omega'      : Item(contents="This is the file 'omega'.\n"),
5930    'A/B/E2'           : Item(),
5931    'A/B/E2/alpha'     : Item(contents="This is a changed 'alpha'.\n"),
5932    'A/B/E2/beta'      : Item(contents="This is the file 'beta'.\n"),
5933  })
5934  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
5935  expected_status.add({
5936    'A/B/E/chi'         : Item(status='  ', copied='+', wc_rev='-'),
5937    'A/B/E/psi'         : Item(status='  ', copied='+', wc_rev='-'),
5938    'A/B/E/omega'       : Item(status='  ', copied='+', wc_rev='-'),
5939    'A/B/E2'            : Item(status='A ', copied='+', wc_rev='-',
5940                               moved_from='A/B/E'),
5941    'A/B/E2/beta'       : Item(status='  ', copied='+', wc_rev='-'),
5942    'A/B/E2/alpha'      : Item(status='M ', copied='+', wc_rev='-'),
5943  })
5944  expected_status.remove('A/B/E/alpha')
5945  expected_status.tweak('A/B/E', status='R ', copied='+', wc_rev='-',
5946                        treeconflict='C', moved_to='A/B/E2')
5947  expected_status.tweak('A/B/E/beta', status='D ')
5948  svntest.actions.run_and_verify_update(wc_dir,
5949                                        expected_output,
5950                                        expected_disk,
5951                                        expected_status,
5952                                        check_props=True)
5953
5954  # Now resolve the conflict, using --accept=working
5955  # This should break the move of A/B/E to A/B/E2, leaving A/B/E2
5956  # as a copy. A/B/E is not reverted.
5957  svntest.actions.run_and_verify_svn(None, [],
5958                                     'resolve', '--recursive',
5959                                     '--accept=working', wc_dir)
5960  expected_status.tweak('A/B/E2', moved_from=None)
5961  expected_status.tweak('A/B/E', treeconflict=None, moved_to=None)
5962  svntest.actions.run_and_verify_status(wc_dir, expected_status)
5963
5964@Issue(4295)
5965def update_removes_switched(sbox):
5966  "update completely removes switched node"
5967
5968  sbox.build(create_wc = False)
5969
5970  wc_dir = sbox.wc_dir
5971  repo_url = sbox.repo_url
5972
5973  svntest.actions.run_and_verify_svn(None, [],
5974                                     'cp', repo_url + '/A',
5975                                           repo_url + '/AA', '-m', 'Q')
5976
5977  svntest.actions.run_and_verify_svn(None, [],
5978                                     'co', repo_url + '/A', sbox.wc_dir)
5979  svntest.actions.run_and_verify_svn(None, [],
5980                                     'switch', repo_url + '/AA/B',
5981                                               wc_dir + '/B')
5982
5983  svntest.actions.run_and_verify_svn(None, [],
5984                                     'rm', repo_url + '/AA/B', '-m', 'Q')
5985
5986  expected_output = svntest.wc.State(wc_dir, {
5987    'B'                 : Item(status='D '),
5988  })
5989  expected_status = svntest.wc.State(wc_dir, {
5990    ''                  : Item(status='  ', wc_rev='3'),
5991    'D'                 : Item(status='  ', wc_rev='3'),
5992    'D/G'               : Item(status='  ', wc_rev='3'),
5993    'D/G/rho'           : Item(status='  ', wc_rev='3'),
5994    'D/G/pi'            : Item(status='  ', wc_rev='3'),
5995    'D/G/tau'           : Item(status='  ', wc_rev='3'),
5996    'D/H'               : Item(status='  ', wc_rev='3'),
5997    'D/H/omega'         : Item(status='  ', wc_rev='3'),
5998    'D/H/chi'           : Item(status='  ', wc_rev='3'),
5999    'D/H/psi'           : Item(status='  ', wc_rev='3'),
6000    'D/gamma'           : Item(status='  ', wc_rev='3'),
6001    'C'                 : Item(status='  ', wc_rev='3'),
6002    'mu'                : Item(status='  ', wc_rev='3'),
6003  })
6004
6005  # Before r1435684 the inherited properties code would try to fetch
6006  # inherited properties for ^/AA/B and fail.
6007  #
6008  # The inherited properties fetch code would then bail and forget to reset
6009  # the ra-session URL back to its original value.
6010  #
6011  # After that the update code (which ignored the specific error code) was
6012  # continued the update against /AA/B (url of missing switched path)
6013  # instead of against A (the working copy url).
6014
6015  # This update removes 'A/B', since its in-repository location is removed.
6016  svntest.actions.run_and_verify_update(wc_dir,
6017                                        expected_output,
6018                                        None,
6019                                        expected_status)
6020
6021  expected_output = svntest.wc.State(wc_dir, {
6022    'B'          : Item(status='A '),
6023    'B/lambda'   : Item(status='A '),
6024    'B/E'        : Item(status='A '),
6025    'B/E/alpha'  : Item(status='A '),
6026    'B/E/beta'   : Item(status='A '),
6027    'B/F'        : Item(status='A '),
6028  })
6029  expected_status = svntest.wc.State(wc_dir, {
6030    ''                  : Item(status='  ', wc_rev='3'),
6031    'D'                 : Item(status='  ', wc_rev='3'),
6032    'D/G'               : Item(status='  ', wc_rev='3'),
6033    'D/G/rho'           : Item(status='  ', wc_rev='3'),
6034    'D/G/pi'            : Item(status='  ', wc_rev='3'),
6035    'D/G/tau'           : Item(status='  ', wc_rev='3'),
6036    'D/H'               : Item(status='  ', wc_rev='3'),
6037    'D/H/omega'         : Item(status='  ', wc_rev='3'),
6038    'D/H/chi'           : Item(status='  ', wc_rev='3'),
6039    'D/H/psi'           : Item(status='  ', wc_rev='3'),
6040    'D/gamma'           : Item(status='  ', wc_rev='3'),
6041    'B'                 : Item(status='  ', wc_rev='3'),
6042    'B/E'               : Item(status='  ', wc_rev='3'),
6043    'B/E/alpha'         : Item(status='  ', wc_rev='3'),
6044    'B/E/beta'          : Item(status='  ', wc_rev='3'),
6045    'B/F'               : Item(status='  ', wc_rev='3'),
6046    'B/lambda'          : Item(status='  ', wc_rev='3'),
6047    'C'                 : Item(status='  ', wc_rev='3'),
6048    'mu'                : Item(status='  ', wc_rev='3'),
6049  })
6050
6051  # And this final update brings back the node, as it was before switching.
6052  svntest.actions.run_and_verify_update(wc_dir,
6053                                       expected_output,
6054                                       None,
6055                                       expected_status)
6056
6057@Issue(3192)
6058def incomplete_overcomplete(sbox):
6059  "verify editor v1 incomplete behavior"
6060
6061  sbox.build()
6062
6063  wc_dir = sbox.wc_dir
6064  repo_dir = sbox.repo_dir
6065  repo_url = sbox.repo_url
6066
6067  # r2 - Make sure we have some dir properties in a clean wc
6068  sbox.simple_rm('A', 'iota')
6069  sbox.simple_propset('keep', 'keep-value', '')
6070  sbox.simple_propset('del', 'del-value', '')
6071  sbox.simple_commit()
6072
6073  # r3 -  Perform some changes that will be undone later
6074  sbox.simple_mkdir('ADDED-dir')
6075  sbox.simple_add_text('The added file', 'added-file')
6076  sbox.simple_propset('prop-added', 'value', '')
6077  sbox.simple_commit('')
6078  sbox.simple_update('')
6079
6080  r3_disk = svntest.wc.State('', {
6081    'added-file'        : Item(contents="The added file"),
6082    '.'                 : Item(props={'prop-added':'value', 'del':'del-value', 'keep':'keep-value'}),
6083    'ADDED-dir'         : Item(),
6084  })
6085
6086  r3_status = svntest.wc.State(wc_dir, {
6087    ''                  : Item(status='  ', wc_rev='3'),
6088    'ADDED-dir'         : Item(status='  ', wc_rev='3'),
6089    'added-file'        : Item(status='  ', wc_rev='3'),
6090  })
6091
6092  # Verify assumptions for later check
6093  svntest.actions.run_and_verify_status(wc_dir, r3_status)
6094  svntest.actions.verify_disk(wc_dir, r3_disk, check_props = True)
6095
6096
6097  # r4 - And we undo r3
6098  sbox.simple_rm('ADDED-dir', 'added-file')
6099  sbox.simple_propdel('prop-added', '')
6100  sbox.simple_commit('')
6101
6102  # r5 - Create some alternate changes
6103  sbox.simple_mkdir('NOT-ADDED-dir')
6104  sbox.simple_add_text('The not added file', 'not-added-file')
6105  sbox.simple_propset('prop-not-added', 'value', '')
6106  sbox.simple_commit('')
6107
6108  # Nothing to do to bring the wc to single revision
6109  expected_output = svntest.wc.State(wc_dir, {
6110  })
6111
6112  r5_disk = svntest.wc.State('', {
6113    ''                  : Item(props={'prop-not-added':'value',
6114                                      'del':'del-value',
6115                                      'keep':'keep-value'}),
6116    'NOT-ADDED-dir'     : Item(),
6117    'not-added-file'    : Item(contents="The not added file"),
6118  })
6119
6120  expected_status = svntest.wc.State(wc_dir, {
6121    ''                  : Item(status='  ', wc_rev='5'),
6122    'NOT-ADDED-dir'     : Item(status='  ', wc_rev='5'),
6123    'not-added-file'    : Item(status='  ', wc_rev='5'),
6124  })
6125
6126
6127  svntest.actions.run_and_verify_update(wc_dir,
6128                                        expected_output,
6129                                        r5_disk,
6130                                        expected_status,
6131                                        check_props=True)
6132
6133  # And now we mark the directory incomplete, as if the update had failed
6134  # half-way through an update to r3
6135  svntest.actions.set_incomplete(wc_dir, 3)
6136
6137  # Tweak status to verify us breaking the wc
6138  expected_status.tweak('', status='! ', wc_rev=3)
6139  svntest.actions.run_and_verify_status(wc_dir, expected_status)
6140
6141  # But the working copy is still 100% at r5
6142  svntest.actions.verify_disk(wc_dir, r5_disk, check_props = True)
6143
6144  # And expect update to do the right thing even though r3 is already encoded
6145  # in the parent. This includes fixing the list of children (reported to the
6146  # server, which will report adds and deletes) and fixing the property list
6147  # (received all; client should delete properties that shouldn't be here)
6148
6149  expected_output = svntest.wc.State(wc_dir, {
6150    ''                  : Item(status=' U'),
6151    'not-added-file'    : Item(status='D '),
6152    'ADDED-dir'         : Item(status='A '),
6153    'added-file'        : Item(status='A '),
6154    'NOT-ADDED-dir'     : Item(status='D '),
6155  })
6156
6157  svntest.actions.run_and_verify_update(wc_dir,
6158                                        expected_output,
6159                                        r3_disk,
6160                                        r3_status,
6161                                        [], True,
6162                                        wc_dir, '-r', 3)
6163
6164@Issue(4300)
6165def update_swapped_depth_dirs(sbox):
6166  "text mod to file in swapped depth dir"
6167
6168  sbox.build()
6169  wc_dir = sbox.wc_dir
6170  sbox.build()
6171  wc_dir = sbox.wc_dir
6172  svntest.main.file_append(sbox.ospath('A/B/E/alpha'), "modified\n")
6173  sbox.simple_commit()
6174  sbox.simple_update(revision=1)
6175
6176  sbox.simple_move("A/B/E", "A/E")
6177  sbox.simple_move("A/B", "A/E/B")
6178  # This is almost certainly not the right status but it's what
6179  # is currently being output so we're using it here so we
6180  # can get to the deeper problem.
6181  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
6182  expected_status.tweak("A/B", "A/B/lambda", "A/B/F", "A/B/E",
6183                        "A/B/E/alpha", "A/B/E/beta", status="D ")
6184  expected_status.tweak("A/B", moved_to="A/E/B")
6185  expected_status.add({
6186      'A/E'          : Item(status='A ', copied='+', wc_rev='-',
6187                            moved_from='A/E/B/E'),
6188      'A/E/B'        : Item(status='A ', copied='+', wc_rev='-',
6189                            moved_from='A/B'),
6190      'A/E/B/E'      : Item(status='D ', copied='+', wc_rev='-',
6191                            moved_to='A/E'),
6192      'A/E/B/F'      : Item(status='  ', copied='+', wc_rev='-'),
6193      'A/E/B/lambda' : Item(status='  ', copied='+', wc_rev='-'),
6194      'A/E/alpha'    : Item(status='  ', copied='+', wc_rev='-'),
6195      'A/E/beta'     : Item(status='  ', copied='+', wc_rev='-'),
6196      'A/E/B/E/alpha': Item(status='D ', copied='+', wc_rev='-'),
6197      'A/E/B/E/beta' : Item(status='D ', copied='+', wc_rev='-'),
6198      })
6199
6200  svntest.actions.run_and_verify_status(wc_dir, expected_status)
6201
6202  expected_output = svntest.wc.State(wc_dir, {
6203    'A/B'         : Item(status='  ', treeconflict='C'),
6204    'A/B/E'       : Item(status='  ', treeconflict='U'),
6205    'A/B/E/alpha' : Item(status='  ', treeconflict='U'),
6206  })
6207  expected_disk = svntest.main.greek_state.copy()
6208  expected_disk.remove('A/B', 'A/B/lambda', 'A/B/F', 'A/B/E',
6209                       'A/B/E/alpha', 'A/B/E/beta')
6210  expected_disk.add({
6211    'A/E'          : Item(),
6212    'A/E/alpha'    : Item(contents="This is the file 'alpha'.\n"),
6213    'A/E/beta'     : Item(contents="This is the file 'beta'.\n"),
6214    'A/E/B'        : Item(),
6215    'A/E/B/lambda' : Item(contents="This is the file 'lambda'.\n"),
6216    'A/E/B/F'      : Item(),
6217  })
6218  expected_status.tweak(wc_rev=2)
6219  expected_status.tweak('A/B', treeconflict='C')
6220  expected_status.tweak('A/E', 'A/E/alpha', 'A/E/beta', 'A/E/B',
6221                        'A/E/B/E', 'A/E/B/E/alpha', 'A/E/B/E/beta',
6222                        'A/E/B/lambda', 'A/E/B/F', wc_rev='-')
6223  svntest.actions.run_and_verify_update(wc_dir,
6224                                        expected_output,
6225                                        expected_disk,
6226                                        expected_status,
6227                                        check_props=True)
6228
6229def move_update_props(sbox):
6230  "move-update with property mods"
6231
6232  sbox.build()
6233  wc_dir = sbox.wc_dir
6234
6235  # Commit some 'future' property changes
6236  sbox.simple_propset('propertyA', 'value1',
6237                      'A/B', 'A/B/E', 'A/B/E/alpha', 'A/B/E/beta')
6238  sbox.simple_commit()
6239  sbox.simple_propset('propertyB', 'value2',
6240                      'A/B', 'A/B/E', 'A/B/E/alpha', 'A/B/E/beta')
6241  sbox.simple_commit()
6242  sbox.simple_update(revision=1)
6243
6244  # Make some local property changes
6245  sbox.simple_propset('propertyB', 'value3',
6246                      'A/B/E', 'A/B/E/beta')
6247
6248  sbox.simple_move("A/B", "A/B2")
6249
6250  # Update and expect a conflict
6251  expected_output = svntest.wc.State(wc_dir, {
6252      'A/B'         : Item(status='  ', treeconflict='C'),
6253      'A/B/E'       : Item(status='  ', treeconflict='U'),
6254      'A/B/E/alpha' : Item(status='  ', treeconflict='U'),
6255      'A/B/E/beta'  : Item(status='  ', treeconflict='U'),
6256      })
6257  expected_disk = svntest.main.greek_state.copy()
6258  expected_disk.remove('A/B/E/alpha', 'A/B/E/beta', 'A/B/E',
6259                       'A/B/lambda', 'A/B/F', 'A/B')
6260  expected_disk.add({
6261      'A/B2'         : Item(),
6262      'A/B2/E'       : Item(),
6263      'A/B2/E/alpha' : Item(contents="This is the file 'alpha'.\n"),
6264      'A/B2/E/beta'  : Item(contents="This is the file 'beta'.\n"),
6265      'A/B2/F'       : Item(),
6266      'A/B2/lambda'  : Item(contents="This is the file 'lambda'.\n"),
6267      })
6268  expected_disk.tweak('A/B2/E', 'A/B2/E/beta', props={'propertyB':'value3'})
6269  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
6270  expected_status.tweak('A/B', status='D ', treeconflict='C', moved_to='A/B2')
6271  expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta',
6272                        'A/B/F', 'A/B/lambda', status='D ')
6273  expected_status.add({
6274      'A/B2'         : Item(status='A ', copied='+', wc_rev='-',
6275                            moved_from='A/B'),
6276      'A/B2/E'       : Item(status=' M', copied='+', wc_rev='-'),
6277      'A/B2/E/beta'  : Item(status=' M', copied='+', wc_rev='-'),
6278      'A/B2/E/alpha' : Item(status='  ', copied='+', wc_rev='-'),
6279      'A/B2/F'       : Item(status='  ', copied='+', wc_rev='-'),
6280      'A/B2/lambda'  : Item(status='  ', copied='+', wc_rev='-'),
6281      })
6282  svntest.actions.run_and_verify_update(wc_dir,
6283                                        expected_output,
6284                                        expected_disk,
6285                                        expected_status,
6286                                        [], True,
6287                                        '-r', '2', wc_dir)
6288
6289  # Resolve conflict moving changes to destination without conflict
6290  svntest.actions.run_and_verify_svn(None, [],
6291                                     'resolve',
6292                                     '--accept=mine-conflict',
6293                                     sbox.ospath('A/B'))
6294
6295  expected_status.tweak('A/B', treeconflict=None)
6296  svntest.actions.run_and_verify_status(wc_dir, expected_status)
6297
6298  expected_disk.tweak('A/B2', 'A/B2/E/alpha', props={'propertyA' : 'value1'})
6299  expected_disk.tweak('A/B2/E', 'A/B2/E/beta', props={'propertyA' : 'value1',
6300                                                      'propertyB':'value3'})
6301  svntest.actions.verify_disk(wc_dir, expected_disk, check_props = True)
6302
6303  # Further update and expect a conflict.
6304  expected_status.tweak('A/B', status='D ', treeconflict='C', moved_to='A/B2')
6305  expected_status.tweak(wc_rev=3)
6306  expected_status.tweak( 'A/B2', 'A/B2/E', 'A/B2/E/beta', 'A/B2/E/alpha',
6307                         'A/B2/F', 'A/B2/lambda', wc_rev='-')
6308  svntest.actions.run_and_verify_update(wc_dir,
6309                                        expected_output,
6310                                        expected_disk,
6311                                        expected_status,
6312                                        [], True,
6313                                        '-r', '3', wc_dir)
6314
6315  # Resolve conflict moving changes and raising property conflicts
6316  svntest.actions.run_and_verify_svn(None, [],
6317                                     'resolve',
6318                                     '--accept=mine-conflict',
6319                                     sbox.ospath('A/B'))
6320
6321  expected_status.tweak('A/B', treeconflict=None)
6322  expected_status.tweak('A/B2/E', 'A/B2/E/beta', status=' C')
6323  svntest.actions.run_and_verify_status(wc_dir, expected_status)
6324
6325  expected_disk.tweak('A/B2', 'A/B2/E/alpha', props={'propertyA' : 'value1',
6326                                                     'propertyB' : 'value2'})
6327  expected_disk.tweak('A/B2/E', 'A/B2/E/beta', props={'propertyA' : 'value1',
6328                                                      'propertyB' : 'value3'})
6329  extra_files = ['dir_conflicts.prej', 'beta.prej']
6330  svntest.actions.verify_disk(wc_dir, expected_disk, True,
6331                              extra_files=extra_files)
6332
6333@Issues(3288)
6334@SkipUnless(svntest.main.is_os_windows)
6335def windows_update_backslash(sbox):
6336  "test filename with backslashes inside"
6337
6338  sbox.build()
6339
6340  wc_dir = sbox.wc_dir
6341
6342  mucc_url = sbox.repo_url
6343
6344  if mucc_url.startswith('http'):
6345    # Apache Httpd doesn't allow creating paths with '\\' in them on Windows
6346    # AH00026: found %2f (encoded '/') in URI (decoded='/svn-test-work/repositories/authz_tests-30/!svn/ver/2/A/completely\\unusable\\dir'), returning 404
6347    #
6348    # Let's use file:// to work around.
6349    mucc_url = 'file:///' + os.path.abspath(sbox.repo_dir).replace('\\', '/')
6350
6351  svntest.actions.run_and_verify_svnmucc(None, [],
6352                    '-U', mucc_url,
6353                    '-m', '',
6354                    'mkdir', 'A/completely\\unusable\\dir')
6355
6356  # No error and a proper skip + recording in the working copy would also
6357  # be a good result. This just verifies current behavior:
6358  #
6359  # - Error via file://, svn:// or http:// with SVNPathAuthz short_circuit
6360  #
6361  # - No error via http:// with SVNPathAuthz on
6362  #   (The reason is that Apache Httpd doesn't allow paths with '\\' in
6363  #    them on Windows, and a subrequest-based access check returns 404.
6364  #    This makes mod_dav_svn report the path as server excluded (aka
6365  #    absent), which doesn't produce output when updating.)
6366  #
6367  # Since https://issues.apache.org/jira/browse/SVN-3288 is about a crash,
6368  # we're fine with either result -- that is, if `svn update' finished
6369  # without an error, we expect specific stdout and proper wc state.
6370  # If it failed, we expect to get the following error:
6371  #
6372  #  svn: E155000: 'completely\unusable\dir' is not valid as filename
6373  #  in directory [...]
6374  #
6375  exit_code, output, errput = svntest.main.run_svn(1, 'up', wc_dir)
6376  if exit_code == 0:
6377    verify.verify_outputs("Unexpected output", output, errput, [
6378                           "Updating '%s':\n" % wc_dir,
6379                           "At revision 2.\n"
6380                          ], [])
6381    expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
6382    svntest.actions.run_and_verify_status(wc_dir, expected_status)
6383  elif exit_code == 1:
6384    verify.verify_outputs("Unexpected output", output, errput,
6385                          None, 'svn: E155000: .* is not valid.*')
6386  else:
6387    raise verify.SVNUnexpectedExitCode(exit_code)
6388
6389def update_moved_away(sbox):
6390  "update subtree of moved away"
6391
6392  sbox.build()
6393  wc_dir = sbox.wc_dir
6394
6395  sbox.simple_add_text('new', 'new')
6396  sbox.simple_commit()
6397
6398  sbox.simple_move('A', 'A_moved')
6399
6400  # Adding prev_status=' ' and prev_treeconflict='C' to A will make
6401  # the test PASS but why are we getting two conflicts?
6402  expected_output = svntest.wc.State(wc_dir, {
6403      'A' : Item(status='  ', treeconflict='C'),
6404  })
6405
6406  expected_disk = None
6407  expected_status = svntest.wc.State(wc_dir, {
6408    ''                  : Item(status='  ', wc_rev='1'),
6409    'A'                 : Item(status='D ', wc_rev='1', moved_to='A_moved',
6410                               treeconflict='C'),
6411    'A/B'               : Item(status='D ', wc_rev='1'),
6412    'A/B/E'             : Item(status='D ', wc_rev='2'),
6413    'A/B/E/beta'        : Item(status='D ', wc_rev='2'),
6414    'A/B/E/alpha'       : Item(status='D ', wc_rev='2'),
6415    'A/B/F'             : Item(status='D ', wc_rev='1'),
6416    'A/B/lambda'        : Item(status='D ', wc_rev='1'),
6417    'A/D'               : Item(status='D ', wc_rev='1'),
6418    'A/D/G'             : Item(status='D ', wc_rev='1'),
6419    'A/D/G/pi'          : Item(status='D ', wc_rev='1'),
6420    'A/D/G/tau'         : Item(status='D ', wc_rev='1'),
6421    'A/D/G/rho'         : Item(status='D ', wc_rev='1'),
6422    'A/D/H'             : Item(status='D ', wc_rev='1'),
6423    'A/D/H/psi'         : Item(status='D ', wc_rev='1'),
6424    'A/D/H/chi'         : Item(status='D ', wc_rev='1'),
6425    'A/D/H/omega'       : Item(status='D ', wc_rev='1'),
6426    'A/D/gamma'         : Item(status='D ', wc_rev='1'),
6427    'A/C'               : Item(status='D ', wc_rev='1'),
6428    'A/mu'              : Item(status='D ', wc_rev='1'),
6429    'A_moved'           : Item(status='A ', copied='+', wc_rev='-',
6430                               moved_from='A'),
6431    'A_moved/D'         : Item(status='  ', copied='+', wc_rev='-'),
6432    'A_moved/D/G'       : Item(status='  ', copied='+', wc_rev='-'),
6433    'A_moved/D/G/rho'   : Item(status='  ', copied='+', wc_rev='-'),
6434    'A_moved/D/G/tau'   : Item(status='  ', copied='+', wc_rev='-'),
6435    'A_moved/D/G/pi'    : Item(status='  ', copied='+', wc_rev='-'),
6436    'A_moved/D/H'       : Item(status='  ', copied='+', wc_rev='-'),
6437    'A_moved/D/H/omega' : Item(status='  ', copied='+', wc_rev='-'),
6438    'A_moved/D/H/psi'   : Item(status='  ', copied='+', wc_rev='-'),
6439    'A_moved/D/H/chi'   : Item(status='  ', copied='+', wc_rev='-'),
6440    'A_moved/D/gamma'   : Item(status='  ', copied='+', wc_rev='-'),
6441    'A_moved/B'         : Item(status='  ', copied='+', wc_rev='-'),
6442    'A_moved/B/E'       : Item(status='  ', copied='+', wc_rev='-'),
6443    'A_moved/B/E/beta'  : Item(status='  ', copied='+', wc_rev='-'),
6444    'A_moved/B/E/alpha' : Item(status='  ', copied='+', wc_rev='-'),
6445    'A_moved/B/lambda'  : Item(status='  ', copied='+', wc_rev='-'),
6446    'A_moved/B/F'       : Item(status='  ', copied='+', wc_rev='-'),
6447    'A_moved/mu'        : Item(status='  ', copied='+', wc_rev='-'),
6448    'A_moved/C'         : Item(status='  ', copied='+', wc_rev='-'),
6449    'iota'              : Item(status='  ', wc_rev='1'),
6450    'new'               : Item(status='  ', wc_rev='2'),
6451  })
6452
6453  # This update raises a tree-conflict on A.  The conflict cannot be
6454  # resolved to update the move destination because the move source is
6455  # mixed rev.
6456
6457  # Note that this exact scenario doesn't apply to switch as we don't
6458  # allow switches with as root a shadowed node.  However it is
6459  # possible to get essentially the problem with switch by invoking a
6460  # depth immedates switch on the parent of the root of the move
6461  # source. That switches the root of the move without switching the
6462  # children.
6463  svntest.actions.run_and_verify_update(wc_dir,
6464                                        expected_output,
6465                                        expected_disk,
6466                                        expected_status,
6467                                        [], False,
6468                                        sbox.ospath('A/B/E'))
6469
6470@Issues(4323)
6471def bump_below_tree_conflict(sbox):
6472  "tree conflicts should be skipped during update"
6473
6474  sbox.build()
6475  wc_dir = sbox.wc_dir
6476
6477  svntest.actions.run_and_verify_svn(None, [],
6478                                     'rm', sbox.repo_url + '/A/B',
6479                                     '-m', '')
6480
6481  sbox.simple_add_text('Q', 'q')
6482  sbox.simple_commit()
6483  sbox.simple_add_text('R', 'r')
6484  sbox.simple_commit()
6485
6486  sbox.simple_update(revision='1')
6487
6488  sbox.simple_rm('A')
6489
6490  expected_output = svntest.wc.State(wc_dir, {
6491    'A'    : Item(status='  ', treeconflict='C'), # The real TC
6492    'A/B'  : Item(status='  ', treeconflict='D'), # Shadowed delete
6493  })
6494  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
6495
6496  expected_status.tweak('A', status='D ', treeconflict='C', wc_rev='2')
6497  expected_status.tweak('A/D', 'A/D/G', 'A/D/G/rho', 'A/D/G/tau', 'A/D/G/pi',
6498                        'A/D/H', 'A/D/H/omega', 'A/D/H/chi', 'A/D/H/psi',
6499                        'A/D/gamma', 'A/mu', 'A/C', status='D ')
6500
6501  expected_status.remove('A/B', 'A/B/lambda', 'A/B/E', 'A/B/E/alpha',
6502                         'A/B/E/beta', 'A/B/F')
6503
6504  svntest.actions.run_and_verify_update(wc_dir,
6505                                        expected_output,
6506                                        None,
6507                                        expected_status,
6508                                        [], False,
6509                                        '-r', '2', wc_dir)
6510
6511  # A is tree conflicted, so an update of A/D should be a skip/no-op.
6512  expected_output = svntest.wc.State(wc_dir, {
6513    'A/D'               : Item(verb='Skipped'),
6514  })
6515  svntest.actions.run_and_verify_update(wc_dir,
6516                                        expected_output,
6517                                        None,
6518                                        expected_status,
6519                                        [], False,
6520                                        sbox.ospath('A/D'))
6521
6522  # A is tree conflicted, so an update of A/D/G should be a skip/no-op.
6523  expected_output = svntest.wc.State(wc_dir, {
6524    'A/D/G'               : Item(verb='Skipped'),
6525  })
6526  svntest.actions.run_and_verify_update(wc_dir,
6527                                        expected_output,
6528                                        None,
6529                                        expected_status,
6530                                        [], False,
6531                                        sbox.ospath('A/D/G'))
6532
6533@Issues(4111)
6534def update_child_below_add(sbox):
6535  "update child below added tree"
6536
6537  sbox.build(read_only = True)
6538  wc_dir = sbox.wc_dir
6539
6540  sbox.simple_update('A/B', 0)
6541  e_path = sbox.ospath('A/B/E')
6542
6543  # Update skips and errors on A/B/E because A/B has a not-present BASE node.
6544  expected_output = ["Skipped '"+e_path+"'\n"]
6545  expected_err = "svn: E155007: "
6546  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
6547  expected_status.remove('A/B', 'A/B/E', 'A/B/E/alpha', 'A/B/E/beta',
6548                         'A/B/F', 'A/B/lambda')
6549  svntest.actions.run_and_verify_svn(expected_output,
6550                                     expected_err,
6551                                     'update', e_path)
6552  svntest.actions.run_and_verify_status(wc_dir, expected_status)
6553
6554
6555  # Add working nodes over A/B
6556  sbox.simple_mkdir('A/B')
6557  sbox.simple_mkdir('A/B/E')
6558  sbox.simple_add_text('the new alpha', 'A/B/E/alpha')
6559
6560  expected_status.add({
6561      'A/B'         : Item(status='A ', wc_rev='-'),
6562      'A/B/E'       : Item(status='A ', wc_rev='-'),
6563      'A/B/E/alpha' : Item(status='A ', wc_rev='-'),
6564  })
6565  expected_output = svntest.wc.State(wc_dir, {
6566      'A/B/E' : Item(verb='Skipped'),
6567  })
6568  # Update should still skip A/B/E
6569  svntest.actions.run_and_verify_update(wc_dir,
6570                                        expected_output,
6571                                        None,
6572                                        expected_status,
6573                                        [], False,
6574                                        sbox.ospath('A/B/E'))
6575
6576def update_conflict_details(sbox):
6577  "update conflict details"
6578
6579  sbox.build()
6580  wc_dir = sbox.wc_dir
6581
6582  sbox.simple_append('A/B/E/new', 'new\n')
6583  sbox.simple_add('A/B/E/new')
6584  sbox.simple_append('A/B/E/alpha', '\nextra\nlines\n')
6585  sbox.simple_rm('A/B/E/beta', 'A/B/F')
6586  sbox.simple_propset('key', 'VAL', 'A/B/E', 'A/B')
6587  sbox.simple_mkdir('A/B/E/new-dir1')
6588  sbox.simple_mkdir('A/B/E/new-dir2')
6589  sbox.simple_mkdir('A/B/E/new-dir3')
6590  sbox.simple_rm('A/B/lambda')
6591  sbox.simple_mkdir('A/B/lambda')
6592  sbox.simple_commit()
6593
6594  sbox.simple_update('', 1)
6595
6596  sbox.simple_propset('key', 'vAl', 'A/B')
6597  sbox.simple_move('A/B/E/beta', 'beta')
6598  sbox.simple_propset('a', 'b', 'A/B/F', 'A/B/lambda')
6599  sbox.simple_append('A/B/E/alpha', 'other\nnew\nlines')
6600  sbox.simple_mkdir('A/B/E/new')
6601  sbox.simple_mkdir('A/B/E/new-dir1')
6602  sbox.simple_append('A/B/E/new-dir2', 'something')
6603  sbox.simple_append('A/B/E/new-dir3', 'something')
6604  sbox.simple_add('A/B/E/new-dir3')
6605
6606  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
6607  expected_status.add({
6608    'A/B/E/new'         : Item(status='R ', treeconflict='C', wc_rev='2'),
6609    'A/B/E/new-dir2'    : Item(status='D ', treeconflict='C', wc_rev='2'),
6610    'A/B/E/new-dir3'    : Item(status='R ', treeconflict='C', wc_rev='2'),
6611    'A/B/E/new-dir1'    : Item(status='  ', wc_rev='2'),
6612    'A/C'               : Item(status='  ', wc_rev='2'),
6613    'iota'              : Item(status='  ', wc_rev='2'),
6614    'beta'              : Item(status='A ', copied='+', wc_rev='-')
6615  })
6616  expected_status.tweak('A/B', status=' C', wc_rev='2')
6617  expected_status.tweak('A/B/E/alpha', status='C ', wc_rev='2')
6618  expected_status.tweak('A/B/E/beta', status='! ', treeconflict='C', wc_rev=None)
6619  expected_status.tweak('A/B/F', status='A ', copied='+', treeconflict='C', wc_rev='-')
6620  expected_status.tweak('A/B/lambda', status='RM', copied='+', treeconflict='C', wc_rev='-')
6621  expected_status.tweak('A/mu', status='  ', wc_rev='2')
6622  expected_output = svntest.wc.State(wc_dir, {
6623    'A/B'               : Item(status=' C'),
6624    'A/B/E'             : Item(status=' U'),
6625    'A/B/E/new'         : Item(status='  ', treeconflict='C'),
6626    'A/B/E/beta'        : Item(status='  ', treeconflict='C'),
6627    'A/B/E/alpha'       : Item(status='C '),
6628    'A/B/E/new-dir2'    : Item(status='  ', treeconflict='C'),
6629    'A/B/E/new-dir3'    : Item(status='  ', treeconflict='C'),
6630    'A/B/E/new-dir1'    : Item(status='E '),
6631    'A/B/F'             : Item(status='  ', treeconflict='C'),
6632    # ### 2 tree conflict reports; one for delete; one for add...
6633    'A/B/lambda'        : Item(status='  ', treeconflict='A',
6634                               prev_status='  ', prev_treeconflict='C'),
6635  })
6636  svntest.actions.run_and_verify_update(wc_dir, expected_output,
6637                                        None, expected_status,
6638                                        [], False,
6639                                        '--adds-as-modification', wc_dir)
6640
6641  # Update can't pass source as none at a specific URL@revision,
6642  # because it doesn't know... the working copy could be mixed
6643  # revision or may have excluded parts...
6644  expected_info = [
6645    {
6646      "Path" : re.escape(sbox.ospath('A/B')),
6647
6648      "Conflicted Properties" : "key",
6649      "Conflict Details": re.escape(
6650            'incoming dir edit upon update' +
6651            ' Source  left: (dir) ^/A/B@1' +
6652            ' Source right: (dir) ^/A/B@2')
6653    },
6654    {
6655      "Path" : re.escape(sbox.ospath('A/B/E')),
6656    },
6657    {
6658      "Path" : re.escape(sbox.ospath('A/B/E/alpha')),
6659      "Conflict Previous Base File" : '.*alpha.*',
6660      "Conflict Previous Working File" : '.*alpha.*',
6661      "Conflict Current Base File": '.*alpha.*',
6662      "Conflict Details": re.escape(
6663          'incoming file edit upon update' +
6664          ' Source  left: (file) ^/A/B/E/alpha@1' +
6665          ' Source right: (file) ^/A/B/E/alpha@2')
6666    },
6667    {
6668      "Path" : re.escape(sbox.ospath('A/B/E/beta')),
6669      "Tree conflict": re.escape(
6670          'local file moved away, incoming file delete or move upon update' +
6671          ' Source  left: (file) ^/A/B/E/beta@1' +
6672          ' Source right: (none) ^/A/B/E/beta@2')
6673    },
6674    {
6675      "Path" : re.escape(sbox.ospath('A/B/E/new')),
6676      "Tree conflict": re.escape(
6677          'local dir add, incoming file add upon update' +
6678          ' Source  left: (none)' +
6679          ' Source right: (file) ^/A/B/E/new@2')
6680    },
6681    {
6682      "Path" : re.escape(sbox.ospath('A/B/E/new-dir1')),
6683      # No tree conflict. Existing directory taken over
6684    },
6685    {
6686      "Path" : re.escape(sbox.ospath('A/B/E/new-dir2')),
6687      "Tree conflict": re.escape(
6688          'local file unversioned, incoming dir add upon update' +
6689          ' Source  left: (none)' +
6690          ' Source right: (dir) ^/A/B/E/new-dir2@2')
6691    },
6692    {
6693      "Path" : re.escape(sbox.ospath('A/B/E/new-dir3')),
6694      "Tree conflict": re.escape(
6695          'local file add, incoming dir add upon update' +
6696          ' Source  left: (none)' +
6697          ' Source right: (dir) ^/A/B/E/new-dir3@2')
6698    },
6699    {
6700      "Path" : re.escape(sbox.ospath('A/B/F')),
6701      "Tree conflict": re.escape(
6702          'local dir edit, incoming dir delete or move upon update' +
6703          ' Source  left: (dir) ^/A/B/F@1' +
6704          ' Source right: (none) ^/A/B/F@2')
6705    },
6706    {
6707      "Path" : re.escape(sbox.ospath('A/B/lambda')),
6708      "Tree conflict": re.escape(
6709          'local file edit, incoming replace with dir upon update' +
6710          ' Source  left: (file) ^/A/B/lambda@1' +
6711          ' Source right: (dir) ^/A/B/lambda@2')
6712    },
6713  ]
6714
6715  svntest.actions.run_and_verify_info(expected_info, sbox.ospath('A/B'),
6716                                      '--depth', 'infinity')
6717
6718# Keywords should be updated in local file even if text change is shortcut
6719# (due to the local change being the same as the incoming change, for example).
6720@XFail()
6721@Issue(4585)
6722def update_keywords_on_shortcut(sbox):
6723  "update_keywords_on_shortcut"
6724
6725  sbox.build()
6726  wc_dir = sbox.wc_dir
6727
6728  # Start with a file with keywords expanded
6729  mu_path = sbox.ospath('A/mu')
6730  svntest.main.file_append(mu_path, '$LastChangedRevision$\n')
6731  svntest.main.run_svn(None, 'ps', 'svn:keywords', 'LastChangedRevision', mu_path)
6732  sbox.simple_commit('A/mu')
6733
6734  # Modify the text, and commit
6735  svntest.main.file_append(mu_path, 'New line.\n')
6736  sbox.simple_commit('A/mu')
6737
6738  # Update back to the previous revision
6739  sbox.simple_update('A/mu', 2)
6740
6741  # Make the same change again locally
6742  svntest.main.file_append(mu_path, 'New line.\n')
6743
6744  # Update, so that merging the text change is a short-cut merge
6745  text_before_up = open(sbox.ospath('A/mu'), 'r').readlines()
6746  sbox.simple_update('A/mu')
6747  text_after_up = open(sbox.ospath('A/mu'), 'r').readlines()
6748
6749  # Check the keywords have been updated
6750  if not any(['$LastChangedRevision: 2 $' in line
6751              for line in text_before_up]):
6752    raise svntest.Failure("keyword not as expected in test set-up phase")
6753  if not any(['$LastChangedRevision: 3 $' in line
6754              for line in text_after_up]):
6755    raise svntest.Failure("update did not update the LastChangedRevision keyword")
6756
6757def update_add_conflicted_deep(sbox):
6758  "deep add conflicted"
6759
6760  sbox.build()
6761  repo_url = sbox.repo_url
6762
6763  svntest.actions.run_and_verify_svnmucc(
6764                        None, [], '-U', repo_url, '-m', '',
6765                        'mkdir', 'A/z',
6766                        'mkdir', 'A/z/z',
6767                        'mkdir', 'A/z/z/z')
6768
6769  svntest.actions.run_and_verify_svnmucc(
6770                        None, [], '-U', repo_url, '-m', '',
6771                        'rm', 'A/z',
6772                        'mkdir', 'A/z',
6773                        'mkdir', 'A/z/z',
6774                        'mkdir', 'A/z/z/z')
6775
6776  sbox.simple_append('A/z', 'A/z')
6777  sbox.simple_add('A/z')
6778  sbox.simple_update('A', 2)
6779  # This final update used to segfault using 1.9.0 and 1.9.1
6780  sbox.simple_update('A/z/z', 3)
6781
6782def missing_tmp_update(sbox):
6783  "missing tmp update caused segfault"
6784
6785  sbox.build(read_only = True)
6786  wc_dir = sbox.wc_dir
6787  svntest.actions.run_and_verify_update(wc_dir, None, None, None, [], False,
6788                                        wc_dir, '--set-depth', 'empty')
6789
6790  os.rmdir(sbox.ospath(svntest.main.get_admin_name() + '/tmp'))
6791
6792  svntest.actions.run_and_verify_svn(None, '.*Unable to create.*',
6793                                     'up', wc_dir, '--set-depth', 'infinity')
6794
6795  # This re-creates .svn/tmp as a side-effect.
6796  svntest.actions.run_and_verify_svn(None, [], 'cleanup',
6797                                     '--vacuum-pristines', wc_dir)
6798
6799  svntest.actions.run_and_verify_update(wc_dir, None, None, None, [], False,
6800                                        wc_dir, '--set-depth', 'infinity')
6801
6802def update_delete_switched(sbox):
6803  "update delete switched"
6804
6805  sbox.build(read_only = True)
6806  wc_dir = sbox.wc_dir
6807
6808  svntest.actions.run_and_verify_switch(wc_dir, sbox.ospath('A/B/E'),
6809                                        sbox.repo_url + '/A/D/G',
6810                                        None, None, None, [], False,
6811                                        '--ignore-ancestry')
6812
6813  # Introduce some change somewhere...
6814  sbox.simple_propset('A', 'A', 'A')
6815
6816  expected_status = svntest.wc.State(wc_dir, {
6817      ''                  : Item(status='  ', wc_rev='1'),
6818      'A'                 : Item(status='A ', copied='+', treeconflict='C', wc_rev='-'),
6819      'A/B'               : Item(status='  ', copied='+', wc_rev='-'),
6820      'A/B/E'             : Item(status='A ', copied='+', wc_rev='-'),
6821      'A/B/E/rho'         : Item(status='  ', copied='+', wc_rev='-'),
6822      'A/B/E/pi'          : Item(status='  ', copied='+', wc_rev='-'),
6823      'A/B/E/tau'         : Item(status='  ', copied='+', wc_rev='-'),
6824      'A/B/lambda'        : Item(status='  ', copied='+', wc_rev='-'),
6825      'A/B/F'             : Item(status='  ', copied='+', wc_rev='-'),
6826      'A/D'               : Item(status='  ', copied='+', wc_rev='-'),
6827      'A/D/G'             : Item(status='  ', copied='+', wc_rev='-'),
6828      'A/D/G/pi'          : Item(status='  ', copied='+', wc_rev='-'),
6829      'A/D/G/tau'         : Item(status='  ', copied='+', wc_rev='-'),
6830      'A/D/G/rho'         : Item(status='  ', copied='+', wc_rev='-'),
6831      'A/D/gamma'         : Item(status='  ', copied='+', wc_rev='-'),
6832      'A/D/H'             : Item(status='  ', copied='+', wc_rev='-'),
6833      'A/D/H/omega'       : Item(status='  ', copied='+', wc_rev='-'),
6834      'A/D/H/psi'         : Item(status='  ', copied='+', wc_rev='-'),
6835      'A/D/H/chi'         : Item(status='  ', copied='+', wc_rev='-'),
6836      'A/mu'              : Item(status='  ', copied='+', wc_rev='-'),
6837      'A/C'               : Item(status='  ', copied='+', wc_rev='-'),
6838      'iota'              : Item(status='  ', wc_rev='1'),
6839  })
6840  svntest.actions.run_and_verify_update(wc_dir, None, None, expected_status,
6841                                        [], False, sbox.ospath('A'), '-r', 0)
6842
6843@XFail()
6844def update_add_missing_local_add(sbox):
6845  "update adds missing local addition"
6846
6847  sbox.build(read_only=True)
6848
6849  # Note that updating 'A' to r0 doesn't reproduce this issue...
6850  sbox.simple_update('', revision='0')
6851  sbox.simple_mkdir('A')
6852  sbox.simple_add_text('mumumu', 'A/mu')
6853  os.unlink(sbox.ospath('A/mu'))
6854  os.rmdir(sbox.ospath('A'))
6855
6856  sbox.simple_update()
6857
6858# Verify that deleting an unmodified directory leaves behind any unversioned
6859# items on disk
6860def update_keeps_unversioned_items_in_deleted_dir(sbox):
6861  "update keeps unversioned items in deleted dir"
6862  sbox.build()
6863  wc_dir = sbox.wc_dir
6864
6865  sbox.simple_rm('A/D/G')
6866  sbox.simple_commit()
6867
6868  sbox.simple_update('', revision='1')
6869
6870  os.mkdir(sbox.ospath('A/D/G/unversioned-dir'))
6871  svntest.main.file_write(sbox.ospath('A/D/G/unversioned.txt'),
6872                          'unversioned file', 'wb')
6873
6874  expected_output = svntest.wc.State(wc_dir, {
6875    'A/D/G' : Item(status='D '),
6876    })
6877
6878  expected_disk = svntest.main.greek_state.copy()
6879  # The unversioned items should be left behind on disk
6880  expected_disk.add({
6881    'A/D/G/unversioned-dir' : Item(),
6882    'A/D/G/unversioned.txt' : Item('unversioned file'),
6883    })
6884  expected_disk.remove('A/D/G/pi')
6885  expected_disk.remove('A/D/G/rho')
6886  expected_disk.remove('A/D/G/tau')
6887
6888  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
6889  expected_status.remove('A/D/G')
6890  expected_status.remove('A/D/G/pi')
6891  expected_status.remove('A/D/G/rho')
6892  expected_status.remove('A/D/G/tau')
6893
6894  svntest.actions.run_and_verify_update(wc_dir,
6895                                        expected_output,
6896                                        expected_disk,
6897                                        expected_status,
6898                                        [], True)
6899
6900#######################################################################
6901# Run the tests
6902
6903
6904# list all tests here, starting with None:
6905test_list = [ None,
6906              update_binary_file,
6907              update_binary_file_2,
6908              update_ignores_added,
6909              update_to_rev_zero,
6910              receive_overlapping_same_change,
6911              update_to_resolve_text_conflicts,
6912              update_delete_modified_files,
6913              update_after_add_rm_deleted,
6914              update_missing,
6915              update_replace_dir,
6916              update_single_file,
6917              prop_update_on_scheduled_delete,
6918              update_receive_illegal_name,
6919              update_deleted_missing_dir,
6920              another_hudson_problem,
6921              update_deleted_targets,
6922              new_dir_with_spaces,
6923              non_recursive_update,
6924              checkout_empty_dir,
6925              update_to_deletion,
6926              update_deletion_inside_out,
6927              update_schedule_add_dir,
6928              update_to_future_add,
6929              obstructed_update_alters_wc_props,
6930              update_xml_unsafe_dir,
6931              conflict_markers_matching_eol,
6932              update_eolstyle_handling,
6933              update_copy_of_old_rev,
6934              forced_update,
6935              forced_update_failures,
6936              update_wc_on_windows_drive,
6937              update_wc_with_replaced_file,
6938              update_with_obstructing_additions,
6939              update_conflicted,
6940              mergeinfo_update_elision,
6941              update_copied_from_replaced_and_changed,
6942              update_copied_and_deleted_prop,
6943              update_accept_conflicts,
6944              update_uuid_changed,
6945              restarted_update_should_delete_dir_prop,
6946              tree_conflicts_on_update_1_1,
6947              tree_conflicts_on_update_1_2,
6948              tree_conflicts_on_update_2_1,
6949              tree_conflicts_on_update_2_2,
6950              tree_conflicts_on_update_2_3,
6951              tree_conflicts_on_update_3,
6952              tree_conflict_uc1_update_deleted_tree,
6953              tree_conflict_uc2_schedule_re_add,
6954              set_deep_depth_on_target_with_shallow_children,
6955              update_wc_of_dir_to_rev_not_containing_this_dir,
6956              update_empty_hides_entries,
6957              mergeinfo_updates_merge_with_local_mods,
6958              update_with_excluded_subdir,
6959              update_with_file_lock_and_keywords_property_set,
6960              update_nonexistent_child_of_copy,
6961              revive_children_of_copy,
6962              skip_access_denied,
6963              update_to_HEAD_plus_1,
6964              update_moved_dir_leaf_del,
6965              update_moved_dir_edited_leaf_del,
6966              update_moved_dir_file_add,
6967              update_moved_dir_dir_add,
6968              update_moved_dir_file_move,
6969              update_binary_file_3,
6970              update_move_text_mod,
6971              update_nested_move_text_mod,
6972              update_with_parents_and_exclude,
6973              update_edit_delete_obstruction,
6974              update_deleted,
6975              break_moved_dir_edited_leaf_del,
6976              break_moved_replaced_dir,
6977              update_removes_switched,
6978              incomplete_overcomplete,
6979              update_swapped_depth_dirs,
6980              move_update_props,
6981              windows_update_backslash,
6982              update_moved_away,
6983              bump_below_tree_conflict,
6984              update_child_below_add,
6985              update_conflict_details,
6986              update_keywords_on_shortcut,
6987              update_add_conflicted_deep,
6988              missing_tmp_update,
6989              update_delete_switched,
6990              update_add_missing_local_add,
6991              update_keeps_unversioned_items_in_deleted_dir,
6992             ]
6993
6994if __name__ == '__main__':
6995  svntest.main.run_tests(test_list)
6996  # NOTREACHED
6997
6998
6999### End of file.
7000