##// END OF EJS Templates
largefiles: use separate try/except and try/finally as needed for python2.4...
Thomas Arendsen Hein -
r15279:01860816 stable
parent child Browse files
Show More
@@ -1,830 +1,832
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 '''Overridden Mercurial commands and functions for the largefiles extension'''
10 10
11 11 import os
12 12 import copy
13 13
14 14 from mercurial import hg, commands, util, cmdutil, match as match_, node, \
15 15 archival, error, merge
16 16 from mercurial.i18n import _
17 17 from mercurial.node import hex
18 18 from hgext import rebase
19 19 import lfutil
20 20
21 21 try:
22 22 from mercurial import scmutil
23 23 except ImportError:
24 24 pass
25 25
26 26 import lfutil
27 27 import lfcommands
28 28
29 29 def installnormalfilesmatchfn(manifest):
30 30 '''overrides scmutil.match so that the matcher it returns will ignore all
31 31 largefiles'''
32 32 oldmatch = None # for the closure
33 33 def override_match(repo, pats=[], opts={}, globbed=False,
34 34 default='relpath'):
35 35 match = oldmatch(repo, pats, opts, globbed, default)
36 36 m = copy.copy(match)
37 37 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
38 38 manifest)
39 39 m._files = filter(notlfile, m._files)
40 40 m._fmap = set(m._files)
41 41 orig_matchfn = m.matchfn
42 42 m.matchfn = lambda f: notlfile(f) and orig_matchfn(f) or None
43 43 return m
44 44 oldmatch = installmatchfn(override_match)
45 45
46 46 def installmatchfn(f):
47 47 oldmatch = scmutil.match
48 48 setattr(f, 'oldmatch', oldmatch)
49 49 scmutil.match = f
50 50 return oldmatch
51 51
52 52 def restorematchfn():
53 53 '''restores scmutil.match to what it was before installnormalfilesmatchfn
54 54 was called. no-op if scmutil.match is its original function.
55 55
56 56 Note that n calls to installnormalfilesmatchfn will require n calls to
57 57 restore matchfn to reverse'''
58 58 scmutil.match = getattr(scmutil.match, 'oldmatch', scmutil.match)
59 59
60 60 # -- Wrappers: modify existing commands --------------------------------
61 61
62 62 # Add works by going through the files that the user wanted to add and
63 63 # checking if they should be added as largefiles. Then it makes a new
64 64 # matcher which matches only the normal files and runs the original
65 65 # version of add.
66 66 def override_add(orig, ui, repo, *pats, **opts):
67 67 large = opts.pop('large', None)
68 68 lfsize = lfutil.getminsize(
69 69 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
70 70
71 71 lfmatcher = None
72 72 if os.path.exists(repo.wjoin(lfutil.shortname)):
73 73 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
74 74 if lfpats:
75 75 lfmatcher = match_.match(repo.root, '', list(lfpats))
76 76
77 77 lfnames = []
78 78 m = scmutil.match(repo[None], pats, opts)
79 79 m.bad = lambda x, y: None
80 80 wctx = repo[None]
81 81 for f in repo.walk(m):
82 82 exact = m.exact(f)
83 83 lfile = lfutil.standin(f) in wctx
84 84 nfile = f in wctx
85 85 exists = lfile or nfile
86 86
87 87 # Don't warn the user when they attempt to add a normal tracked file.
88 88 # The normal add code will do that for us.
89 89 if exact and exists:
90 90 if lfile:
91 91 ui.warn(_('%s already a largefile\n') % f)
92 92 continue
93 93
94 94 if exact or not exists:
95 95 abovemin = (lfsize and
96 96 os.path.getsize(repo.wjoin(f)) >= lfsize * 1024 * 1024)
97 97 if large or abovemin or (lfmatcher and lfmatcher(f)):
98 98 lfnames.append(f)
99 99 if ui.verbose or not exact:
100 100 ui.status(_('adding %s as a largefile\n') % m.rel(f))
101 101
102 102 bad = []
103 103 standins = []
104 104
105 105 # Need to lock, otherwise there could be a race condition between
106 106 # when standins are created and added to the repo.
107 107 wlock = repo.wlock()
108 108 try:
109 109 if not opts.get('dry_run'):
110 110 lfdirstate = lfutil.openlfdirstate(ui, repo)
111 111 for f in lfnames:
112 112 standinname = lfutil.standin(f)
113 113 lfutil.writestandin(repo, standinname, hash='',
114 114 executable=lfutil.getexecutable(repo.wjoin(f)))
115 115 standins.append(standinname)
116 116 if lfdirstate[f] == 'r':
117 117 lfdirstate.normallookup(f)
118 118 else:
119 119 lfdirstate.add(f)
120 120 lfdirstate.write()
121 121 bad += [lfutil.splitstandin(f)
122 122 for f in lfutil.repo_add(repo, standins)
123 123 if f in m.files()]
124 124 finally:
125 125 wlock.release()
126 126
127 127 installnormalfilesmatchfn(repo[None].manifest())
128 128 result = orig(ui, repo, *pats, **opts)
129 129 restorematchfn()
130 130
131 131 return (result == 1 or bad) and 1 or 0
132 132
133 133 def override_remove(orig, ui, repo, *pats, **opts):
134 134 manifest = repo[None].manifest()
135 135 installnormalfilesmatchfn(manifest)
136 136 orig(ui, repo, *pats, **opts)
137 137 restorematchfn()
138 138
139 139 after, force = opts.get('after'), opts.get('force')
140 140 if not pats and not after:
141 141 raise util.Abort(_('no files specified'))
142 142 m = scmutil.match(repo[None], pats, opts)
143 143 try:
144 144 repo.lfstatus = True
145 145 s = repo.status(match=m, clean=True)
146 146 finally:
147 147 repo.lfstatus = False
148 148 modified, added, deleted, clean = [[f for f in list
149 149 if lfutil.standin(f) in manifest]
150 150 for list in [s[0], s[1], s[3], s[6]]]
151 151
152 152 def warn(files, reason):
153 153 for f in files:
154 154 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
155 155 % (m.rel(f), reason))
156 156
157 157 if force:
158 158 remove, forget = modified + deleted + clean, added
159 159 elif after:
160 160 remove, forget = deleted, []
161 161 warn(modified + added + clean, _('still exists'))
162 162 else:
163 163 remove, forget = deleted + clean, []
164 164 warn(modified, _('is modified'))
165 165 warn(added, _('has been marked for add'))
166 166
167 167 for f in sorted(remove + forget):
168 168 if ui.verbose or not m.exact(f):
169 169 ui.status(_('removing %s\n') % m.rel(f))
170 170
171 171 # Need to lock because standin files are deleted then removed from the
172 172 # repository and we could race inbetween.
173 173 wlock = repo.wlock()
174 174 try:
175 175 lfdirstate = lfutil.openlfdirstate(ui, repo)
176 176 for f in remove:
177 177 if not after:
178 178 os.unlink(repo.wjoin(f))
179 179 currentdir = os.path.split(f)[0]
180 180 while currentdir and not os.listdir(repo.wjoin(currentdir)):
181 181 os.rmdir(repo.wjoin(currentdir))
182 182 currentdir = os.path.split(currentdir)[0]
183 183 lfdirstate.remove(f)
184 184 lfdirstate.write()
185 185
186 186 forget = [lfutil.standin(f) for f in forget]
187 187 remove = [lfutil.standin(f) for f in remove]
188 188 lfutil.repo_forget(repo, forget)
189 189 lfutil.repo_remove(repo, remove, unlink=True)
190 190 finally:
191 191 wlock.release()
192 192
193 193 def override_status(orig, ui, repo, *pats, **opts):
194 194 try:
195 195 repo.lfstatus = True
196 196 return orig(ui, repo, *pats, **opts)
197 197 finally:
198 198 repo.lfstatus = False
199 199
200 200 def override_log(orig, ui, repo, *pats, **opts):
201 201 try:
202 202 repo.lfstatus = True
203 203 orig(ui, repo, *pats, **opts)
204 204 finally:
205 205 repo.lfstatus = False
206 206
207 207 def override_verify(orig, ui, repo, *pats, **opts):
208 208 large = opts.pop('large', False)
209 209 all = opts.pop('lfa', False)
210 210 contents = opts.pop('lfc', False)
211 211
212 212 result = orig(ui, repo, *pats, **opts)
213 213 if large:
214 214 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
215 215 return result
216 216
217 217 # Override needs to refresh standins so that update's normal merge
218 218 # will go through properly. Then the other update hook (overriding repo.update)
219 219 # will get the new files. Filemerge is also overriden so that the merge
220 220 # will merge standins correctly.
221 221 def override_update(orig, ui, repo, *pats, **opts):
222 222 lfdirstate = lfutil.openlfdirstate(ui, repo)
223 223 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
224 224 False, False)
225 225 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
226 226
227 227 # Need to lock between the standins getting updated and their
228 228 # largefiles getting updated
229 229 wlock = repo.wlock()
230 230 try:
231 231 if opts['check']:
232 232 mod = len(modified) > 0
233 233 for lfile in unsure:
234 234 standin = lfutil.standin(lfile)
235 235 if repo['.'][standin].data().strip() != \
236 236 lfutil.hashfile(repo.wjoin(lfile)):
237 237 mod = True
238 238 else:
239 239 lfdirstate.normal(lfile)
240 240 lfdirstate.write()
241 241 if mod:
242 242 raise util.Abort(_('uncommitted local changes'))
243 243 # XXX handle removed differently
244 244 if not opts['clean']:
245 245 for lfile in unsure + modified + added:
246 246 lfutil.updatestandin(repo, lfutil.standin(lfile))
247 247 finally:
248 248 wlock.release()
249 249 return orig(ui, repo, *pats, **opts)
250 250
251 251 # Override filemerge to prompt the user about how they wish to merge
252 252 # largefiles. This will handle identical edits, and copy/rename +
253 253 # edit without prompting the user.
254 254 def override_filemerge(origfn, repo, mynode, orig, fcd, fco, fca):
255 255 # Use better variable names here. Because this is a wrapper we cannot
256 256 # change the variable names in the function declaration.
257 257 fcdest, fcother, fcancestor = fcd, fco, fca
258 258 if not lfutil.isstandin(orig):
259 259 return origfn(repo, mynode, orig, fcdest, fcother, fcancestor)
260 260 else:
261 261 if not fcother.cmp(fcdest): # files identical?
262 262 return None
263 263
264 264 # backwards, use working dir parent as ancestor
265 265 if fcancestor == fcother:
266 266 fcancestor = fcdest.parents()[0]
267 267
268 268 if orig != fcother.path():
269 269 repo.ui.status(_('merging %s and %s to %s\n')
270 270 % (lfutil.splitstandin(orig),
271 271 lfutil.splitstandin(fcother.path()),
272 272 lfutil.splitstandin(fcdest.path())))
273 273 else:
274 274 repo.ui.status(_('merging %s\n')
275 275 % lfutil.splitstandin(fcdest.path()))
276 276
277 277 if fcancestor.path() != fcother.path() and fcother.data() == \
278 278 fcancestor.data():
279 279 return 0
280 280 if fcancestor.path() != fcdest.path() and fcdest.data() == \
281 281 fcancestor.data():
282 282 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
283 283 return 0
284 284
285 285 if repo.ui.promptchoice(_('largefile %s has a merge conflict\n'
286 286 'keep (l)ocal or take (o)ther?') %
287 287 lfutil.splitstandin(orig),
288 288 (_('&Local'), _('&Other')), 0) == 0:
289 289 return 0
290 290 else:
291 291 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
292 292 return 0
293 293
294 294 # Copy first changes the matchers to match standins instead of
295 295 # largefiles. Then it overrides util.copyfile in that function it
296 296 # checks if the destination largefile already exists. It also keeps a
297 297 # list of copied files so that the largefiles can be copied and the
298 298 # dirstate updated.
299 299 def override_copy(orig, ui, repo, pats, opts, rename=False):
300 300 # doesn't remove largefile on rename
301 301 if len(pats) < 2:
302 302 # this isn't legal, let the original function deal with it
303 303 return orig(ui, repo, pats, opts, rename)
304 304
305 305 def makestandin(relpath):
306 306 path = scmutil.canonpath(repo.root, repo.getcwd(), relpath)
307 307 return os.path.join(os.path.relpath('.', repo.getcwd()),
308 308 lfutil.standin(path))
309 309
310 310 fullpats = scmutil.expandpats(pats)
311 311 dest = fullpats[-1]
312 312
313 313 if os.path.isdir(dest):
314 314 if not os.path.isdir(makestandin(dest)):
315 315 os.makedirs(makestandin(dest))
316 316 # This could copy both lfiles and normal files in one command,
317 317 # but we don't want to do that. First replace their matcher to
318 318 # only match normal files and run it, then replace it to just
319 319 # match largefiles and run it again.
320 320 nonormalfiles = False
321 321 nolfiles = False
322 322 try:
323 installnormalfilesmatchfn(repo[None].manifest())
324 result = orig(ui, repo, pats, opts, rename)
325 except util.Abort, e:
326 if str(e) != 'no files to copy':
327 raise e
328 else:
329 nonormalfiles = True
330 result = 0
323 try:
324 installnormalfilesmatchfn(repo[None].manifest())
325 result = orig(ui, repo, pats, opts, rename)
326 except util.Abort, e:
327 if str(e) != 'no files to copy':
328 raise e
329 else:
330 nonormalfiles = True
331 result = 0
331 332 finally:
332 333 restorematchfn()
333 334
334 335 # The first rename can cause our current working directory to be removed.
335 336 # In that case there is nothing left to copy/rename so just quit.
336 337 try:
337 338 repo.getcwd()
338 339 except OSError:
339 340 return result
340 341
341 342 try:
342 # When we call orig below it creates the standins but we don't add them
343 # to the dir state until later so lock during that time.
344 wlock = repo.wlock()
343 try:
344 # When we call orig below it creates the standins but we don't add them
345 # to the dir state until later so lock during that time.
346 wlock = repo.wlock()
345 347
346 manifest = repo[None].manifest()
347 oldmatch = None # for the closure
348 def override_match(repo, pats=[], opts={}, globbed=False,
349 default='relpath'):
350 newpats = []
351 # The patterns were previously mangled to add the standin
352 # directory; we need to remove that now
348 manifest = repo[None].manifest()
349 oldmatch = None # for the closure
350 def override_match(repo, pats=[], opts={}, globbed=False,
351 default='relpath'):
352 newpats = []
353 # The patterns were previously mangled to add the standin
354 # directory; we need to remove that now
355 for pat in pats:
356 if match_.patkind(pat) is None and lfutil.shortname in pat:
357 newpats.append(pat.replace(lfutil.shortname, ''))
358 else:
359 newpats.append(pat)
360 match = oldmatch(repo, newpats, opts, globbed, default)
361 m = copy.copy(match)
362 lfile = lambda f: lfutil.standin(f) in manifest
363 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
364 m._fmap = set(m._files)
365 orig_matchfn = m.matchfn
366 m.matchfn = lambda f: (lfutil.isstandin(f) and
367 lfile(lfutil.splitstandin(f)) and
368 orig_matchfn(lfutil.splitstandin(f)) or
369 None)
370 return m
371 oldmatch = installmatchfn(override_match)
372 listpats = []
353 373 for pat in pats:
354 if match_.patkind(pat) is None and lfutil.shortname in pat:
355 newpats.append(pat.replace(lfutil.shortname, ''))
374 if match_.patkind(pat) is not None:
375 listpats.append(pat)
356 376 else:
357 newpats.append(pat)
358 match = oldmatch(repo, newpats, opts, globbed, default)
359 m = copy.copy(match)
360 lfile = lambda f: lfutil.standin(f) in manifest
361 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
362 m._fmap = set(m._files)
363 orig_matchfn = m.matchfn
364 m.matchfn = lambda f: (lfutil.isstandin(f) and
365 lfile(lfutil.splitstandin(f)) and
366 orig_matchfn(lfutil.splitstandin(f)) or
367 None)
368 return m
369 oldmatch = installmatchfn(override_match)
370 listpats = []
371 for pat in pats:
372 if match_.patkind(pat) is not None:
373 listpats.append(pat)
374 else:
375 listpats.append(makestandin(pat))
377 listpats.append(makestandin(pat))
376 378
377 try:
378 origcopyfile = util.copyfile
379 copiedfiles = []
380 def override_copyfile(src, dest):
381 if lfutil.shortname in src and lfutil.shortname in dest:
382 destlfile = dest.replace(lfutil.shortname, '')
383 if not opts['force'] and os.path.exists(destlfile):
384 raise IOError('',
385 _('destination largefile already exists'))
386 copiedfiles.append((src, dest))
387 origcopyfile(src, dest)
379 try:
380 origcopyfile = util.copyfile
381 copiedfiles = []
382 def override_copyfile(src, dest):
383 if lfutil.shortname in src and lfutil.shortname in dest:
384 destlfile = dest.replace(lfutil.shortname, '')
385 if not opts['force'] and os.path.exists(destlfile):
386 raise IOError('',
387 _('destination largefile already exists'))
388 copiedfiles.append((src, dest))
389 origcopyfile(src, dest)
388 390
389 util.copyfile = override_copyfile
390 result += orig(ui, repo, listpats, opts, rename)
391 finally:
392 util.copyfile = origcopyfile
391 util.copyfile = override_copyfile
392 result += orig(ui, repo, listpats, opts, rename)
393 finally:
394 util.copyfile = origcopyfile
393 395
394 lfdirstate = lfutil.openlfdirstate(ui, repo)
395 for (src, dest) in copiedfiles:
396 if lfutil.shortname in src and lfutil.shortname in dest:
397 srclfile = src.replace(lfutil.shortname, '')
398 destlfile = dest.replace(lfutil.shortname, '')
399 destlfiledir = os.path.dirname(destlfile) or '.'
400 if not os.path.isdir(destlfiledir):
401 os.makedirs(destlfiledir)
402 if rename:
403 os.rename(srclfile, destlfile)
404 lfdirstate.remove(os.path.relpath(srclfile,
396 lfdirstate = lfutil.openlfdirstate(ui, repo)
397 for (src, dest) in copiedfiles:
398 if lfutil.shortname in src and lfutil.shortname in dest:
399 srclfile = src.replace(lfutil.shortname, '')
400 destlfile = dest.replace(lfutil.shortname, '')
401 destlfiledir = os.path.dirname(destlfile) or '.'
402 if not os.path.isdir(destlfiledir):
403 os.makedirs(destlfiledir)
404 if rename:
405 os.rename(srclfile, destlfile)
406 lfdirstate.remove(os.path.relpath(srclfile,
407 repo.root))
408 else:
409 util.copyfile(srclfile, destlfile)
410 lfdirstate.add(os.path.relpath(destlfile,
405 411 repo.root))
406 else:
407 util.copyfile(srclfile, destlfile)
408 lfdirstate.add(os.path.relpath(destlfile,
409 repo.root))
410 lfdirstate.write()
411 except util.Abort, e:
412 if str(e) != 'no files to copy':
413 raise e
414 else:
415 nolfiles = True
412 lfdirstate.write()
413 except util.Abort, e:
414 if str(e) != 'no files to copy':
415 raise e
416 else:
417 nolfiles = True
416 418 finally:
417 419 restorematchfn()
418 420 wlock.release()
419 421
420 422 if nolfiles and nonormalfiles:
421 423 raise util.Abort(_('no files to copy'))
422 424
423 425 return result
424 426
425 427 # When the user calls revert, we have to be careful to not revert any
426 428 # changes to other largefiles accidentally. This means we have to keep
427 429 # track of the largefiles that are being reverted so we only pull down
428 430 # the necessary largefiles.
429 431 #
430 432 # Standins are only updated (to match the hash of largefiles) before
431 433 # commits. Update the standins then run the original revert, changing
432 434 # the matcher to hit standins instead of largefiles. Based on the
433 435 # resulting standins update the largefiles. Then return the standins
434 436 # to their proper state
435 437 def override_revert(orig, ui, repo, *pats, **opts):
436 438 # Because we put the standins in a bad state (by updating them)
437 439 # and then return them to a correct state we need to lock to
438 440 # prevent others from changing them in their incorrect state.
439 441 wlock = repo.wlock()
440 442 try:
441 443 lfdirstate = lfutil.openlfdirstate(ui, repo)
442 444 (modified, added, removed, missing, unknown, ignored, clean) = \
443 445 lfutil.lfdirstate_status(lfdirstate, repo, repo['.'].rev())
444 446 for lfile in modified:
445 447 lfutil.updatestandin(repo, lfutil.standin(lfile))
446 448
447 449 try:
448 450 ctx = repo[opts.get('rev')]
449 451 oldmatch = None # for the closure
450 452 def override_match(ctxorrepo, pats=[], opts={}, globbed=False,
451 453 default='relpath'):
452 454 if util.safehasattr(ctxorrepo, 'match'):
453 455 ctx0 = ctxorrepo
454 456 else:
455 457 ctx0 = ctxorrepo[None]
456 458 match = oldmatch(ctxorrepo, pats, opts, globbed, default)
457 459 m = copy.copy(match)
458 460 def tostandin(f):
459 461 if lfutil.standin(f) in ctx0 or lfutil.standin(f) in ctx:
460 462 return lfutil.standin(f)
461 463 elif lfutil.standin(f) in repo[None]:
462 464 return None
463 465 return f
464 466 m._files = [tostandin(f) for f in m._files]
465 467 m._files = [f for f in m._files if f is not None]
466 468 m._fmap = set(m._files)
467 469 orig_matchfn = m.matchfn
468 470 def matchfn(f):
469 471 if lfutil.isstandin(f):
470 472 # We need to keep track of what largefiles are being
471 473 # matched so we know which ones to update later --
472 474 # otherwise we accidentally revert changes to other
473 475 # largefiles. This is repo-specific, so duckpunch the
474 476 # repo object to keep the list of largefiles for us
475 477 # later.
476 478 if orig_matchfn(lfutil.splitstandin(f)) and \
477 479 (f in repo[None] or f in ctx):
478 480 lfileslist = getattr(repo, '_lfilestoupdate', [])
479 481 lfileslist.append(lfutil.splitstandin(f))
480 482 repo._lfilestoupdate = lfileslist
481 483 return True
482 484 else:
483 485 return False
484 486 return orig_matchfn(f)
485 487 m.matchfn = matchfn
486 488 return m
487 489 oldmatch = installmatchfn(override_match)
488 490 scmutil.match
489 491 matches = override_match(repo[None], pats, opts)
490 492 orig(ui, repo, *pats, **opts)
491 493 finally:
492 494 restorematchfn()
493 495 lfileslist = getattr(repo, '_lfilestoupdate', [])
494 496 lfcommands.updatelfiles(ui, repo, filelist=lfileslist,
495 497 printmessage=False)
496 498
497 499 # empty out the largefiles list so we start fresh next time
498 500 repo._lfilestoupdate = []
499 501 for lfile in modified:
500 502 if lfile in lfileslist:
501 503 if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\
502 504 in repo['.']:
503 505 lfutil.writestandin(repo, lfutil.standin(lfile),
504 506 repo['.'][lfile].data().strip(),
505 507 'x' in repo['.'][lfile].flags())
506 508 lfdirstate = lfutil.openlfdirstate(ui, repo)
507 509 for lfile in added:
508 510 standin = lfutil.standin(lfile)
509 511 if standin not in ctx and (standin in matches or opts.get('all')):
510 512 if lfile in lfdirstate:
511 513 lfdirstate.drop(lfile)
512 514 util.unlinkpath(repo.wjoin(standin))
513 515 lfdirstate.write()
514 516 finally:
515 517 wlock.release()
516 518
517 519 def hg_update(orig, repo, node):
518 520 result = orig(repo, node)
519 521 # XXX check if it worked first
520 522 lfcommands.updatelfiles(repo.ui, repo)
521 523 return result
522 524
523 525 def hg_clean(orig, repo, node, show_stats=True):
524 526 result = orig(repo, node, show_stats)
525 527 lfcommands.updatelfiles(repo.ui, repo)
526 528 return result
527 529
528 530 def hg_merge(orig, repo, node, force=None, remind=True):
529 531 result = orig(repo, node, force, remind)
530 532 lfcommands.updatelfiles(repo.ui, repo)
531 533 return result
532 534
533 535 # When we rebase a repository with remotely changed largefiles, we need to
534 536 # take some extra care so that the largefiles are correctly updated in the
535 537 # working copy
536 538 def override_pull(orig, ui, repo, source=None, **opts):
537 539 if opts.get('rebase', False):
538 540 repo._isrebasing = True
539 541 try:
540 542 if opts.get('update'):
541 543 del opts['update']
542 544 ui.debug('--update and --rebase are not compatible, ignoring '
543 545 'the update flag\n')
544 546 del opts['rebase']
545 547 cmdutil.bailifchanged(repo)
546 548 revsprepull = len(repo)
547 549 origpostincoming = commands.postincoming
548 550 def _dummy(*args, **kwargs):
549 551 pass
550 552 commands.postincoming = _dummy
551 553 repo.lfpullsource = source
552 554 if not source:
553 555 source = 'default'
554 556 try:
555 557 result = commands.pull(ui, repo, source, **opts)
556 558 finally:
557 559 commands.postincoming = origpostincoming
558 560 revspostpull = len(repo)
559 561 if revspostpull > revsprepull:
560 562 result = result or rebase.rebase(ui, repo)
561 563 finally:
562 564 repo._isrebasing = False
563 565 else:
564 566 repo.lfpullsource = source
565 567 if not source:
566 568 source = 'default'
567 569 result = orig(ui, repo, source, **opts)
568 570 return result
569 571
570 572 def override_rebase(orig, ui, repo, **opts):
571 573 repo._isrebasing = True
572 574 try:
573 575 orig(ui, repo, **opts)
574 576 finally:
575 577 repo._isrebasing = False
576 578
577 579 def override_archive(orig, repo, dest, node, kind, decode=True, matchfn=None,
578 580 prefix=None, mtime=None, subrepos=None):
579 581 # No need to lock because we are only reading history and
580 582 # largefile caches, neither of which are modified.
581 583 lfcommands.cachelfiles(repo.ui, repo, node)
582 584
583 585 if kind not in archival.archivers:
584 586 raise util.Abort(_("unknown archive type '%s'") % kind)
585 587
586 588 ctx = repo[node]
587 589
588 590 if kind == 'files':
589 591 if prefix:
590 592 raise util.Abort(
591 593 _('cannot give prefix when archiving to files'))
592 594 else:
593 595 prefix = archival.tidyprefix(dest, kind, prefix)
594 596
595 597 def write(name, mode, islink, getdata):
596 598 if matchfn and not matchfn(name):
597 599 return
598 600 data = getdata()
599 601 if decode:
600 602 data = repo.wwritedata(name, data)
601 603 archiver.addfile(prefix + name, mode, islink, data)
602 604
603 605 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
604 606
605 607 if repo.ui.configbool("ui", "archivemeta", True):
606 608 def metadata():
607 609 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
608 610 hex(repo.changelog.node(0)), hex(node), ctx.branch())
609 611
610 612 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
611 613 if repo.tagtype(t) == 'global')
612 614 if not tags:
613 615 repo.ui.pushbuffer()
614 616 opts = {'template': '{latesttag}\n{latesttagdistance}',
615 617 'style': '', 'patch': None, 'git': None}
616 618 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
617 619 ltags, dist = repo.ui.popbuffer().split('\n')
618 620 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
619 621 tags += 'latesttagdistance: %s\n' % dist
620 622
621 623 return base + tags
622 624
623 625 write('.hg_archival.txt', 0644, False, metadata)
624 626
625 627 for f in ctx:
626 628 ff = ctx.flags(f)
627 629 getdata = ctx[f].data
628 630 if lfutil.isstandin(f):
629 631 path = lfutil.findfile(repo, getdata().strip())
630 632 f = lfutil.splitstandin(f)
631 633
632 634 def getdatafn():
633 635 try:
634 636 fd = open(path, 'rb')
635 637 return fd.read()
636 638 finally:
637 639 fd.close()
638 640
639 641 getdata = getdatafn
640 642 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
641 643
642 644 if subrepos:
643 645 for subpath in ctx.substate:
644 646 sub = ctx.sub(subpath)
645 647 try:
646 648 sub.archive(repo.ui, archiver, prefix)
647 649 except TypeError:
648 650 sub.archive(archiver, prefix)
649 651
650 652 archiver.done()
651 653
652 654 # If a largefile is modified, the change is not reflected in its
653 655 # standin until a commit. cmdutil.bailifchanged() raises an exception
654 656 # if the repo has uncommitted changes. Wrap it to also check if
655 657 # largefiles were changed. This is used by bisect and backout.
656 658 def override_bailifchanged(orig, repo):
657 659 orig(repo)
658 660 repo.lfstatus = True
659 661 modified, added, removed, deleted = repo.status()[:4]
660 662 repo.lfstatus = False
661 663 if modified or added or removed or deleted:
662 664 raise util.Abort(_('outstanding uncommitted changes'))
663 665
664 666 # Fetch doesn't use cmdutil.bail_if_changed so override it to add the check
665 667 def override_fetch(orig, ui, repo, *pats, **opts):
666 668 repo.lfstatus = True
667 669 modified, added, removed, deleted = repo.status()[:4]
668 670 repo.lfstatus = False
669 671 if modified or added or removed or deleted:
670 672 raise util.Abort(_('outstanding uncommitted changes'))
671 673 return orig(ui, repo, *pats, **opts)
672 674
673 675 def override_forget(orig, ui, repo, *pats, **opts):
674 676 installnormalfilesmatchfn(repo[None].manifest())
675 677 orig(ui, repo, *pats, **opts)
676 678 restorematchfn()
677 679 m = scmutil.match(repo[None], pats, opts)
678 680
679 681 try:
680 682 repo.lfstatus = True
681 683 s = repo.status(match=m, clean=True)
682 684 finally:
683 685 repo.lfstatus = False
684 686 forget = sorted(s[0] + s[1] + s[3] + s[6])
685 687 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
686 688
687 689 for f in forget:
688 690 if lfutil.standin(f) not in repo.dirstate and not \
689 691 os.path.isdir(m.rel(lfutil.standin(f))):
690 692 ui.warn(_('not removing %s: file is already untracked\n')
691 693 % m.rel(f))
692 694
693 695 for f in forget:
694 696 if ui.verbose or not m.exact(f):
695 697 ui.status(_('removing %s\n') % m.rel(f))
696 698
697 699 # Need to lock because standin files are deleted then removed from the
698 700 # repository and we could race inbetween.
699 701 wlock = repo.wlock()
700 702 try:
701 703 lfdirstate = lfutil.openlfdirstate(ui, repo)
702 704 for f in forget:
703 705 if lfdirstate[f] == 'a':
704 706 lfdirstate.drop(f)
705 707 else:
706 708 lfdirstate.remove(f)
707 709 lfdirstate.write()
708 710 lfutil.repo_remove(repo, [lfutil.standin(f) for f in forget],
709 711 unlink=True)
710 712 finally:
711 713 wlock.release()
712 714
713 715 def getoutgoinglfiles(ui, repo, dest=None, **opts):
714 716 dest = ui.expandpath(dest or 'default-push', dest or 'default')
715 717 dest, branches = hg.parseurl(dest, opts.get('branch'))
716 718 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
717 719 if revs:
718 720 revs = [repo.lookup(rev) for rev in revs]
719 721
720 722 remoteui = hg.remoteui
721 723
722 724 try:
723 725 remote = hg.repository(remoteui(repo, opts), dest)
724 726 except error.RepoError:
725 727 return None
726 728 o = lfutil.findoutgoing(repo, remote, False)
727 729 if not o:
728 730 return None
729 731 o = repo.changelog.nodesbetween(o, revs)[0]
730 732 if opts.get('newest_first'):
731 733 o.reverse()
732 734
733 735 toupload = set()
734 736 for n in o:
735 737 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
736 738 ctx = repo[n]
737 739 files = set(ctx.files())
738 740 if len(parents) == 2:
739 741 mc = ctx.manifest()
740 742 mp1 = ctx.parents()[0].manifest()
741 743 mp2 = ctx.parents()[1].manifest()
742 744 for f in mp1:
743 745 if f not in mc:
744 746 files.add(f)
745 747 for f in mp2:
746 748 if f not in mc:
747 749 files.add(f)
748 750 for f in mc:
749 751 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
750 752 files.add(f)
751 753 toupload = toupload.union(
752 754 set([f for f in files if lfutil.isstandin(f) and f in ctx]))
753 755 return toupload
754 756
755 757 def override_outgoing(orig, ui, repo, dest=None, **opts):
756 758 orig(ui, repo, dest, **opts)
757 759
758 760 if opts.pop('large', None):
759 761 toupload = getoutgoinglfiles(ui, repo, dest, **opts)
760 762 if toupload is None:
761 763 ui.status(_('largefiles: No remote repo\n'))
762 764 else:
763 765 ui.status(_('largefiles to upload:\n'))
764 766 for file in toupload:
765 767 ui.status(lfutil.splitstandin(file) + '\n')
766 768 ui.status('\n')
767 769
768 770 def override_summary(orig, ui, repo, *pats, **opts):
769 771 orig(ui, repo, *pats, **opts)
770 772
771 773 if opts.pop('large', None):
772 774 toupload = getoutgoinglfiles(ui, repo, None, **opts)
773 775 if toupload is None:
774 776 ui.status(_('largefiles: No remote repo\n'))
775 777 else:
776 778 ui.status(_('largefiles: %d to upload\n') % len(toupload))
777 779
778 780 def override_addremove(orig, ui, repo, *pats, **opts):
779 781 # Check if the parent or child has largefiles; if so, disallow
780 782 # addremove. If there is a symlink in the manifest then getting
781 783 # the manifest throws an exception: catch it and let addremove
782 784 # deal with it.
783 785 try:
784 786 manifesttip = set(repo['tip'].manifest())
785 787 except util.Abort:
786 788 manifesttip = set()
787 789 try:
788 790 manifestworking = set(repo[None].manifest())
789 791 except util.Abort:
790 792 manifestworking = set()
791 793
792 794 # Manifests are only iterable so turn them into sets then union
793 795 for file in manifesttip.union(manifestworking):
794 796 if file.startswith(lfutil.shortname):
795 797 raise util.Abort(
796 798 _('addremove cannot be run on a repo with largefiles'))
797 799
798 800 return orig(ui, repo, *pats, **opts)
799 801
800 802 # Calling purge with --all will cause the largefiles to be deleted.
801 803 # Override repo.status to prevent this from happening.
802 804 def override_purge(orig, ui, repo, *dirs, **opts):
803 805 oldstatus = repo.status
804 806 def override_status(node1='.', node2=None, match=None, ignored=False,
805 807 clean=False, unknown=False, listsubrepos=False):
806 808 r = oldstatus(node1, node2, match, ignored, clean, unknown,
807 809 listsubrepos)
808 810 lfdirstate = lfutil.openlfdirstate(ui, repo)
809 811 modified, added, removed, deleted, unknown, ignored, clean = r
810 812 unknown = [f for f in unknown if lfdirstate[f] == '?']
811 813 ignored = [f for f in ignored if lfdirstate[f] == '?']
812 814 return modified, added, removed, deleted, unknown, ignored, clean
813 815 repo.status = override_status
814 816 orig(ui, repo, *dirs, **opts)
815 817 repo.status = oldstatus
816 818
817 819 def override_rollback(orig, ui, repo, **opts):
818 820 result = orig(ui, repo, **opts)
819 821 merge.update(repo, node=None, branchmerge=False, force=True,
820 822 partial=lfutil.isstandin)
821 823 lfdirstate = lfutil.openlfdirstate(ui, repo)
822 824 lfiles = lfutil.listlfiles(repo)
823 825 oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
824 826 for file in lfiles:
825 827 if file in oldlfiles:
826 828 lfdirstate.normallookup(file)
827 829 else:
828 830 lfdirstate.add(file)
829 831 lfdirstate.write()
830 832 return result
General Comments 0
You need to be logged in to leave comments. Login now