##// END OF EJS Templates
Fix file-changed-to-dir and dir-to-file commits (issue660)....
Maxim Dounin -
r5487:7a64931e default
parent child Browse files
Show More
@@ -0,0 +1,89 b''
1 #!/bin/sh
2 # http://www.selenic.com/mercurial/bts/issue660
3
4
5 hg init a
6 cd a
7 echo a > a
8 mkdir b
9 echo b > b/b
10 hg commit -A -m "a is file, b is dir"
11
12 echo % file replaced with directory
13
14 rm a
15 mkdir a
16 echo a > a/a
17
18 echo % should fail - would corrupt dirstate
19 hg add a/a
20
21 echo % removing shadow
22 hg rm --after a
23
24 echo % should succeed - shadow removed
25 hg add a/a
26
27 echo % directory replaced with file
28
29 rm -r b
30 echo b > b
31
32 echo % should fail - would corrupt dirstate
33 hg add b
34
35 echo % removing shadow
36 hg rm --after b/b
37
38 echo % should succeed - shadow removed
39 hg add b
40
41 echo % look what we got
42 hg st
43
44 echo % revert reintroducing shadow - should fail
45 rm -r a b
46 hg revert b/b
47
48 echo % revert all - should succeed
49 hg revert --all
50 hg st
51
52 echo % addremove
53
54 rm -r a b
55 mkdir a
56 echo a > a/a
57 echo b > b
58
59 hg addremove
60 hg st
61
62 echo % commit
63 hg ci -A -m "a is dir, b is file"
64 hg st --all
65
66 echo % long directory replaced with file
67
68 mkdir d
69 mkdir d/d
70 echo d > d/d/d
71 hg commit -A -m "d is long directory"
72 rm -r d
73 echo d > d
74
75 echo % should fail - would corrupt dirstate
76 hg add d
77
78 echo % removing shadow
79 hg rm --after d/d/d
80
81 echo % should succeed - shadow removed
82 hg add d
83
84 #echo % update should work
85 #
86 #hg up -r 0
87 #hg up -r 1
88
89 exit 0
@@ -0,0 +1,42 b''
1 adding a
2 adding b/b
3 % file replaced with directory
4 % should fail - would corrupt dirstate
5 abort: file 'a' in dirstate clashes with 'a/a'
6 % removing shadow
7 % should succeed - shadow removed
8 % directory replaced with file
9 % should fail - would corrupt dirstate
10 abort: directory 'b' already in dirstate
11 % removing shadow
12 % should succeed - shadow removed
13 % look what we got
14 A a/a
15 A b
16 R a
17 R b/b
18 % revert reintroducing shadow - should fail
19 abort: file 'b' in dirstate clashes with 'b/b'
20 % revert all - should succeed
21 undeleting a
22 forgetting a/a
23 forgetting b
24 undeleting b/b
25 % addremove
26 removing a
27 adding a/a
28 adding b
29 removing b/b
30 A a/a
31 A b
32 R a
33 R b/b
34 % commit
35 C a/a
36 C b
37 % long directory replaced with file
38 adding d/d/d
39 % should fail - would corrupt dirstate
40 abort: directory 'd' already in dirstate
41 % removing shadow
42 % should succeed - shadow removed
@@ -266,14 +266,15 b' def addremove(repo, pats=[], opts={}, dr'
266 266 mapping[abs] = rel, exact
267 267 if repo.ui.verbose or not exact:
268 268 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
269 if repo.dirstate[abs] != 'r' and not util.lexists(target):
269 if repo.dirstate[abs] != 'r' and (not util.lexists(target)
270 or (os.path.isdir(target) and not os.path.islink(target))):
270 271 remove.append(abs)
271 272 mapping[abs] = rel, exact
272 273 if repo.ui.verbose or not exact:
273 274 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
274 275 if not dry_run:
276 repo.remove(remove)
275 277 repo.add(add)
276 repo.remove(remove)
277 278 if similarity > 0:
278 279 for old, new, score in findrenames(repo, add, remove, similarity):
279 280 oldrel, oldexact = mapping[old]
@@ -50,7 +50,8 b' class dirstate(object):'
50 50 elif name == '_dirs':
51 51 self._dirs = {}
52 52 for f in self._map:
53 self._incpath(f)
53 if self[f] != 'r':
54 self._incpath(f)
54 55 return self._dirs
55 56 elif name == '_ignore':
56 57 files = [self._join('.hgignore')]
@@ -205,14 +206,25 b' class dirstate(object):'
205 206 d = f[:c]
206 207 if d in self._dirs:
207 208 break
208 if d in self._map:
209 if d in self._map and self[d] != 'r':
209 210 raise util.Abort(_('file %r in dirstate clashes with %r') %
210 211 (d, f))
211 212 self._incpath(f)
212 213
214 def _changepath(self, f, newstate):
215 # handle upcoming path changes
216 oldstate = self[f]
217 if oldstate not in "?r" and newstate in "?r":
218 self._decpath(f)
219 return
220 if oldstate in "?r" and newstate not in "?r":
221 self._incpathcheck(f)
222 return
223
213 224 def normal(self, f):
214 225 'mark a file normal and clean'
215 226 self._dirty = True
227 self._changepath(f, 'n')
216 228 s = os.lstat(self._join(f))
217 229 self._map[f] = ('n', s.st_mode, s.st_size, s.st_mtime, 0)
218 230 if self._copymap.has_key(f):
@@ -221,6 +233,7 b' class dirstate(object):'
221 233 def normallookup(self, f):
222 234 'mark a file normal, but possibly dirty'
223 235 self._dirty = True
236 self._changepath(f, 'n')
224 237 self._map[f] = ('n', 0, -1, -1, 0)
225 238 if f in self._copymap:
226 239 del self._copymap[f]
@@ -228,6 +241,7 b' class dirstate(object):'
228 241 def normaldirty(self, f):
229 242 'mark a file normal, but dirty'
230 243 self._dirty = True
244 self._changepath(f, 'n')
231 245 self._map[f] = ('n', 0, -2, -1, 0)
232 246 if f in self._copymap:
233 247 del self._copymap[f]
@@ -235,7 +249,7 b' class dirstate(object):'
235 249 def add(self, f):
236 250 'mark a file added'
237 251 self._dirty = True
238 self._incpathcheck(f)
252 self._changepath(f, 'a')
239 253 self._map[f] = ('a', 0, -1, -1, 0)
240 254 if f in self._copymap:
241 255 del self._copymap[f]
@@ -243,8 +257,8 b' class dirstate(object):'
243 257 def remove(self, f):
244 258 'mark a file removed'
245 259 self._dirty = True
260 self._changepath(f, 'r')
246 261 self._map[f] = ('r', 0, 0, 0, 0)
247 self._decpath(f)
248 262 if f in self._copymap:
249 263 del self._copymap[f]
250 264
@@ -252,6 +266,7 b' class dirstate(object):'
252 266 'mark a file merged'
253 267 self._dirty = True
254 268 s = os.lstat(self._join(f))
269 self._changepath(f, 'm')
255 270 self._map[f] = ('m', s.st_mode, s.st_size, s.st_mtime, 0)
256 271 if f in self._copymap:
257 272 del self._copymap[f]
@@ -260,13 +275,15 b' class dirstate(object):'
260 275 'forget a file'
261 276 self._dirty = True
262 277 try:
278 self._changepath(f, '?')
263 279 del self._map[f]
264 self._decpath(f)
265 280 except KeyError:
266 281 self._ui.warn(_("not in dirstate: %s!\n") % f)
267 282
268 283 def clear(self):
269 284 self._map = {}
285 if "_dirs" in self.__dict__:
286 delattr(self, "_dirs");
270 287 self._copymap = {}
271 288 self._pl = [nullid, nullid]
272 289 self._dirty = True
@@ -522,7 +539,7 b' class dirstate(object):'
522 539 try:
523 540 st = lstat(_join(fn))
524 541 except OSError, inst:
525 if inst.errno != errno.ENOENT:
542 if inst.errno not in (errno.ENOENT, errno.ENOTDIR):
526 543 raise
527 544 st = None
528 545 # We need to re-check that it is a valid file
@@ -720,7 +720,7 b' class path_auditor(object):'
720 720 except OSError, err:
721 721 # EINVAL can be raised as invalid path syntax under win32.
722 722 # They must be ignored for patterns can be checked too.
723 if err.errno not in (errno.ENOENT, errno.EINVAL):
723 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
724 724 raise
725 725 else:
726 726 if stat.S_ISLNK(st.st_mode):
General Comments 0
You need to be logged in to leave comments. Login now