##// END OF EJS Templates
py3: make largefiles/lfutil.py use absolute_import
liscju -
r29309:bfc10525 default
parent child Browse files
Show More
@@ -1,655 +1,664 b''
1 1 # Copyright 2009-2010 Gregory P. Ward
2 2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 3 # Copyright 2010-2011 Fog Creek Software
4 4 # Copyright 2010-2011 Unity Technologies
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 '''largefiles utility code: must not import other modules in this package.'''
10 from __future__ import absolute_import
10 11
12 import copy
11 13 import os
12 14 import platform
13 15 import stat
14 import copy
16
17 from mercurial.i18n import _
15 18
16 from mercurial import dirstate, httpconnection, match as match_, util, scmutil
17 from mercurial.i18n import _
18 from mercurial import node, error
19 from mercurial import (
20 dirstate,
21 error,
22 httpconnection,
23 match as match_,
24 node,
25 scmutil,
26 util,
27 )
19 28
20 29 shortname = '.hglf'
21 30 shortnameslash = shortname + '/'
22 31 longname = 'largefiles'
23 32
24 33
25 34 # -- Private worker functions ------------------------------------------
26 35
27 36 def getminsize(ui, assumelfiles, opt, default=10):
28 37 lfsize = opt
29 38 if not lfsize and assumelfiles:
30 39 lfsize = ui.config(longname, 'minsize', default=default)
31 40 if lfsize:
32 41 try:
33 42 lfsize = float(lfsize)
34 43 except ValueError:
35 44 raise error.Abort(_('largefiles: size must be number (not %s)\n')
36 45 % lfsize)
37 46 if lfsize is None:
38 47 raise error.Abort(_('minimum size for largefiles must be specified'))
39 48 return lfsize
40 49
41 50 def link(src, dest):
42 51 """Try to create hardlink - if that fails, efficiently make a copy."""
43 52 util.makedirs(os.path.dirname(dest))
44 53 try:
45 54 util.oslink(src, dest)
46 55 except OSError:
47 56 # if hardlinks fail, fallback on atomic copy
48 57 dst = util.atomictempfile(dest)
49 58 for chunk in util.filechunkiter(open(src, 'rb')):
50 59 dst.write(chunk)
51 60 dst.close()
52 61 os.chmod(dest, os.stat(src).st_mode)
53 62
54 63 def usercachepath(ui, hash):
55 64 '''Return the correct location in the "global" largefiles cache for a file
56 65 with the given hash.
57 66 This cache is used for sharing of largefiles across repositories - both
58 67 to preserve download bandwidth and storage space.'''
59 68 return os.path.join(_usercachedir(ui), hash)
60 69
61 70 def _usercachedir(ui):
62 71 '''Return the location of the "global" largefiles cache.'''
63 72 path = ui.configpath(longname, 'usercache', None)
64 73 if path:
65 74 return path
66 75 if os.name == 'nt':
67 76 appdata = os.getenv('LOCALAPPDATA', os.getenv('APPDATA'))
68 77 if appdata:
69 78 return os.path.join(appdata, longname)
70 79 elif platform.system() == 'Darwin':
71 80 home = os.getenv('HOME')
72 81 if home:
73 82 return os.path.join(home, 'Library', 'Caches', longname)
74 83 elif os.name == 'posix':
75 84 path = os.getenv('XDG_CACHE_HOME')
76 85 if path:
77 86 return os.path.join(path, longname)
78 87 home = os.getenv('HOME')
79 88 if home:
80 89 return os.path.join(home, '.cache', longname)
81 90 else:
82 91 raise error.Abort(_('unknown operating system: %s\n') % os.name)
83 92 raise error.Abort(_('unknown %s usercache location\n') % longname)
84 93
85 94 def inusercache(ui, hash):
86 95 path = usercachepath(ui, hash)
87 96 return os.path.exists(path)
88 97
89 98 def findfile(repo, hash):
90 99 '''Return store path of the largefile with the specified hash.
91 100 As a side effect, the file might be linked from user cache.
92 101 Return None if the file can't be found locally.'''
93 102 path, exists = findstorepath(repo, hash)
94 103 if exists:
95 104 repo.ui.note(_('found %s in store\n') % hash)
96 105 return path
97 106 elif inusercache(repo.ui, hash):
98 107 repo.ui.note(_('found %s in system cache\n') % hash)
99 108 path = storepath(repo, hash)
100 109 link(usercachepath(repo.ui, hash), path)
101 110 return path
102 111 return None
103 112
104 113 class largefilesdirstate(dirstate.dirstate):
105 114 def __getitem__(self, key):
106 115 return super(largefilesdirstate, self).__getitem__(unixpath(key))
107 116 def normal(self, f):
108 117 return super(largefilesdirstate, self).normal(unixpath(f))
109 118 def remove(self, f):
110 119 return super(largefilesdirstate, self).remove(unixpath(f))
111 120 def add(self, f):
112 121 return super(largefilesdirstate, self).add(unixpath(f))
113 122 def drop(self, f):
114 123 return super(largefilesdirstate, self).drop(unixpath(f))
115 124 def forget(self, f):
116 125 return super(largefilesdirstate, self).forget(unixpath(f))
117 126 def normallookup(self, f):
118 127 return super(largefilesdirstate, self).normallookup(unixpath(f))
119 128 def _ignore(self, f):
120 129 return False
121 130 def write(self, tr=False):
122 131 # (1) disable PENDING mode always
123 132 # (lfdirstate isn't yet managed as a part of the transaction)
124 133 # (2) avoid develwarn 'use dirstate.write with ....'
125 134 super(largefilesdirstate, self).write(None)
126 135
127 136 def openlfdirstate(ui, repo, create=True):
128 137 '''
129 138 Return a dirstate object that tracks largefiles: i.e. its root is
130 139 the repo root, but it is saved in .hg/largefiles/dirstate.
131 140 '''
132 141 vfs = repo.vfs
133 142 lfstoredir = longname
134 143 opener = scmutil.opener(vfs.join(lfstoredir))
135 144 lfdirstate = largefilesdirstate(opener, ui, repo.root,
136 145 repo.dirstate._validate)
137 146
138 147 # If the largefiles dirstate does not exist, populate and create
139 148 # it. This ensures that we create it on the first meaningful
140 149 # largefiles operation in a new clone.
141 150 if create and not vfs.exists(vfs.join(lfstoredir, 'dirstate')):
142 151 matcher = getstandinmatcher(repo)
143 152 standins = repo.dirstate.walk(matcher, [], False, False)
144 153
145 154 if len(standins) > 0:
146 155 vfs.makedirs(lfstoredir)
147 156
148 157 for standin in standins:
149 158 lfile = splitstandin(standin)
150 159 lfdirstate.normallookup(lfile)
151 160 return lfdirstate
152 161
153 162 def lfdirstatestatus(lfdirstate, repo):
154 163 wctx = repo['.']
155 164 match = match_.always(repo.root, repo.getcwd())
156 165 unsure, s = lfdirstate.status(match, [], False, False, False)
157 166 modified, clean = s.modified, s.clean
158 167 for lfile in unsure:
159 168 try:
160 169 fctx = wctx[standin(lfile)]
161 170 except LookupError:
162 171 fctx = None
163 172 if not fctx or fctx.data().strip() != hashfile(repo.wjoin(lfile)):
164 173 modified.append(lfile)
165 174 else:
166 175 clean.append(lfile)
167 176 lfdirstate.normal(lfile)
168 177 return s
169 178
170 179 def listlfiles(repo, rev=None, matcher=None):
171 180 '''return a list of largefiles in the working copy or the
172 181 specified changeset'''
173 182
174 183 if matcher is None:
175 184 matcher = getstandinmatcher(repo)
176 185
177 186 # ignore unknown files in working directory
178 187 return [splitstandin(f)
179 188 for f in repo[rev].walk(matcher)
180 189 if rev is not None or repo.dirstate[f] != '?']
181 190
182 191 def instore(repo, hash, forcelocal=False):
183 192 '''Return true if a largefile with the given hash exists in the user
184 193 cache.'''
185 194 return os.path.exists(storepath(repo, hash, forcelocal))
186 195
187 196 def storepath(repo, hash, forcelocal=False):
188 197 '''Return the correct location in the repository largefiles cache for a
189 198 file with the given hash.'''
190 199 if not forcelocal and repo.shared():
191 200 return repo.vfs.reljoin(repo.sharedpath, longname, hash)
192 201 return repo.join(longname, hash)
193 202
194 203 def findstorepath(repo, hash):
195 204 '''Search through the local store path(s) to find the file for the given
196 205 hash. If the file is not found, its path in the primary store is returned.
197 206 The return value is a tuple of (path, exists(path)).
198 207 '''
199 208 # For shared repos, the primary store is in the share source. But for
200 209 # backward compatibility, force a lookup in the local store if it wasn't
201 210 # found in the share source.
202 211 path = storepath(repo, hash, False)
203 212
204 213 if instore(repo, hash):
205 214 return (path, True)
206 215 elif repo.shared() and instore(repo, hash, True):
207 216 return storepath(repo, hash, True)
208 217
209 218 return (path, False)
210 219
211 220 def copyfromcache(repo, hash, filename):
212 221 '''Copy the specified largefile from the repo or system cache to
213 222 filename in the repository. Return true on success or false if the
214 223 file was not found in either cache (which should not happened:
215 224 this is meant to be called only after ensuring that the needed
216 225 largefile exists in the cache).'''
217 226 wvfs = repo.wvfs
218 227 path = findfile(repo, hash)
219 228 if path is None:
220 229 return False
221 230 wvfs.makedirs(wvfs.dirname(wvfs.join(filename)))
222 231 # The write may fail before the file is fully written, but we
223 232 # don't use atomic writes in the working copy.
224 233 with open(path, 'rb') as srcfd:
225 234 with wvfs(filename, 'wb') as destfd:
226 235 gothash = copyandhash(srcfd, destfd)
227 236 if gothash != hash:
228 237 repo.ui.warn(_('%s: data corruption in %s with hash %s\n')
229 238 % (filename, path, gothash))
230 239 wvfs.unlink(filename)
231 240 return False
232 241 return True
233 242
234 243 def copytostore(repo, rev, file, uploaded=False):
235 244 wvfs = repo.wvfs
236 245 hash = readstandin(repo, file, rev)
237 246 if instore(repo, hash):
238 247 return
239 248 if wvfs.exists(file):
240 249 copytostoreabsolute(repo, wvfs.join(file), hash)
241 250 else:
242 251 repo.ui.warn(_("%s: largefile %s not available from local store\n") %
243 252 (file, hash))
244 253
245 254 def copyalltostore(repo, node):
246 255 '''Copy all largefiles in a given revision to the store'''
247 256
248 257 ctx = repo[node]
249 258 for filename in ctx.files():
250 259 if isstandin(filename) and filename in ctx.manifest():
251 260 realfile = splitstandin(filename)
252 261 copytostore(repo, ctx.node(), realfile)
253 262
254 263
255 264 def copytostoreabsolute(repo, file, hash):
256 265 if inusercache(repo.ui, hash):
257 266 link(usercachepath(repo.ui, hash), storepath(repo, hash))
258 267 else:
259 268 util.makedirs(os.path.dirname(storepath(repo, hash)))
260 269 dst = util.atomictempfile(storepath(repo, hash),
261 270 createmode=repo.store.createmode)
262 271 for chunk in util.filechunkiter(open(file, 'rb')):
263 272 dst.write(chunk)
264 273 dst.close()
265 274 linktousercache(repo, hash)
266 275
267 276 def linktousercache(repo, hash):
268 277 '''Link / copy the largefile with the specified hash from the store
269 278 to the cache.'''
270 279 path = usercachepath(repo.ui, hash)
271 280 link(storepath(repo, hash), path)
272 281
273 282 def getstandinmatcher(repo, rmatcher=None):
274 283 '''Return a match object that applies rmatcher to the standin directory'''
275 284 wvfs = repo.wvfs
276 285 standindir = shortname
277 286
278 287 # no warnings about missing files or directories
279 288 badfn = lambda f, msg: None
280 289
281 290 if rmatcher and not rmatcher.always():
282 291 pats = [wvfs.join(standindir, pat) for pat in rmatcher.files()]
283 292 if not pats:
284 293 pats = [wvfs.join(standindir)]
285 294 match = scmutil.match(repo[None], pats, badfn=badfn)
286 295 # if pats is empty, it would incorrectly always match, so clear _always
287 296 match._always = False
288 297 else:
289 298 # no patterns: relative to repo root
290 299 match = scmutil.match(repo[None], [wvfs.join(standindir)], badfn=badfn)
291 300 return match
292 301
293 302 def composestandinmatcher(repo, rmatcher):
294 303 '''Return a matcher that accepts standins corresponding to the
295 304 files accepted by rmatcher. Pass the list of files in the matcher
296 305 as the paths specified by the user.'''
297 306 smatcher = getstandinmatcher(repo, rmatcher)
298 307 isstandin = smatcher.matchfn
299 308 def composedmatchfn(f):
300 309 return isstandin(f) and rmatcher.matchfn(splitstandin(f))
301 310 smatcher.matchfn = composedmatchfn
302 311
303 312 return smatcher
304 313
305 314 def standin(filename):
306 315 '''Return the repo-relative path to the standin for the specified big
307 316 file.'''
308 317 # Notes:
309 318 # 1) Some callers want an absolute path, but for instance addlargefiles
310 319 # needs it repo-relative so it can be passed to repo[None].add(). So
311 320 # leave it up to the caller to use repo.wjoin() to get an absolute path.
312 321 # 2) Join with '/' because that's what dirstate always uses, even on
313 322 # Windows. Change existing separator to '/' first in case we are
314 323 # passed filenames from an external source (like the command line).
315 324 return shortnameslash + util.pconvert(filename)
316 325
317 326 def isstandin(filename):
318 327 '''Return true if filename is a big file standin. filename must be
319 328 in Mercurial's internal form (slash-separated).'''
320 329 return filename.startswith(shortnameslash)
321 330
322 331 def splitstandin(filename):
323 332 # Split on / because that's what dirstate always uses, even on Windows.
324 333 # Change local separator to / first just in case we are passed filenames
325 334 # from an external source (like the command line).
326 335 bits = util.pconvert(filename).split('/', 1)
327 336 if len(bits) == 2 and bits[0] == shortname:
328 337 return bits[1]
329 338 else:
330 339 return None
331 340
332 341 def updatestandin(repo, standin):
333 342 file = repo.wjoin(splitstandin(standin))
334 343 if repo.wvfs.exists(splitstandin(standin)):
335 344 hash = hashfile(file)
336 345 executable = getexecutable(file)
337 346 writestandin(repo, standin, hash, executable)
338 347 else:
339 348 raise error.Abort(_('%s: file not found!') % splitstandin(standin))
340 349
341 350 def readstandin(repo, filename, node=None):
342 351 '''read hex hash from standin for filename at given node, or working
343 352 directory if no node is given'''
344 353 return repo[node][standin(filename)].data().strip()
345 354
346 355 def writestandin(repo, standin, hash, executable):
347 356 '''write hash to <repo.root>/<standin>'''
348 357 repo.wwrite(standin, hash + '\n', executable and 'x' or '')
349 358
350 359 def copyandhash(instream, outfile):
351 360 '''Read bytes from instream (iterable) and write them to outfile,
352 361 computing the SHA-1 hash of the data along the way. Return the hash.'''
353 362 hasher = util.sha1('')
354 363 for data in instream:
355 364 hasher.update(data)
356 365 outfile.write(data)
357 366 return hasher.hexdigest()
358 367
359 368 def hashrepofile(repo, file):
360 369 return hashfile(repo.wjoin(file))
361 370
362 371 def hashfile(file):
363 372 if not os.path.exists(file):
364 373 return ''
365 374 hasher = util.sha1('')
366 375 fd = open(file, 'rb')
367 376 for data in util.filechunkiter(fd, 128 * 1024):
368 377 hasher.update(data)
369 378 fd.close()
370 379 return hasher.hexdigest()
371 380
372 381 def getexecutable(filename):
373 382 mode = os.stat(filename).st_mode
374 383 return ((mode & stat.S_IXUSR) and
375 384 (mode & stat.S_IXGRP) and
376 385 (mode & stat.S_IXOTH))
377 386
378 387 def urljoin(first, second, *arg):
379 388 def join(left, right):
380 389 if not left.endswith('/'):
381 390 left += '/'
382 391 if right.startswith('/'):
383 392 right = right[1:]
384 393 return left + right
385 394
386 395 url = join(first, second)
387 396 for a in arg:
388 397 url = join(url, a)
389 398 return url
390 399
391 400 def hexsha1(data):
392 401 """hexsha1 returns the hex-encoded sha1 sum of the data in the file-like
393 402 object data"""
394 403 h = util.sha1()
395 404 for chunk in util.filechunkiter(data):
396 405 h.update(chunk)
397 406 return h.hexdigest()
398 407
399 408 def httpsendfile(ui, filename):
400 409 return httpconnection.httpsendfile(ui, filename, 'rb')
401 410
402 411 def unixpath(path):
403 412 '''Return a version of path normalized for use with the lfdirstate.'''
404 413 return util.pconvert(os.path.normpath(path))
405 414
406 415 def islfilesrepo(repo):
407 416 '''Return true if the repo is a largefile repo.'''
408 417 if ('largefiles' in repo.requirements and
409 418 any(shortnameslash in f[0] for f in repo.store.datafiles())):
410 419 return True
411 420
412 421 return any(openlfdirstate(repo.ui, repo, False))
413 422
414 423 class storeprotonotcapable(Exception):
415 424 def __init__(self, storetypes):
416 425 self.storetypes = storetypes
417 426
418 427 def getstandinsstate(repo):
419 428 standins = []
420 429 matcher = getstandinmatcher(repo)
421 430 for standin in repo.dirstate.walk(matcher, [], False, False):
422 431 lfile = splitstandin(standin)
423 432 try:
424 433 hash = readstandin(repo, lfile)
425 434 except IOError:
426 435 hash = None
427 436 standins.append((lfile, hash))
428 437 return standins
429 438
430 439 def synclfdirstate(repo, lfdirstate, lfile, normallookup):
431 440 lfstandin = standin(lfile)
432 441 if lfstandin in repo.dirstate:
433 442 stat = repo.dirstate._map[lfstandin]
434 443 state, mtime = stat[0], stat[3]
435 444 else:
436 445 state, mtime = '?', -1
437 446 if state == 'n':
438 447 if (normallookup or mtime < 0 or
439 448 not repo.wvfs.exists(lfile)):
440 449 # state 'n' doesn't ensure 'clean' in this case
441 450 lfdirstate.normallookup(lfile)
442 451 else:
443 452 lfdirstate.normal(lfile)
444 453 elif state == 'm':
445 454 lfdirstate.normallookup(lfile)
446 455 elif state == 'r':
447 456 lfdirstate.remove(lfile)
448 457 elif state == 'a':
449 458 lfdirstate.add(lfile)
450 459 elif state == '?':
451 460 lfdirstate.drop(lfile)
452 461
453 462 def markcommitted(orig, ctx, node):
454 463 repo = ctx.repo()
455 464
456 465 orig(node)
457 466
458 467 # ATTENTION: "ctx.files()" may differ from "repo[node].files()"
459 468 # because files coming from the 2nd parent are omitted in the latter.
460 469 #
461 470 # The former should be used to get targets of "synclfdirstate",
462 471 # because such files:
463 472 # - are marked as "a" by "patch.patch()" (e.g. via transplant), and
464 473 # - have to be marked as "n" after commit, but
465 474 # - aren't listed in "repo[node].files()"
466 475
467 476 lfdirstate = openlfdirstate(repo.ui, repo)
468 477 for f in ctx.files():
469 478 if isstandin(f):
470 479 lfile = splitstandin(f)
471 480 synclfdirstate(repo, lfdirstate, lfile, False)
472 481 lfdirstate.write()
473 482
474 483 # As part of committing, copy all of the largefiles into the cache.
475 484 copyalltostore(repo, node)
476 485
477 486 def getlfilestoupdate(oldstandins, newstandins):
478 487 changedstandins = set(oldstandins).symmetric_difference(set(newstandins))
479 488 filelist = []
480 489 for f in changedstandins:
481 490 if f[0] not in filelist:
482 491 filelist.append(f[0])
483 492 return filelist
484 493
485 494 def getlfilestoupload(repo, missing, addfunc):
486 495 for i, n in enumerate(missing):
487 496 repo.ui.progress(_('finding outgoing largefiles'), i,
488 497 unit=_('revisions'), total=len(missing))
489 498 parents = [p for p in repo[n].parents() if p != node.nullid]
490 499
491 500 oldlfstatus = repo.lfstatus
492 501 repo.lfstatus = False
493 502 try:
494 503 ctx = repo[n]
495 504 finally:
496 505 repo.lfstatus = oldlfstatus
497 506
498 507 files = set(ctx.files())
499 508 if len(parents) == 2:
500 509 mc = ctx.manifest()
501 510 mp1 = ctx.parents()[0].manifest()
502 511 mp2 = ctx.parents()[1].manifest()
503 512 for f in mp1:
504 513 if f not in mc:
505 514 files.add(f)
506 515 for f in mp2:
507 516 if f not in mc:
508 517 files.add(f)
509 518 for f in mc:
510 519 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
511 520 files.add(f)
512 521 for fn in files:
513 522 if isstandin(fn) and fn in ctx:
514 523 addfunc(fn, ctx[fn].data().strip())
515 524 repo.ui.progress(_('finding outgoing largefiles'), None)
516 525
517 526 def updatestandinsbymatch(repo, match):
518 527 '''Update standins in the working directory according to specified match
519 528
520 529 This returns (possibly modified) ``match`` object to be used for
521 530 subsequent commit process.
522 531 '''
523 532
524 533 ui = repo.ui
525 534
526 535 # Case 1: user calls commit with no specific files or
527 536 # include/exclude patterns: refresh and commit all files that
528 537 # are "dirty".
529 538 if match is None or match.always():
530 539 # Spend a bit of time here to get a list of files we know
531 540 # are modified so we can compare only against those.
532 541 # It can cost a lot of time (several seconds)
533 542 # otherwise to update all standins if the largefiles are
534 543 # large.
535 544 lfdirstate = openlfdirstate(ui, repo)
536 545 dirtymatch = match_.always(repo.root, repo.getcwd())
537 546 unsure, s = lfdirstate.status(dirtymatch, [], False, False,
538 547 False)
539 548 modifiedfiles = unsure + s.modified + s.added + s.removed
540 549 lfiles = listlfiles(repo)
541 550 # this only loops through largefiles that exist (not
542 551 # removed/renamed)
543 552 for lfile in lfiles:
544 553 if lfile in modifiedfiles:
545 554 if repo.wvfs.exists(standin(lfile)):
546 555 # this handles the case where a rebase is being
547 556 # performed and the working copy is not updated
548 557 # yet.
549 558 if repo.wvfs.exists(lfile):
550 559 updatestandin(repo,
551 560 standin(lfile))
552 561
553 562 return match
554 563
555 564 lfiles = listlfiles(repo)
556 565 match._files = repo._subdirlfs(match.files(), lfiles)
557 566
558 567 # Case 2: user calls commit with specified patterns: refresh
559 568 # any matching big files.
560 569 smatcher = composestandinmatcher(repo, match)
561 570 standins = repo.dirstate.walk(smatcher, [], False, False)
562 571
563 572 # No matching big files: get out of the way and pass control to
564 573 # the usual commit() method.
565 574 if not standins:
566 575 return match
567 576
568 577 # Refresh all matching big files. It's possible that the
569 578 # commit will end up failing, in which case the big files will
570 579 # stay refreshed. No harm done: the user modified them and
571 580 # asked to commit them, so sooner or later we're going to
572 581 # refresh the standins. Might as well leave them refreshed.
573 582 lfdirstate = openlfdirstate(ui, repo)
574 583 for fstandin in standins:
575 584 lfile = splitstandin(fstandin)
576 585 if lfdirstate[lfile] != 'r':
577 586 updatestandin(repo, fstandin)
578 587
579 588 # Cook up a new matcher that only matches regular files or
580 589 # standins corresponding to the big files requested by the
581 590 # user. Have to modify _files to prevent commit() from
582 591 # complaining "not tracked" for big files.
583 592 match = copy.copy(match)
584 593 origmatchfn = match.matchfn
585 594
586 595 # Check both the list of largefiles and the list of
587 596 # standins because if a largefile was removed, it
588 597 # won't be in the list of largefiles at this point
589 598 match._files += sorted(standins)
590 599
591 600 actualfiles = []
592 601 for f in match._files:
593 602 fstandin = standin(f)
594 603
595 604 # For largefiles, only one of the normal and standin should be
596 605 # committed (except if one of them is a remove). In the case of a
597 606 # standin removal, drop the normal file if it is unknown to dirstate.
598 607 # Thus, skip plain largefile names but keep the standin.
599 608 if f in lfiles or fstandin in standins:
600 609 if repo.dirstate[fstandin] != 'r':
601 610 if repo.dirstate[f] != 'r':
602 611 continue
603 612 elif repo.dirstate[f] == '?':
604 613 continue
605 614
606 615 actualfiles.append(f)
607 616 match._files = actualfiles
608 617
609 618 def matchfn(f):
610 619 if origmatchfn(f):
611 620 return f not in lfiles
612 621 else:
613 622 return f in standins
614 623
615 624 match.matchfn = matchfn
616 625
617 626 return match
618 627
619 628 class automatedcommithook(object):
620 629 '''Stateful hook to update standins at the 1st commit of resuming
621 630
622 631 For efficiency, updating standins in the working directory should
623 632 be avoided while automated committing (like rebase, transplant and
624 633 so on), because they should be updated before committing.
625 634
626 635 But the 1st commit of resuming automated committing (e.g. ``rebase
627 636 --continue``) should update them, because largefiles may be
628 637 modified manually.
629 638 '''
630 639 def __init__(self, resuming):
631 640 self.resuming = resuming
632 641
633 642 def __call__(self, repo, match):
634 643 if self.resuming:
635 644 self.resuming = False # avoids updating at subsequent commits
636 645 return updatestandinsbymatch(repo, match)
637 646 else:
638 647 return match
639 648
640 649 def getstatuswriter(ui, repo, forcibly=None):
641 650 '''Return the function to write largefiles specific status out
642 651
643 652 If ``forcibly`` is ``None``, this returns the last element of
644 653 ``repo._lfstatuswriters`` as "default" writer function.
645 654
646 655 Otherwise, this returns the function to always write out (or
647 656 ignore if ``not forcibly``) status.
648 657 '''
649 658 if forcibly is None and util.safehasattr(repo, '_largefilesenabled'):
650 659 return repo._lfstatuswriters[-1]
651 660 else:
652 661 if forcibly:
653 662 return ui.status # forcibly WRITE OUT
654 663 else:
655 664 return lambda *msg, **opts: None # forcibly IGNORE
@@ -1,159 +1,158 b''
1 1 #require test-repo
2 2
3 3 $ . "$TESTDIR/helpers-testrepo.sh"
4 4 $ cd "$TESTDIR"/..
5 5
6 6 $ hg files 'set:(**.py)' | sed 's|\\|/|g' | xargs python contrib/check-py3-compat.py
7 7 hgext/fsmonitor/pywatchman/__init__.py not using absolute_import
8 8 hgext/fsmonitor/pywatchman/__init__.py requires print_function
9 9 hgext/fsmonitor/pywatchman/capabilities.py not using absolute_import
10 10 hgext/fsmonitor/pywatchman/pybser.py not using absolute_import
11 11 hgext/highlight/__init__.py not using absolute_import
12 12 hgext/highlight/highlight.py not using absolute_import
13 hgext/largefiles/lfutil.py not using absolute_import
14 13 hgext/largefiles/localstore.py not using absolute_import
15 14 hgext/largefiles/overrides.py not using absolute_import
16 15 hgext/largefiles/proto.py not using absolute_import
17 16 hgext/largefiles/remotestore.py not using absolute_import
18 17 hgext/largefiles/reposetup.py not using absolute_import
19 18 hgext/largefiles/uisetup.py not using absolute_import
20 19 hgext/largefiles/wirestore.py not using absolute_import
21 20 hgext/share.py not using absolute_import
22 21 hgext/win32text.py not using absolute_import
23 22 i18n/check-translation.py not using absolute_import
24 23 i18n/polib.py not using absolute_import
25 24 setup.py not using absolute_import
26 25 tests/heredoctest.py requires print_function
27 26 tests/md5sum.py not using absolute_import
28 27 tests/readlink.py not using absolute_import
29 28 tests/run-tests.py not using absolute_import
30 29 tests/test-demandimport.py not using absolute_import
31 30
32 31 #if py3exe
33 32 $ hg files 'set:(**.py)' | sed 's|\\|/|g' | xargs $PYTHON3 contrib/check-py3-compat.py
34 33 doc/hgmanpage.py: invalid syntax: invalid syntax (<unknown>, line *) (glob)
35 34 hgext/automv.py: error importing module: <SyntaxError> invalid syntax (commands.py, line *) (line *) (glob)
36 35 hgext/blackbox.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
37 36 hgext/bugzilla.py: error importing module: <ImportError> No module named 'urlparse' (line *) (glob)
38 37 hgext/censor.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
39 38 hgext/chgserver.py: error importing module: <ImportError> No module named 'SocketServer' (line *) (glob)
40 39 hgext/children.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
41 40 hgext/churn.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
42 41 hgext/clonebundles.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
43 42 hgext/color.py: invalid syntax: invalid syntax (<unknown>, line *) (glob)
44 43 hgext/convert/bzr.py: error importing module: <SystemError> Parent module 'hgext.convert' not loaded, cannot perform relative import (line *) (glob)
45 44 hgext/convert/common.py: error importing module: <ImportError> No module named 'cPickle' (line *) (glob)
46 45 hgext/convert/convcmd.py: error importing: <SyntaxError> invalid syntax (bundle*.py, line *) (error at bundlerepo.py:*) (glob)
47 46 hgext/convert/cvs.py: error importing module: <SystemError> Parent module 'hgext.convert' not loaded, cannot perform relative import (line *) (glob)
48 47 hgext/convert/cvsps.py: error importing module: <ImportError> No module named 'cPickle' (line *) (glob)
49 48 hgext/convert/darcs.py: error importing module: <SystemError> Parent module 'hgext.convert' not loaded, cannot perform relative import (line *) (glob)
50 49 hgext/convert/filemap.py: error importing module: <SystemError> Parent module 'hgext.convert' not loaded, cannot perform relative import (line *) (glob)
51 50 hgext/convert/git.py: error importing module: <SystemError> Parent module 'hgext.convert' not loaded, cannot perform relative import (line *) (glob)
52 51 hgext/convert/gnuarch.py: error importing module: <SystemError> Parent module 'hgext.convert' not loaded, cannot perform relative import (line *) (glob)
53 52 hgext/convert/hg.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
54 53 hgext/convert/monotone.py: error importing module: <SystemError> Parent module 'hgext.convert' not loaded, cannot perform relative import (line *) (glob)
55 54 hgext/convert/p*.py: error importing module: <SystemError> Parent module 'hgext.convert' not loaded, cannot perform relative import (line *) (glob)
56 55 hgext/convert/subversion.py: error importing module: <ImportError> No module named 'cPickle' (line *) (glob)
57 56 hgext/convert/transport.py: error importing module: <ImportError> No module named 'svn.client' (line *) (glob)
58 57 hgext/eol.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
59 58 hgext/extdiff.py: error importing module: <SyntaxError> invalid syntax (archival.py, line *) (line *) (glob)
60 59 hgext/factotum.py: error importing: <ImportError> No module named 'rfc822' (error at __init__.py:*) (glob)
61 60 hgext/fetch.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
62 61 hgext/fsmonitor/watchmanclient.py: error importing module: <SystemError> Parent module 'hgext.fsmonitor' not loaded, cannot perform relative import (line *) (glob)
63 62 hgext/gpg.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
64 63 hgext/graphlog.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
65 64 hgext/hgk.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
66 65 hgext/histedit.py: error importing module: <SyntaxError> invalid syntax (bundle*.py, line *) (line *) (glob)
67 66 hgext/keyword.py: error importing: <ImportError> No module named 'BaseHTTPServer' (error at common.py:*) (glob)
68 67 hgext/largefiles/basestore.py: error importing: <SyntaxError> invalid syntax (bundle*.py, line *) (error at bundlerepo.py:*) (glob)
69 68 hgext/largefiles/lfcommands.py: error importing: <SyntaxError> invalid syntax (bundle*.py, line *) (error at bundlerepo.py:*) (glob)
70 69 hgext/largefiles/lfutil.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
71 70 hgext/largefiles/localstore.py: error importing module: <ImportError> No module named 'lfutil' (line *) (glob)
72 71 hgext/largefiles/overrides.py: error importing: <SyntaxError> invalid syntax (bundle*.py, line *) (error at bundlerepo.py:*) (glob)
73 72 hgext/largefiles/proto.py: error importing: <ImportError> No module named 'httplib' (error at httppeer.py:*) (glob)
74 73 hgext/largefiles/remotestore.py: error importing: <SyntaxError> invalid syntax (bundle*.py, line *) (error at wireproto.py:*) (glob)
75 74 hgext/largefiles/reposetup.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
76 75 hgext/largefiles/uisetup.py: error importing module: <SyntaxError> invalid syntax (archival.py, line *) (line *) (glob)
77 76 hgext/largefiles/wirestore.py: error importing module: <ImportError> No module named 'lfutil' (line *) (glob)
78 77 hgext/mq.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
79 78 hgext/notify.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
80 79 hgext/pager.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
81 80 hgext/patchbomb.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
82 81 hgext/purge.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
83 82 hgext/rebase.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
84 83 hgext/record.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
85 84 hgext/relink.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
86 85 hgext/schemes.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
87 86 hgext/share.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
88 87 hgext/shelve.py: error importing module: <SyntaxError> invalid syntax (bundle*.py, line *) (line *) (glob)
89 88 hgext/strip.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
90 89 hgext/transplant.py: error importing: <SyntaxError> invalid syntax (bundle*.py, line *) (error at bundlerepo.py:*) (glob)
91 90 mercurial/archival.py: invalid syntax: invalid syntax (<unknown>, line *) (glob)
92 91 mercurial/branchmap.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
93 92 mercurial/bundle*.py: invalid syntax: invalid syntax (<unknown>, line *) (glob)
94 93 mercurial/bundlerepo.py: error importing module: <SyntaxError> invalid syntax (bundle*.py, line *) (line *) (glob)
95 94 mercurial/changegroup.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
96 95 mercurial/changelog.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
97 96 mercurial/cmdutil.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
98 97 mercurial/commands.py: invalid syntax: invalid syntax (<unknown>, line *) (glob)
99 98 mercurial/commandserver.py: error importing module: <ImportError> No module named 'SocketServer' (line *) (glob)
100 99 mercurial/context.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
101 100 mercurial/copies.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
102 101 mercurial/crecord.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
103 102 mercurial/dirstate.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
104 103 mercurial/discovery.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
105 104 mercurial/dispatch.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
106 105 mercurial/exchange.py: error importing module: <SyntaxError> invalid syntax (bundle*.py, line *) (line *) (glob)
107 106 mercurial/extensions.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
108 107 mercurial/filelog.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
109 108 mercurial/filemerge.py: error importing: <ImportError> No module named 'cPickle' (error at formatter.py:*) (glob)
110 109 mercurial/fileset.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
111 110 mercurial/formatter.py: error importing module: <ImportError> No module named 'cPickle' (line *) (glob)
112 111 mercurial/graphmod.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
113 112 mercurial/help.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
114 113 mercurial/hg.py: error importing: <SyntaxError> invalid syntax (bundle*.py, line *) (error at bundlerepo.py:*) (glob)
115 114 mercurial/hgweb/common.py: error importing module: <ImportError> No module named 'BaseHTTPServer' (line *) (glob)
116 115 mercurial/hgweb/hgweb_mod.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
117 116 mercurial/hgweb/hgwebdir_mod.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
118 117 mercurial/hgweb/protocol.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
119 118 mercurial/hgweb/request.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
120 119 mercurial/hgweb/server.py: error importing module: <ImportError> No module named 'BaseHTTPServer' (line *) (glob)
121 120 mercurial/hgweb/webcommands.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
122 121 mercurial/hgweb/webutil.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
123 122 mercurial/hgweb/wsgicgi.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
124 123 mercurial/hook.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
125 124 mercurial/httpconnection.py: error importing: <ImportError> No module named 'rfc822' (error at __init__.py:*) (glob)
126 125 mercurial/httppeer.py: error importing module: <ImportError> No module named 'httplib' (line *) (glob)
127 126 mercurial/keepalive.py: error importing module: <ImportError> No module named 'httplib' (line *) (glob)
128 127 mercurial/localrepo.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
129 128 mercurial/mail.py: error importing module: <AttributeError> module 'email' has no attribute 'Header' (line *) (glob)
130 129 mercurial/manifest.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
131 130 mercurial/merge.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
132 131 mercurial/namespaces.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
133 132 mercurial/patch.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
134 133 mercurial/pure/mpatch.py: error importing module: <ImportError> cannot import name 'pycompat' (line *) (glob)
135 134 mercurial/pure/parsers.py: error importing module: <ImportError> No module named 'mercurial.pure.node' (line *) (glob)
136 135 mercurial/repair.py: error importing module: <SyntaxError> invalid syntax (bundle*.py, line *) (line *) (glob)
137 136 mercurial/revlog.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
138 137 mercurial/revset.py: error importing module: <AttributeError> 'dict' object has no attribute 'iteritems' (line *) (glob)
139 138 mercurial/scmutil.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
140 139 mercurial/scmwindows.py: error importing module: <ImportError> No module named '_winreg' (line *) (glob)
141 140 mercurial/simplemerge.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
142 141 mercurial/sshpeer.py: error importing: <SyntaxError> invalid syntax (bundle*.py, line *) (error at wireproto.py:*) (glob)
143 142 mercurial/sshserver.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
144 143 mercurial/statichttprepo.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
145 144 mercurial/store.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
146 145 mercurial/streamclone.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
147 146 mercurial/subrepo.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
148 147 mercurial/templatefilters.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
149 148 mercurial/templatekw.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
150 149 mercurial/templater.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
151 150 mercurial/ui.py: error importing: <ImportError> No module named 'cPickle' (error at formatter.py:*) (glob)
152 151 mercurial/unionrepo.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
153 152 mercurial/url.py: error importing module: <ImportError> No module named 'httplib' (line *) (glob)
154 153 mercurial/verify.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
155 154 mercurial/win*.py: error importing module: <ImportError> No module named 'msvcrt' (line *) (glob)
156 155 mercurial/windows.py: error importing module: <ImportError> No module named '_winreg' (line *) (glob)
157 156 mercurial/wireproto.py: error importing module: <SyntaxError> invalid syntax (bundle*.py, line *) (line *) (glob)
158 157
159 158 #endif
General Comments 0
You need to be logged in to leave comments. Login now