##// END OF EJS Templates
branchcache: stay silent if failing to read cache files...
Mads Kiilerich -
r24728:75688a6f default
parent child Browse files
Show More
@@ -1,441 +1,439 b''
1 1 # branchmap.py - logic to computes, maintain and stores branchmap for local repo
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from node import bin, hex, nullid, nullrev
9 9 import encoding
10 10 import scmutil
11 11 import util
12 12 import time
13 13 from array import array
14 14 from struct import calcsize, pack, unpack
15 15
16 16 def _filename(repo):
17 17 """name of a branchcache file for a given repo or repoview"""
18 18 filename = "cache/branch2"
19 19 if repo.filtername:
20 20 filename = '%s-%s' % (filename, repo.filtername)
21 21 return filename
22 22
23 23 def read(repo):
24 24 try:
25 25 f = repo.vfs(_filename(repo))
26 26 lines = f.read().split('\n')
27 27 f.close()
28 28 except (IOError, OSError):
29 29 return None
30 30
31 31 try:
32 32 cachekey = lines.pop(0).split(" ", 2)
33 33 last, lrev = cachekey[:2]
34 34 last, lrev = bin(last), int(lrev)
35 35 filteredhash = None
36 36 if len(cachekey) > 2:
37 37 filteredhash = bin(cachekey[2])
38 38 partial = branchcache(tipnode=last, tiprev=lrev,
39 39 filteredhash=filteredhash)
40 40 if not partial.validfor(repo):
41 41 # invalidate the cache
42 42 raise ValueError('tip differs')
43 43 for l in lines:
44 44 if not l:
45 45 continue
46 46 node, state, label = l.split(" ", 2)
47 47 if state not in 'oc':
48 48 raise ValueError('invalid branch state')
49 49 label = encoding.tolocal(label.strip())
50 50 if not node in repo:
51 51 raise ValueError('node %s does not exist' % node)
52 52 node = bin(node)
53 53 partial.setdefault(label, []).append(node)
54 54 if state == 'c':
55 55 partial._closednodes.add(node)
56 56 except KeyboardInterrupt:
57 57 raise
58 58 except Exception, inst:
59 59 if repo.ui.debugflag:
60 60 msg = 'invalid branchheads cache'
61 61 if repo.filtername is not None:
62 62 msg += ' (%s)' % repo.filtername
63 63 msg += ': %s\n'
64 64 repo.ui.debug(msg % inst)
65 65 partial = None
66 66 return partial
67 67
68 68 ### Nearest subset relation
69 69 # Nearest subset of filter X is a filter Y so that:
70 70 # * Y is included in X,
71 71 # * X - Y is as small as possible.
72 72 # This create and ordering used for branchmap purpose.
73 73 # the ordering may be partial
74 74 subsettable = {None: 'visible',
75 75 'visible': 'served',
76 76 'served': 'immutable',
77 77 'immutable': 'base'}
78 78
79 79 def updatecache(repo):
80 80 cl = repo.changelog
81 81 filtername = repo.filtername
82 82 partial = repo._branchcaches.get(filtername)
83 83
84 84 revs = []
85 85 if partial is None or not partial.validfor(repo):
86 86 partial = read(repo)
87 87 if partial is None:
88 88 subsetname = subsettable.get(filtername)
89 89 if subsetname is None:
90 90 partial = branchcache()
91 91 else:
92 92 subset = repo.filtered(subsetname)
93 93 partial = subset.branchmap().copy()
94 94 extrarevs = subset.changelog.filteredrevs - cl.filteredrevs
95 95 revs.extend(r for r in extrarevs if r <= partial.tiprev)
96 96 revs.extend(cl.revs(start=partial.tiprev + 1))
97 97 if revs:
98 98 partial.update(repo, revs)
99 99 partial.write(repo)
100 100
101 101 assert partial.validfor(repo), filtername
102 102 repo._branchcaches[repo.filtername] = partial
103 103
104 104 class branchcache(dict):
105 105 """A dict like object that hold branches heads cache.
106 106
107 107 This cache is used to avoid costly computations to determine all the
108 108 branch heads of a repo.
109 109
110 110 The cache is serialized on disk in the following format:
111 111
112 112 <tip hex node> <tip rev number> [optional filtered repo hex hash]
113 113 <branch head hex node> <open/closed state> <branch name>
114 114 <branch head hex node> <open/closed state> <branch name>
115 115 ...
116 116
117 117 The first line is used to check if the cache is still valid. If the
118 118 branch cache is for a filtered repo view, an optional third hash is
119 119 included that hashes the hashes of all filtered revisions.
120 120
121 121 The open/closed state is represented by a single letter 'o' or 'c'.
122 122 This field can be used to avoid changelog reads when determining if a
123 123 branch head closes a branch or not.
124 124 """
125 125
126 126 def __init__(self, entries=(), tipnode=nullid, tiprev=nullrev,
127 127 filteredhash=None, closednodes=None):
128 128 super(branchcache, self).__init__(entries)
129 129 self.tipnode = tipnode
130 130 self.tiprev = tiprev
131 131 self.filteredhash = filteredhash
132 132 # closednodes is a set of nodes that close their branch. If the branch
133 133 # cache has been updated, it may contain nodes that are no longer
134 134 # heads.
135 135 if closednodes is None:
136 136 self._closednodes = set()
137 137 else:
138 138 self._closednodes = closednodes
139 139
140 140 def validfor(self, repo):
141 141 """Is the cache content valid regarding a repo
142 142
143 143 - False when cached tipnode is unknown or if we detect a strip.
144 144 - True when cache is up to date or a subset of current repo."""
145 145 try:
146 146 return ((self.tipnode == repo.changelog.node(self.tiprev))
147 147 and (self.filteredhash == \
148 148 scmutil.filteredhash(repo, self.tiprev)))
149 149 except IndexError:
150 150 return False
151 151
152 152 def _branchtip(self, heads):
153 153 '''Return tuple with last open head in heads and false,
154 154 otherwise return last closed head and true.'''
155 155 tip = heads[-1]
156 156 closed = True
157 157 for h in reversed(heads):
158 158 if h not in self._closednodes:
159 159 tip = h
160 160 closed = False
161 161 break
162 162 return tip, closed
163 163
164 164 def branchtip(self, branch):
165 165 '''Return the tipmost open head on branch head, otherwise return the
166 166 tipmost closed head on branch.
167 167 Raise KeyError for unknown branch.'''
168 168 return self._branchtip(self[branch])[0]
169 169
170 170 def branchheads(self, branch, closed=False):
171 171 heads = self[branch]
172 172 if not closed:
173 173 heads = [h for h in heads if h not in self._closednodes]
174 174 return heads
175 175
176 176 def iterbranches(self):
177 177 for bn, heads in self.iteritems():
178 178 yield (bn, heads) + self._branchtip(heads)
179 179
180 180 def copy(self):
181 181 """return an deep copy of the branchcache object"""
182 182 return branchcache(self, self.tipnode, self.tiprev, self.filteredhash,
183 183 self._closednodes)
184 184
185 185 def write(self, repo):
186 186 try:
187 187 f = repo.vfs(_filename(repo), "w", atomictemp=True)
188 188 cachekey = [hex(self.tipnode), str(self.tiprev)]
189 189 if self.filteredhash is not None:
190 190 cachekey.append(hex(self.filteredhash))
191 191 f.write(" ".join(cachekey) + '\n')
192 192 nodecount = 0
193 193 for label, nodes in sorted(self.iteritems()):
194 194 for node in nodes:
195 195 nodecount += 1
196 196 if node in self._closednodes:
197 197 state = 'c'
198 198 else:
199 199 state = 'o'
200 200 f.write("%s %s %s\n" % (hex(node), state,
201 201 encoding.fromlocal(label)))
202 202 f.close()
203 203 repo.ui.log('branchcache',
204 204 'wrote %s branch cache with %d labels and %d nodes\n',
205 205 repo.filtername, len(self), nodecount)
206 206 except (IOError, OSError, util.Abort), inst:
207 207 repo.ui.debug("couldn't write branch cache: %s\n" % inst)
208 208 # Abort may be raise by read only opener
209 209 pass
210 210
211 211 def update(self, repo, revgen):
212 212 """Given a branchhead cache, self, that may have extra nodes or be
213 213 missing heads, and a generator of nodes that are strictly a superset of
214 214 heads missing, this function updates self to be correct.
215 215 """
216 216 starttime = time.time()
217 217 cl = repo.changelog
218 218 # collect new branch entries
219 219 newbranches = {}
220 220 getbranchinfo = repo.revbranchcache().branchinfo
221 221 for r in revgen:
222 222 branch, closesbranch = getbranchinfo(r)
223 223 newbranches.setdefault(branch, []).append(r)
224 224 if closesbranch:
225 225 self._closednodes.add(cl.node(r))
226 226
227 227 # fetch current topological heads to speed up filtering
228 228 topoheads = set(cl.headrevs())
229 229
230 230 # if older branchheads are reachable from new ones, they aren't
231 231 # really branchheads. Note checking parents is insufficient:
232 232 # 1 (branch a) -> 2 (branch b) -> 3 (branch a)
233 233 for branch, newheadrevs in newbranches.iteritems():
234 234 bheads = self.setdefault(branch, [])
235 235 bheadset = set(cl.rev(node) for node in bheads)
236 236
237 237 # This have been tested True on all internal usage of this function.
238 238 # run it again in case of doubt
239 239 # assert not (set(bheadrevs) & set(newheadrevs))
240 240 newheadrevs.sort()
241 241 bheadset.update(newheadrevs)
242 242
243 243 # This prunes out two kinds of heads - heads that are superseded by
244 244 # a head in newheadrevs, and newheadrevs that are not heads because
245 245 # an existing head is their descendant.
246 246 uncertain = bheadset - topoheads
247 247 if uncertain:
248 248 floorrev = min(uncertain)
249 249 ancestors = set(cl.ancestors(newheadrevs, floorrev))
250 250 bheadset -= ancestors
251 251 bheadrevs = sorted(bheadset)
252 252 self[branch] = [cl.node(rev) for rev in bheadrevs]
253 253 tiprev = bheadrevs[-1]
254 254 if tiprev > self.tiprev:
255 255 self.tipnode = cl.node(tiprev)
256 256 self.tiprev = tiprev
257 257
258 258 if not self.validfor(repo):
259 259 # cache key are not valid anymore
260 260 self.tipnode = nullid
261 261 self.tiprev = nullrev
262 262 for heads in self.values():
263 263 tiprev = max(cl.rev(node) for node in heads)
264 264 if tiprev > self.tiprev:
265 265 self.tipnode = cl.node(tiprev)
266 266 self.tiprev = tiprev
267 267 self.filteredhash = scmutil.filteredhash(repo, self.tiprev)
268 268
269 269 duration = time.time() - starttime
270 270 repo.ui.log('branchcache', 'updated %s branch cache in %.4f seconds\n',
271 271 repo.filtername, duration)
272 272
273 273 # Revision branch info cache
274 274
275 275 _rbcversion = '-v1'
276 276 _rbcnames = 'cache/rbc-names' + _rbcversion
277 277 _rbcrevs = 'cache/rbc-revs' + _rbcversion
278 278 # [4 byte hash prefix][4 byte branch name number with sign bit indicating open]
279 279 _rbcrecfmt = '>4sI'
280 280 _rbcrecsize = calcsize(_rbcrecfmt)
281 281 _rbcnodelen = 4
282 282 _rbcbranchidxmask = 0x7fffffff
283 283 _rbccloseflag = 0x80000000
284 284
285 285 class revbranchcache(object):
286 286 """Persistent cache, mapping from revision number to branch name and close.
287 287 This is a low level cache, independent of filtering.
288 288
289 289 Branch names are stored in rbc-names in internal encoding separated by 0.
290 290 rbc-names is append-only, and each branch name is only stored once and will
291 291 thus have a unique index.
292 292
293 293 The branch info for each revision is stored in rbc-revs as constant size
294 294 records. The whole file is read into memory, but it is only 'parsed' on
295 295 demand. The file is usually append-only but will be truncated if repo
296 296 modification is detected.
297 297 The record for each revision contains the first 4 bytes of the
298 298 corresponding node hash, and the record is only used if it still matches.
299 299 Even a completely trashed rbc-revs fill thus still give the right result
300 300 while converging towards full recovery ... assuming no incorrectly matching
301 301 node hashes.
302 302 The record also contains 4 bytes where 31 bits contains the index of the
303 303 branch and the last bit indicate that it is a branch close commit.
304 304 The usage pattern for rbc-revs is thus somewhat similar to 00changelog.i
305 305 and will grow with it but be 1/8th of its size.
306 306 """
307 307
308 308 def __init__(self, repo, readonly=True):
309 309 assert repo.filtername is None
310 310 self._repo = repo
311 311 self._names = [] # branch names in local encoding with static index
312 312 self._rbcrevs = array('c') # structs of type _rbcrecfmt
313 313 self._rbcsnameslen = 0
314 314 try:
315 315 bndata = repo.vfs.read(_rbcnames)
316 316 self._rbcsnameslen = len(bndata) # for verification before writing
317 317 self._names = [encoding.tolocal(bn) for bn in bndata.split('\0')]
318 318 except (IOError, OSError), inst:
319 repo.ui.debug("couldn't read revision branch cache names: %s\n" %
320 inst)
321 319 if readonly:
322 320 # don't try to use cache - fall back to the slow path
323 321 self.branchinfo = self._branchinfo
324 322
325 323 if self._names:
326 324 try:
327 325 data = repo.vfs.read(_rbcrevs)
328 326 self._rbcrevs.fromstring(data)
329 327 except (IOError, OSError), inst:
330 328 repo.ui.debug("couldn't read revision branch cache: %s\n" %
331 329 inst)
332 330 # remember number of good records on disk
333 331 self._rbcrevslen = min(len(self._rbcrevs) // _rbcrecsize,
334 332 len(repo.changelog))
335 333 if self._rbcrevslen == 0:
336 334 self._names = []
337 335 self._rbcnamescount = len(self._names) # number of good names on disk
338 336 self._namesreverse = dict((b, r) for r, b in enumerate(self._names))
339 337
340 338 def branchinfo(self, rev):
341 339 """Return branch name and close flag for rev, using and updating
342 340 persistent cache."""
343 341 changelog = self._repo.changelog
344 342 rbcrevidx = rev * _rbcrecsize
345 343
346 344 # if requested rev is missing, add and populate all missing revs
347 345 if len(self._rbcrevs) < rbcrevidx + _rbcrecsize:
348 346 self._rbcrevs.extend('\0' * (len(changelog) * _rbcrecsize -
349 347 len(self._rbcrevs)))
350 348
351 349 # fast path: extract data from cache, use it if node is matching
352 350 reponode = changelog.node(rev)[:_rbcnodelen]
353 351 cachenode, branchidx = unpack(
354 352 _rbcrecfmt, buffer(self._rbcrevs, rbcrevidx, _rbcrecsize))
355 353 close = bool(branchidx & _rbccloseflag)
356 354 if close:
357 355 branchidx &= _rbcbranchidxmask
358 356 if cachenode == '\0\0\0\0':
359 357 pass
360 358 elif cachenode == reponode:
361 359 return self._names[branchidx], close
362 360 else:
363 361 # rev/node map has changed, invalidate the cache from here up
364 362 truncate = rbcrevidx + _rbcrecsize
365 363 del self._rbcrevs[truncate:]
366 364 self._rbcrevslen = min(self._rbcrevslen, truncate)
367 365
368 366 # fall back to slow path and make sure it will be written to disk
369 367 return self._branchinfo(rev)
370 368
371 369 def _branchinfo(self, rev):
372 370 """Retrieve branch info from changelog and update _rbcrevs"""
373 371 changelog = self._repo.changelog
374 372 b, close = changelog.branchinfo(rev)
375 373 if b in self._namesreverse:
376 374 branchidx = self._namesreverse[b]
377 375 else:
378 376 branchidx = len(self._names)
379 377 self._names.append(b)
380 378 self._namesreverse[b] = branchidx
381 379 reponode = changelog.node(rev)
382 380 if close:
383 381 branchidx |= _rbccloseflag
384 382 self._setcachedata(rev, reponode, branchidx)
385 383 return b, close
386 384
387 385 def _setcachedata(self, rev, node, branchidx):
388 386 """Writes the node's branch data to the in-memory cache data."""
389 387 rbcrevidx = rev * _rbcrecsize
390 388 rec = array('c')
391 389 rec.fromstring(pack(_rbcrecfmt, node, branchidx))
392 390 self._rbcrevs[rbcrevidx:rbcrevidx + _rbcrecsize] = rec
393 391 self._rbcrevslen = min(self._rbcrevslen, rev)
394 392
395 393 tr = self._repo.currenttransaction()
396 394 if tr:
397 395 tr.addfinalize('write-revbranchcache', self.write)
398 396
399 397 def write(self, tr=None):
400 398 """Save branch cache if it is dirty."""
401 399 repo = self._repo
402 400 if self._rbcnamescount < len(self._names):
403 401 try:
404 402 if self._rbcnamescount != 0:
405 403 f = repo.vfs.open(_rbcnames, 'ab')
406 404 if f.tell() == self._rbcsnameslen:
407 405 f.write('\0')
408 406 else:
409 407 f.close()
410 408 repo.ui.debug("%s changed - rewriting it\n" % _rbcnames)
411 409 self._rbcnamescount = 0
412 410 self._rbcrevslen = 0
413 411 if self._rbcnamescount == 0:
414 412 f = repo.vfs.open(_rbcnames, 'wb')
415 413 f.write('\0'.join(encoding.fromlocal(b)
416 414 for b in self._names[self._rbcnamescount:]))
417 415 self._rbcsnameslen = f.tell()
418 416 f.close()
419 417 except (IOError, OSError, util.Abort), inst:
420 418 repo.ui.debug("couldn't write revision branch cache names: "
421 419 "%s\n" % inst)
422 420 return
423 421 self._rbcnamescount = len(self._names)
424 422
425 423 start = self._rbcrevslen * _rbcrecsize
426 424 if start != len(self._rbcrevs):
427 425 revs = min(len(repo.changelog), len(self._rbcrevs) // _rbcrecsize)
428 426 try:
429 427 f = repo.vfs.open(_rbcrevs, 'ab')
430 428 if f.tell() != start:
431 429 repo.ui.debug("truncating %s to %s\n" % (_rbcrevs, start))
432 430 f.seek(start)
433 431 f.truncate()
434 432 end = revs * _rbcrecsize
435 433 f.write(self._rbcrevs[start:end])
436 434 f.close()
437 435 except (IOError, OSError, util.Abort), inst:
438 436 repo.ui.debug("couldn't write revision branch cache: %s\n" %
439 437 inst)
440 438 return
441 439 self._rbcrevslen = revs
@@ -1,223 +1,222 b''
1 1 #require icasefs
2 2
3 3 $ hg debugfs | grep 'case-sensitive:'
4 4 case-sensitive: no
5 5
6 6 test file addition with bad case
7 7
8 8 $ hg init repo1
9 9 $ cd repo1
10 10 $ echo a > a
11 11 $ hg add A
12 12 adding a
13 13 $ hg st
14 14 A a
15 15 $ hg ci -m adda
16 16 $ hg manifest
17 17 a
18 18 $ cd ..
19 19
20 20 test case collision on rename (issue750)
21 21
22 22 $ hg init repo2
23 23 $ cd repo2
24 24 $ echo a > a
25 25 $ hg --debug ci -Am adda
26 26 adding a
27 27 committing files:
28 28 a
29 29 committing manifest
30 30 committing changelog
31 couldn't read revision branch cache names: * (glob)
32 31 committed changeset 0:07f4944404050f47db2e5c5071e0e84e7a27bba9
33 32
34 33 Case-changing renames should work:
35 34
36 35 $ hg mv a A
37 36 $ hg mv A a
38 37 $ hg st
39 38
40 39 addremove after case-changing rename has no effect (issue4590)
41 40
42 41 $ hg mv a A
43 42 $ hg addremove
44 43 recording removal of a as rename to A (100% similar)
45 44 $ hg revert --all
46 45 forgetting A
47 46 undeleting a
48 47
49 48 test changing case of path components
50 49
51 50 $ mkdir D
52 51 $ echo b > D/b
53 52 $ hg ci -Am addb D/b
54 53 $ hg mv D/b d/b
55 54 D/b: not overwriting - file exists
56 55 $ hg mv D/b d/c
57 56 $ hg st
58 57 A D/c
59 58 R D/b
60 59 $ mv D temp
61 60 $ mv temp d
62 61 $ hg st
63 62 A D/c
64 63 R D/b
65 64 $ hg revert -aq
66 65 $ rm d/c
67 66 $ echo c > D/c
68 67 $ hg add D/c
69 68 $ hg st
70 69 A D/c
71 70 $ hg ci -m addc D/c
72 71 $ hg mv d/b d/e
73 72 moving D/b to D/e (glob)
74 73 $ hg st
75 74 A D/e
76 75 R D/b
77 76 $ hg revert -aq
78 77 $ rm d/e
79 78 $ hg mv d/b D/B
80 79 moving D/b to D/B (glob)
81 80 $ hg st
82 81 A D/B
83 82 R D/b
84 83 $ cd ..
85 84
86 85 test case collision between revisions (issue912)
87 86
88 87 $ hg init repo3
89 88 $ cd repo3
90 89 $ echo a > a
91 90 $ hg ci -Am adda
92 91 adding a
93 92 $ hg rm a
94 93 $ hg ci -Am removea
95 94 $ echo A > A
96 95
97 96 on linux hfs keeps the old case stored, force it
98 97
99 98 $ mv a aa
100 99 $ mv aa A
101 100 $ hg ci -Am addA
102 101 adding A
103 102
104 103 used to fail under case insensitive fs
105 104
106 105 $ hg up -C 0
107 106 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
108 107 $ hg up -C
109 108 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
110 109
111 110 no clobbering of untracked files with wrong casing
112 111
113 112 $ hg up -r null
114 113 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
115 114 $ echo gold > a
116 115 $ hg up
117 116 A: untracked file differs
118 117 abort: untracked files in working directory differ from files in requested revision
119 118 [255]
120 119 $ cat a
121 120 gold
122 121 $ rm a
123 122
124 123 test that normal file in different case on target context is not
125 124 unlinked by largefiles extension.
126 125
127 126 $ cat >> .hg/hgrc <<EOF
128 127 > [extensions]
129 128 > largefiles=
130 129 > EOF
131 130 $ hg update -q -C 1
132 131 $ hg status -A
133 132 $ echo 'A as largefiles' > A
134 133 $ hg add --large A
135 134 $ hg commit -m '#3'
136 135 created new head
137 136 $ hg manifest -r 3
138 137 .hglf/A
139 138 $ hg manifest -r 0
140 139 a
141 140 $ hg update -q -C 0
142 141 $ hg status -A
143 142 C a
144 143 $ hg update -q -C 3
145 144 $ hg update -q 0
146 145
147 146 $ cd ..
148 147
149 148 issue 3342: file in nested directory causes unexpected abort
150 149
151 150 $ hg init issue3342
152 151 $ cd issue3342
153 152
154 153 $ mkdir -p a/B/c/D
155 154 $ echo e > a/B/c/D/e
156 155 $ hg add a/B/c/D/e
157 156
158 157 $ cd ..
159 158
160 159 issue 3340: mq does not handle case changes correctly
161 160
162 161 in addition to reported case, 'hg qrefresh' is also tested against
163 162 case changes.
164 163
165 164 $ echo "[extensions]" >> $HGRCPATH
166 165 $ echo "mq=" >> $HGRCPATH
167 166
168 167 $ hg init issue3340
169 168 $ cd issue3340
170 169
171 170 $ echo a > mIxEdCaSe
172 171 $ hg add mIxEdCaSe
173 172 $ hg commit -m '#0'
174 173 $ hg rename mIxEdCaSe tmp
175 174 $ hg rename tmp MiXeDcAsE
176 175 $ hg status -A
177 176 A MiXeDcAsE
178 177 mIxEdCaSe
179 178 R mIxEdCaSe
180 179 $ hg qnew changecase
181 180 $ hg status -A
182 181 C MiXeDcAsE
183 182
184 183 $ hg qpop -a
185 184 popping changecase
186 185 patch queue now empty
187 186 $ hg qnew refresh-casechange
188 187 $ hg status -A
189 188 C mIxEdCaSe
190 189 $ hg rename mIxEdCaSe tmp
191 190 $ hg rename tmp MiXeDcAsE
192 191 $ hg status -A
193 192 A MiXeDcAsE
194 193 mIxEdCaSe
195 194 R mIxEdCaSe
196 195 $ hg qrefresh
197 196 $ hg status -A
198 197 C MiXeDcAsE
199 198
200 199 $ hg qpop -a
201 200 popping refresh-casechange
202 201 patch queue now empty
203 202 $ hg qnew refresh-pattern
204 203 $ hg status
205 204 $ echo A > A
206 205 $ hg add
207 206 adding A
208 207 $ hg qrefresh a # issue 3271, qrefresh with file handled case wrong
209 208 $ hg status # empty status means the qrefresh worked
210 209
211 210 #if osx
212 211
213 212 We assume anyone running the tests on a case-insensitive volume on OS
214 213 X will be using HFS+. If that's not true, this test will fail.
215 214
216 215 $ rm A
217 216 >>> open(u'a\u200c'.encode('utf-8'), 'w').write('unicode is fun')
218 217 $ hg status
219 218 M A
220 219
221 220 #endif
222 221
223 222 $ cd ..
@@ -1,145 +1,144 b''
1 1 #require svn svn-bindings
2 2
3 3 $ cat >> $HGRCPATH <<EOF
4 4 > [extensions]
5 5 > convert =
6 6 > EOF
7 7
8 8 $ svnadmin create svn-repo
9 9 $ svnadmin load -q svn-repo < "$TESTDIR/svn/encoding.svndump"
10 10
11 11 Convert while testing all possible outputs
12 12
13 13 $ hg --debug convert svn-repo A-hg
14 14 initializing destination A-hg repository
15 15 reparent to file://*/svn-repo (glob)
16 16 run hg sink pre-conversion action
17 17 scanning source...
18 18 found trunk at 'trunk'
19 19 found tags at 'tags'
20 20 found branches at 'branches'
21 21 found branch branch\xc3\xa9 at 5 (esc)
22 22 found branch branch\xc3\xa9e at 6 (esc)
23 23 scanning: 1/4 revisions (25.00%)
24 24 reparent to file://*/svn-repo/trunk (glob)
25 25 fetching revision log for "/trunk" from 4 to 0
26 26 parsing revision 4 (2 changes)
27 27 parsing revision 3 (4 changes)
28 28 parsing revision 2 (3 changes)
29 29 parsing revision 1 (3 changes)
30 30 no copyfrom path, don't know what to do.
31 31 '/branches' is not under '/trunk', ignoring
32 32 '/tags' is not under '/trunk', ignoring
33 33 scanning: 2/4 revisions (50.00%)
34 34 reparent to file://*/svn-repo/branches/branch%C3%A9 (glob)
35 35 fetching revision log for "/branches/branch\xc3\xa9" from 5 to 0 (esc)
36 36 parsing revision 5 (1 changes)
37 37 reparent to file://*/svn-repo (glob)
38 38 reparent to file://*/svn-repo/branches/branch%C3%A9 (glob)
39 39 found parent of branch /branches/branch\xc3\xa9 at 4: /trunk (esc)
40 40 scanning: 3/4 revisions (75.00%)
41 41 reparent to file://*/svn-repo/branches/branch%C3%A9e (glob)
42 42 fetching revision log for "/branches/branch\xc3\xa9e" from 6 to 0 (esc)
43 43 parsing revision 6 (1 changes)
44 44 reparent to file://*/svn-repo (glob)
45 45 reparent to file://*/svn-repo/branches/branch%C3%A9e (glob)
46 46 found parent of branch /branches/branch\xc3\xa9e at 5: /branches/branch\xc3\xa9 (esc)
47 47 scanning: 4/4 revisions (100.00%)
48 48 scanning: 5/4 revisions (125.00%)
49 49 scanning: 6/4 revisions (150.00%)
50 50 sorting...
51 51 converting...
52 52 5 init projA
53 53 source: svn:afeb9c47-92ff-4c0c-9f72-e1f6eb8ac9af/trunk@1
54 54 converting: 0/6 revisions (0.00%)
55 55 committing changelog
56 couldn't read revision branch cache names: * (glob)
57 56 4 hello
58 57 source: svn:afeb9c47-92ff-4c0c-9f72-e1f6eb8ac9af/trunk@2
59 58 converting: 1/6 revisions (16.67%)
60 59 reparent to file://*/svn-repo/trunk (glob)
61 60 scanning paths: /trunk/\xc3\xa0 0/3 (0.00%) (esc)
62 61 scanning paths: /trunk/\xc3\xa0/e\xcc\x81 1/3 (33.33%) (esc)
63 62 scanning paths: /trunk/\xc3\xa9 2/3 (66.67%) (esc)
64 63 committing files:
65 64 \xc3\xa0/e\xcc\x81 (esc)
66 65 getting files: \xc3\xa0/e\xcc\x81 1/2 (50.00%) (esc)
67 66 \xc3\xa9 (esc)
68 67 getting files: \xc3\xa9 2/2 (100.00%) (esc)
69 68 committing manifest
70 69 committing changelog
71 70 3 copy files
72 71 source: svn:afeb9c47-92ff-4c0c-9f72-e1f6eb8ac9af/trunk@3
73 72 converting: 2/6 revisions (33.33%)
74 73 scanning paths: /trunk/\xc3\xa0 0/4 (0.00%) (esc)
75 74 gone from -1
76 75 reparent to file://*/svn-repo (glob)
77 76 reparent to file://*/svn-repo/trunk (glob)
78 77 scanning paths: /trunk/\xc3\xa8 1/4 (25.00%) (esc)
79 78 copied to \xc3\xa8 from \xc3\xa9@2 (esc)
80 79 scanning paths: /trunk/\xc3\xa9 2/4 (50.00%) (esc)
81 80 gone from -1
82 81 reparent to file://*/svn-repo (glob)
83 82 reparent to file://*/svn-repo/trunk (glob)
84 83 scanning paths: /trunk/\xc3\xb9 3/4 (75.00%) (esc)
85 84 mark /trunk/\xc3\xb9 came from \xc3\xa0:2 (esc)
86 85 getting files: \xc3\xa0/e\xcc\x81 1/4 (25.00%) (esc)
87 86 getting files: \xc3\xa9 2/4 (50.00%) (esc)
88 87 committing files:
89 88 \xc3\xa8 (esc)
90 89 getting files: \xc3\xa8 3/4 (75.00%) (esc)
91 90 \xc3\xa8: copy \xc3\xa9:6b67ccefd5ce6de77e7ead4f5292843a0255329f (esc)
92 91 \xc3\xb9/e\xcc\x81 (esc)
93 92 getting files: \xc3\xb9/e\xcc\x81 4/4 (100.00%) (esc)
94 93 \xc3\xb9/e\xcc\x81: copy \xc3\xa0/e\xcc\x81:a9092a3d84a37b9993b5c73576f6de29b7ea50f6 (esc)
95 94 committing manifest
96 95 committing changelog
97 96 2 remove files
98 97 source: svn:afeb9c47-92ff-4c0c-9f72-e1f6eb8ac9af/trunk@4
99 98 converting: 3/6 revisions (50.00%)
100 99 scanning paths: /trunk/\xc3\xa8 0/2 (0.00%) (esc)
101 100 gone from -1
102 101 reparent to file://*/svn-repo (glob)
103 102 reparent to file://*/svn-repo/trunk (glob)
104 103 scanning paths: /trunk/\xc3\xb9 1/2 (50.00%) (esc)
105 104 gone from -1
106 105 reparent to file://*/svn-repo (glob)
107 106 reparent to file://*/svn-repo/trunk (glob)
108 107 getting files: \xc3\xa8 1/2 (50.00%) (esc)
109 108 getting files: \xc3\xb9/e\xcc\x81 2/2 (100.00%) (esc)
110 109 committing files:
111 110 committing manifest
112 111 committing changelog
113 112 1 branch to branch?
114 113 source: svn:afeb9c47-92ff-4c0c-9f72-e1f6eb8ac9af/branches/branch?@5
115 114 converting: 4/6 revisions (66.67%)
116 115 reparent to file://*/svn-repo/branches/branch%C3%A9 (glob)
117 116 scanning paths: /branches/branch\xc3\xa9 0/1 (0.00%) (esc)
118 117 committing changelog
119 118 0 branch to branch?e
120 119 source: svn:afeb9c47-92ff-4c0c-9f72-e1f6eb8ac9af/branches/branch?e@6
121 120 converting: 5/6 revisions (83.33%)
122 121 reparent to file://*/svn-repo/branches/branch%C3%A9e (glob)
123 122 scanning paths: /branches/branch\xc3\xa9e 0/1 (0.00%) (esc)
124 123 committing changelog
125 124 reparent to file://*/svn-repo (glob)
126 125 reparent to file://*/svn-repo/branches/branch%C3%A9e (glob)
127 126 reparent to file://*/svn-repo (glob)
128 127 reparent to file://*/svn-repo/branches/branch%C3%A9e (glob)
129 128 updating tags
130 129 committing files:
131 130 .hgtags
132 131 committing manifest
133 132 committing changelog
134 133 run hg sink post-conversion action
135 134 $ cd A-hg
136 135 $ hg up
137 136 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
138 137
139 138 Check tags are in UTF-8
140 139
141 140 $ cat .hgtags
142 141 e94e4422020e715add80525e8f0f46c9968689f1 branch\xc3\xa9e (esc)
143 142 f7e66f98380ed1e53a797c5c7a7a2616a7ab377d branch\xc3\xa9 (esc)
144 143
145 144 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now