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