##// END OF EJS Templates
largefiles: add context manager for setting/clearing "lfstatus" attribute...
Martin von Zweigbergk -
r43588:03dae104 default
parent child Browse files
Show More
@@ -1,1827 +1,1807 b''
1 # Copyright 2009-2010 Gregory P. Ward
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
4 # Copyright 2010-2011 Unity Technologies
5 #
5 #
6 # This software may be used and distributed according to the terms of the
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.
7 # GNU General Public License version 2 or any later version.
8
8
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
10 from __future__ import absolute_import
10 from __future__ import absolute_import
11
11
12 import contextlib
12 import copy
13 import copy
13 import os
14 import os
14
15
15 from mercurial.i18n import _
16 from mercurial.i18n import _
16
17
17 from mercurial.pycompat import open
18 from mercurial.pycompat import open
18
19
19 from mercurial.hgweb import webcommands
20 from mercurial.hgweb import webcommands
20
21
21 from mercurial import (
22 from mercurial import (
22 archival,
23 archival,
23 cmdutil,
24 cmdutil,
24 copies as copiesmod,
25 copies as copiesmod,
25 error,
26 error,
26 exchange,
27 exchange,
27 extensions,
28 extensions,
28 exthelper,
29 exthelper,
29 filemerge,
30 filemerge,
30 hg,
31 hg,
31 logcmdutil,
32 logcmdutil,
32 match as matchmod,
33 match as matchmod,
33 merge,
34 merge,
34 pathutil,
35 pathutil,
35 pycompat,
36 pycompat,
36 scmutil,
37 scmutil,
37 smartset,
38 smartset,
38 subrepo,
39 subrepo,
39 upgrade,
40 upgrade,
40 url as urlmod,
41 url as urlmod,
41 util,
42 util,
42 )
43 )
43
44
44 from . import (
45 from . import (
45 lfcommands,
46 lfcommands,
46 lfutil,
47 lfutil,
47 storefactory,
48 storefactory,
48 )
49 )
49
50
50 eh = exthelper.exthelper()
51 eh = exthelper.exthelper()
51
52
52 # -- Utility functions: commonly/repeatedly needed functionality ---------------
53 # -- Utility functions: commonly/repeatedly needed functionality ---------------
53
54
54
55
55 def composelargefilematcher(match, manifest):
56 def composelargefilematcher(match, manifest):
56 '''create a matcher that matches only the largefiles in the original
57 '''create a matcher that matches only the largefiles in the original
57 matcher'''
58 matcher'''
58 m = copy.copy(match)
59 m = copy.copy(match)
59 lfile = lambda f: lfutil.standin(f) in manifest
60 lfile = lambda f: lfutil.standin(f) in manifest
60 m._files = [lf for lf in m._files if lfile(lf)]
61 m._files = [lf for lf in m._files if lfile(lf)]
61 m._fileset = set(m._files)
62 m._fileset = set(m._files)
62 m.always = lambda: False
63 m.always = lambda: False
63 origmatchfn = m.matchfn
64 origmatchfn = m.matchfn
64 m.matchfn = lambda f: lfile(f) and origmatchfn(f)
65 m.matchfn = lambda f: lfile(f) and origmatchfn(f)
65 return m
66 return m
66
67
67
68
68 def composenormalfilematcher(match, manifest, exclude=None):
69 def composenormalfilematcher(match, manifest, exclude=None):
69 excluded = set()
70 excluded = set()
70 if exclude is not None:
71 if exclude is not None:
71 excluded.update(exclude)
72 excluded.update(exclude)
72
73
73 m = copy.copy(match)
74 m = copy.copy(match)
74 notlfile = lambda f: not (
75 notlfile = lambda f: not (
75 lfutil.isstandin(f) or lfutil.standin(f) in manifest or f in excluded
76 lfutil.isstandin(f) or lfutil.standin(f) in manifest or f in excluded
76 )
77 )
77 m._files = [lf for lf in m._files if notlfile(lf)]
78 m._files = [lf for lf in m._files if notlfile(lf)]
78 m._fileset = set(m._files)
79 m._fileset = set(m._files)
79 m.always = lambda: False
80 m.always = lambda: False
80 origmatchfn = m.matchfn
81 origmatchfn = m.matchfn
81 m.matchfn = lambda f: notlfile(f) and origmatchfn(f)
82 m.matchfn = lambda f: notlfile(f) and origmatchfn(f)
82 return m
83 return m
83
84
84
85
85 def addlargefiles(ui, repo, isaddremove, matcher, uipathfn, **opts):
86 def addlargefiles(ui, repo, isaddremove, matcher, uipathfn, **opts):
86 large = opts.get(r'large')
87 large = opts.get(r'large')
87 lfsize = lfutil.getminsize(
88 lfsize = lfutil.getminsize(
88 ui, lfutil.islfilesrepo(repo), opts.get(r'lfsize')
89 ui, lfutil.islfilesrepo(repo), opts.get(r'lfsize')
89 )
90 )
90
91
91 lfmatcher = None
92 lfmatcher = None
92 if lfutil.islfilesrepo(repo):
93 if lfutil.islfilesrepo(repo):
93 lfpats = ui.configlist(lfutil.longname, b'patterns')
94 lfpats = ui.configlist(lfutil.longname, b'patterns')
94 if lfpats:
95 if lfpats:
95 lfmatcher = matchmod.match(repo.root, b'', list(lfpats))
96 lfmatcher = matchmod.match(repo.root, b'', list(lfpats))
96
97
97 lfnames = []
98 lfnames = []
98 m = matcher
99 m = matcher
99
100
100 wctx = repo[None]
101 wctx = repo[None]
101 for f in wctx.walk(matchmod.badmatch(m, lambda x, y: None)):
102 for f in wctx.walk(matchmod.badmatch(m, lambda x, y: None)):
102 exact = m.exact(f)
103 exact = m.exact(f)
103 lfile = lfutil.standin(f) in wctx
104 lfile = lfutil.standin(f) in wctx
104 nfile = f in wctx
105 nfile = f in wctx
105 exists = lfile or nfile
106 exists = lfile or nfile
106
107
107 # Don't warn the user when they attempt to add a normal tracked file.
108 # Don't warn the user when they attempt to add a normal tracked file.
108 # The normal add code will do that for us.
109 # The normal add code will do that for us.
109 if exact and exists:
110 if exact and exists:
110 if lfile:
111 if lfile:
111 ui.warn(_(b'%s already a largefile\n') % uipathfn(f))
112 ui.warn(_(b'%s already a largefile\n') % uipathfn(f))
112 continue
113 continue
113
114
114 if (exact or not exists) and not lfutil.isstandin(f):
115 if (exact or not exists) and not lfutil.isstandin(f):
115 # In case the file was removed previously, but not committed
116 # In case the file was removed previously, but not committed
116 # (issue3507)
117 # (issue3507)
117 if not repo.wvfs.exists(f):
118 if not repo.wvfs.exists(f):
118 continue
119 continue
119
120
120 abovemin = (
121 abovemin = (
121 lfsize and repo.wvfs.lstat(f).st_size >= lfsize * 1024 * 1024
122 lfsize and repo.wvfs.lstat(f).st_size >= lfsize * 1024 * 1024
122 )
123 )
123 if large or abovemin or (lfmatcher and lfmatcher(f)):
124 if large or abovemin or (lfmatcher and lfmatcher(f)):
124 lfnames.append(f)
125 lfnames.append(f)
125 if ui.verbose or not exact:
126 if ui.verbose or not exact:
126 ui.status(_(b'adding %s as a largefile\n') % uipathfn(f))
127 ui.status(_(b'adding %s as a largefile\n') % uipathfn(f))
127
128
128 bad = []
129 bad = []
129
130
130 # Need to lock, otherwise there could be a race condition between
131 # Need to lock, otherwise there could be a race condition between
131 # when standins are created and added to the repo.
132 # when standins are created and added to the repo.
132 with repo.wlock():
133 with repo.wlock():
133 if not opts.get(r'dry_run'):
134 if not opts.get(r'dry_run'):
134 standins = []
135 standins = []
135 lfdirstate = lfutil.openlfdirstate(ui, repo)
136 lfdirstate = lfutil.openlfdirstate(ui, repo)
136 for f in lfnames:
137 for f in lfnames:
137 standinname = lfutil.standin(f)
138 standinname = lfutil.standin(f)
138 lfutil.writestandin(
139 lfutil.writestandin(
139 repo,
140 repo,
140 standinname,
141 standinname,
141 hash=b'',
142 hash=b'',
142 executable=lfutil.getexecutable(repo.wjoin(f)),
143 executable=lfutil.getexecutable(repo.wjoin(f)),
143 )
144 )
144 standins.append(standinname)
145 standins.append(standinname)
145 if lfdirstate[f] == b'r':
146 if lfdirstate[f] == b'r':
146 lfdirstate.normallookup(f)
147 lfdirstate.normallookup(f)
147 else:
148 else:
148 lfdirstate.add(f)
149 lfdirstate.add(f)
149 lfdirstate.write()
150 lfdirstate.write()
150 bad += [
151 bad += [
151 lfutil.splitstandin(f)
152 lfutil.splitstandin(f)
152 for f in repo[None].add(standins)
153 for f in repo[None].add(standins)
153 if f in m.files()
154 if f in m.files()
154 ]
155 ]
155
156
156 added = [f for f in lfnames if f not in bad]
157 added = [f for f in lfnames if f not in bad]
157 return added, bad
158 return added, bad
158
159
159
160
161 @contextlib.contextmanager
162 def lfstatus(repo):
163 repo.lfstatus = True
164 try:
165 yield
166 finally:
167 repo.lfstatus = False
168
169
160 def removelargefiles(ui, repo, isaddremove, matcher, uipathfn, dryrun, **opts):
170 def removelargefiles(ui, repo, isaddremove, matcher, uipathfn, dryrun, **opts):
161 after = opts.get(r'after')
171 after = opts.get(r'after')
162 m = composelargefilematcher(matcher, repo[None].manifest())
172 m = composelargefilematcher(matcher, repo[None].manifest())
163 try:
173 with lfstatus(repo):
164 repo.lfstatus = True
165 s = repo.status(match=m, clean=not isaddremove)
174 s = repo.status(match=m, clean=not isaddremove)
166 finally:
167 repo.lfstatus = False
168 manifest = repo[None].manifest()
175 manifest = repo[None].manifest()
169 modified, added, deleted, clean = [
176 modified, added, deleted, clean = [
170 [f for f in list if lfutil.standin(f) in manifest]
177 [f for f in list if lfutil.standin(f) in manifest]
171 for list in (s.modified, s.added, s.deleted, s.clean)
178 for list in (s.modified, s.added, s.deleted, s.clean)
172 ]
179 ]
173
180
174 def warn(files, msg):
181 def warn(files, msg):
175 for f in files:
182 for f in files:
176 ui.warn(msg % uipathfn(f))
183 ui.warn(msg % uipathfn(f))
177 return int(len(files) > 0)
184 return int(len(files) > 0)
178
185
179 if after:
186 if after:
180 remove = deleted
187 remove = deleted
181 result = warn(
188 result = warn(
182 modified + added + clean, _(b'not removing %s: file still exists\n')
189 modified + added + clean, _(b'not removing %s: file still exists\n')
183 )
190 )
184 else:
191 else:
185 remove = deleted + clean
192 remove = deleted + clean
186 result = warn(
193 result = warn(
187 modified,
194 modified,
188 _(
195 _(
189 b'not removing %s: file is modified (use -f'
196 b'not removing %s: file is modified (use -f'
190 b' to force removal)\n'
197 b' to force removal)\n'
191 ),
198 ),
192 )
199 )
193 result = (
200 result = (
194 warn(
201 warn(
195 added,
202 added,
196 _(
203 _(
197 b'not removing %s: file has been marked for add'
204 b'not removing %s: file has been marked for add'
198 b' (use forget to undo)\n'
205 b' (use forget to undo)\n'
199 ),
206 ),
200 )
207 )
201 or result
208 or result
202 )
209 )
203
210
204 # Need to lock because standin files are deleted then removed from the
211 # Need to lock because standin files are deleted then removed from the
205 # repository and we could race in-between.
212 # repository and we could race in-between.
206 with repo.wlock():
213 with repo.wlock():
207 lfdirstate = lfutil.openlfdirstate(ui, repo)
214 lfdirstate = lfutil.openlfdirstate(ui, repo)
208 for f in sorted(remove):
215 for f in sorted(remove):
209 if ui.verbose or not m.exact(f):
216 if ui.verbose or not m.exact(f):
210 ui.status(_(b'removing %s\n') % uipathfn(f))
217 ui.status(_(b'removing %s\n') % uipathfn(f))
211
218
212 if not dryrun:
219 if not dryrun:
213 if not after:
220 if not after:
214 repo.wvfs.unlinkpath(f, ignoremissing=True)
221 repo.wvfs.unlinkpath(f, ignoremissing=True)
215
222
216 if dryrun:
223 if dryrun:
217 return result
224 return result
218
225
219 remove = [lfutil.standin(f) for f in remove]
226 remove = [lfutil.standin(f) for f in remove]
220 # If this is being called by addremove, let the original addremove
227 # If this is being called by addremove, let the original addremove
221 # function handle this.
228 # function handle this.
222 if not isaddremove:
229 if not isaddremove:
223 for f in remove:
230 for f in remove:
224 repo.wvfs.unlinkpath(f, ignoremissing=True)
231 repo.wvfs.unlinkpath(f, ignoremissing=True)
225 repo[None].forget(remove)
232 repo[None].forget(remove)
226
233
227 for f in remove:
234 for f in remove:
228 lfutil.synclfdirstate(
235 lfutil.synclfdirstate(
229 repo, lfdirstate, lfutil.splitstandin(f), False
236 repo, lfdirstate, lfutil.splitstandin(f), False
230 )
237 )
231
238
232 lfdirstate.write()
239 lfdirstate.write()
233
240
234 return result
241 return result
235
242
236
243
237 # For overriding mercurial.hgweb.webcommands so that largefiles will
244 # For overriding mercurial.hgweb.webcommands so that largefiles will
238 # appear at their right place in the manifests.
245 # appear at their right place in the manifests.
239 @eh.wrapfunction(webcommands, b'decodepath')
246 @eh.wrapfunction(webcommands, b'decodepath')
240 def decodepath(orig, path):
247 def decodepath(orig, path):
241 return lfutil.splitstandin(path) or path
248 return lfutil.splitstandin(path) or path
242
249
243
250
244 # -- Wrappers: modify existing commands --------------------------------
251 # -- Wrappers: modify existing commands --------------------------------
245
252
246
253
247 @eh.wrapcommand(
254 @eh.wrapcommand(
248 b'add',
255 b'add',
249 opts=[
256 opts=[
250 (b'', b'large', None, _(b'add as largefile')),
257 (b'', b'large', None, _(b'add as largefile')),
251 (b'', b'normal', None, _(b'add as normal file')),
258 (b'', b'normal', None, _(b'add as normal file')),
252 (
259 (
253 b'',
260 b'',
254 b'lfsize',
261 b'lfsize',
255 b'',
262 b'',
256 _(
263 _(
257 b'add all files above this size (in megabytes) '
264 b'add all files above this size (in megabytes) '
258 b'as largefiles (default: 10)'
265 b'as largefiles (default: 10)'
259 ),
266 ),
260 ),
267 ),
261 ],
268 ],
262 )
269 )
263 def overrideadd(orig, ui, repo, *pats, **opts):
270 def overrideadd(orig, ui, repo, *pats, **opts):
264 if opts.get(r'normal') and opts.get(r'large'):
271 if opts.get(r'normal') and opts.get(r'large'):
265 raise error.Abort(_(b'--normal cannot be used with --large'))
272 raise error.Abort(_(b'--normal cannot be used with --large'))
266 return orig(ui, repo, *pats, **opts)
273 return orig(ui, repo, *pats, **opts)
267
274
268
275
269 @eh.wrapfunction(cmdutil, b'add')
276 @eh.wrapfunction(cmdutil, b'add')
270 def cmdutiladd(orig, ui, repo, matcher, prefix, uipathfn, explicitonly, **opts):
277 def cmdutiladd(orig, ui, repo, matcher, prefix, uipathfn, explicitonly, **opts):
271 # The --normal flag short circuits this override
278 # The --normal flag short circuits this override
272 if opts.get(r'normal'):
279 if opts.get(r'normal'):
273 return orig(ui, repo, matcher, prefix, uipathfn, explicitonly, **opts)
280 return orig(ui, repo, matcher, prefix, uipathfn, explicitonly, **opts)
274
281
275 ladded, lbad = addlargefiles(ui, repo, False, matcher, uipathfn, **opts)
282 ladded, lbad = addlargefiles(ui, repo, False, matcher, uipathfn, **opts)
276 normalmatcher = composenormalfilematcher(
283 normalmatcher = composenormalfilematcher(
277 matcher, repo[None].manifest(), ladded
284 matcher, repo[None].manifest(), ladded
278 )
285 )
279 bad = orig(ui, repo, normalmatcher, prefix, uipathfn, explicitonly, **opts)
286 bad = orig(ui, repo, normalmatcher, prefix, uipathfn, explicitonly, **opts)
280
287
281 bad.extend(f for f in lbad)
288 bad.extend(f for f in lbad)
282 return bad
289 return bad
283
290
284
291
285 @eh.wrapfunction(cmdutil, b'remove')
292 @eh.wrapfunction(cmdutil, b'remove')
286 def cmdutilremove(
293 def cmdutilremove(
287 orig, ui, repo, matcher, prefix, uipathfn, after, force, subrepos, dryrun
294 orig, ui, repo, matcher, prefix, uipathfn, after, force, subrepos, dryrun
288 ):
295 ):
289 normalmatcher = composenormalfilematcher(matcher, repo[None].manifest())
296 normalmatcher = composenormalfilematcher(matcher, repo[None].manifest())
290 result = orig(
297 result = orig(
291 ui,
298 ui,
292 repo,
299 repo,
293 normalmatcher,
300 normalmatcher,
294 prefix,
301 prefix,
295 uipathfn,
302 uipathfn,
296 after,
303 after,
297 force,
304 force,
298 subrepos,
305 subrepos,
299 dryrun,
306 dryrun,
300 )
307 )
301 return (
308 return (
302 removelargefiles(
309 removelargefiles(
303 ui, repo, False, matcher, uipathfn, dryrun, after=after, force=force
310 ui, repo, False, matcher, uipathfn, dryrun, after=after, force=force
304 )
311 )
305 or result
312 or result
306 )
313 )
307
314
308
315
309 @eh.wrapfunction(subrepo.hgsubrepo, b'status')
316 @eh.wrapfunction(subrepo.hgsubrepo, b'status')
310 def overridestatusfn(orig, repo, rev2, **opts):
317 def overridestatusfn(orig, repo, rev2, **opts):
311 try:
318 with lfstatus(repo._repo):
312 repo._repo.lfstatus = True
313 return orig(repo, rev2, **opts)
319 return orig(repo, rev2, **opts)
314 finally:
315 repo._repo.lfstatus = False
316
320
317
321
318 @eh.wrapcommand(b'status')
322 @eh.wrapcommand(b'status')
319 def overridestatus(orig, ui, repo, *pats, **opts):
323 def overridestatus(orig, ui, repo, *pats, **opts):
320 try:
324 with lfstatus(repo):
321 repo.lfstatus = True
322 return orig(ui, repo, *pats, **opts)
325 return orig(ui, repo, *pats, **opts)
323 finally:
324 repo.lfstatus = False
325
326
326
327
327 @eh.wrapfunction(subrepo.hgsubrepo, b'dirty')
328 @eh.wrapfunction(subrepo.hgsubrepo, b'dirty')
328 def overridedirty(orig, repo, ignoreupdate=False, missing=False):
329 def overridedirty(orig, repo, ignoreupdate=False, missing=False):
329 try:
330 with lfstatus(repo._repo):
330 repo._repo.lfstatus = True
331 return orig(repo, ignoreupdate=ignoreupdate, missing=missing)
331 return orig(repo, ignoreupdate=ignoreupdate, missing=missing)
332 finally:
333 repo._repo.lfstatus = False
334
332
335
333
336 @eh.wrapcommand(b'log')
334 @eh.wrapcommand(b'log')
337 def overridelog(orig, ui, repo, *pats, **opts):
335 def overridelog(orig, ui, repo, *pats, **opts):
338 def overridematchandpats(
336 def overridematchandpats(
339 orig,
337 orig,
340 ctx,
338 ctx,
341 pats=(),
339 pats=(),
342 opts=None,
340 opts=None,
343 globbed=False,
341 globbed=False,
344 default=b'relpath',
342 default=b'relpath',
345 badfn=None,
343 badfn=None,
346 ):
344 ):
347 """Matcher that merges root directory with .hglf, suitable for log.
345 """Matcher that merges root directory with .hglf, suitable for log.
348 It is still possible to match .hglf directly.
346 It is still possible to match .hglf directly.
349 For any listed files run log on the standin too.
347 For any listed files run log on the standin too.
350 matchfn tries both the given filename and with .hglf stripped.
348 matchfn tries both the given filename and with .hglf stripped.
351 """
349 """
352 if opts is None:
350 if opts is None:
353 opts = {}
351 opts = {}
354 matchandpats = orig(ctx, pats, opts, globbed, default, badfn=badfn)
352 matchandpats = orig(ctx, pats, opts, globbed, default, badfn=badfn)
355 m, p = copy.copy(matchandpats)
353 m, p = copy.copy(matchandpats)
356
354
357 if m.always():
355 if m.always():
358 # We want to match everything anyway, so there's no benefit trying
356 # We want to match everything anyway, so there's no benefit trying
359 # to add standins.
357 # to add standins.
360 return matchandpats
358 return matchandpats
361
359
362 pats = set(p)
360 pats = set(p)
363
361
364 def fixpats(pat, tostandin=lfutil.standin):
362 def fixpats(pat, tostandin=lfutil.standin):
365 if pat.startswith(b'set:'):
363 if pat.startswith(b'set:'):
366 return pat
364 return pat
367
365
368 kindpat = matchmod._patsplit(pat, None)
366 kindpat = matchmod._patsplit(pat, None)
369
367
370 if kindpat[0] is not None:
368 if kindpat[0] is not None:
371 return kindpat[0] + b':' + tostandin(kindpat[1])
369 return kindpat[0] + b':' + tostandin(kindpat[1])
372 return tostandin(kindpat[1])
370 return tostandin(kindpat[1])
373
371
374 cwd = repo.getcwd()
372 cwd = repo.getcwd()
375 if cwd:
373 if cwd:
376 hglf = lfutil.shortname
374 hglf = lfutil.shortname
377 back = util.pconvert(repo.pathto(hglf)[: -len(hglf)])
375 back = util.pconvert(repo.pathto(hglf)[: -len(hglf)])
378
376
379 def tostandin(f):
377 def tostandin(f):
380 # The file may already be a standin, so truncate the back
378 # The file may already be a standin, so truncate the back
381 # prefix and test before mangling it. This avoids turning
379 # prefix and test before mangling it. This avoids turning
382 # 'glob:../.hglf/foo*' into 'glob:../.hglf/../.hglf/foo*'.
380 # 'glob:../.hglf/foo*' into 'glob:../.hglf/../.hglf/foo*'.
383 if f.startswith(back) and lfutil.splitstandin(f[len(back) :]):
381 if f.startswith(back) and lfutil.splitstandin(f[len(back) :]):
384 return f
382 return f
385
383
386 # An absolute path is from outside the repo, so truncate the
384 # An absolute path is from outside the repo, so truncate the
387 # path to the root before building the standin. Otherwise cwd
385 # path to the root before building the standin. Otherwise cwd
388 # is somewhere in the repo, relative to root, and needs to be
386 # is somewhere in the repo, relative to root, and needs to be
389 # prepended before building the standin.
387 # prepended before building the standin.
390 if os.path.isabs(cwd):
388 if os.path.isabs(cwd):
391 f = f[len(back) :]
389 f = f[len(back) :]
392 else:
390 else:
393 f = cwd + b'/' + f
391 f = cwd + b'/' + f
394 return back + lfutil.standin(f)
392 return back + lfutil.standin(f)
395
393
396 else:
394 else:
397
395
398 def tostandin(f):
396 def tostandin(f):
399 if lfutil.isstandin(f):
397 if lfutil.isstandin(f):
400 return f
398 return f
401 return lfutil.standin(f)
399 return lfutil.standin(f)
402
400
403 pats.update(fixpats(f, tostandin) for f in p)
401 pats.update(fixpats(f, tostandin) for f in p)
404
402
405 for i in range(0, len(m._files)):
403 for i in range(0, len(m._files)):
406 # Don't add '.hglf' to m.files, since that is already covered by '.'
404 # Don't add '.hglf' to m.files, since that is already covered by '.'
407 if m._files[i] == b'.':
405 if m._files[i] == b'.':
408 continue
406 continue
409 standin = lfutil.standin(m._files[i])
407 standin = lfutil.standin(m._files[i])
410 # If the "standin" is a directory, append instead of replace to
408 # If the "standin" is a directory, append instead of replace to
411 # support naming a directory on the command line with only
409 # support naming a directory on the command line with only
412 # largefiles. The original directory is kept to support normal
410 # largefiles. The original directory is kept to support normal
413 # files.
411 # files.
414 if standin in ctx:
412 if standin in ctx:
415 m._files[i] = standin
413 m._files[i] = standin
416 elif m._files[i] not in ctx and repo.wvfs.isdir(standin):
414 elif m._files[i] not in ctx and repo.wvfs.isdir(standin):
417 m._files.append(standin)
415 m._files.append(standin)
418
416
419 m._fileset = set(m._files)
417 m._fileset = set(m._files)
420 m.always = lambda: False
418 m.always = lambda: False
421 origmatchfn = m.matchfn
419 origmatchfn = m.matchfn
422
420
423 def lfmatchfn(f):
421 def lfmatchfn(f):
424 lf = lfutil.splitstandin(f)
422 lf = lfutil.splitstandin(f)
425 if lf is not None and origmatchfn(lf):
423 if lf is not None and origmatchfn(lf):
426 return True
424 return True
427 r = origmatchfn(f)
425 r = origmatchfn(f)
428 return r
426 return r
429
427
430 m.matchfn = lfmatchfn
428 m.matchfn = lfmatchfn
431
429
432 ui.debug(b'updated patterns: %s\n' % b', '.join(sorted(pats)))
430 ui.debug(b'updated patterns: %s\n' % b', '.join(sorted(pats)))
433 return m, pats
431 return m, pats
434
432
435 # For hg log --patch, the match object is used in two different senses:
433 # For hg log --patch, the match object is used in two different senses:
436 # (1) to determine what revisions should be printed out, and
434 # (1) to determine what revisions should be printed out, and
437 # (2) to determine what files to print out diffs for.
435 # (2) to determine what files to print out diffs for.
438 # The magic matchandpats override should be used for case (1) but not for
436 # The magic matchandpats override should be used for case (1) but not for
439 # case (2).
437 # case (2).
440 oldmatchandpats = scmutil.matchandpats
438 oldmatchandpats = scmutil.matchandpats
441
439
442 def overridemakefilematcher(orig, repo, pats, opts, badfn=None):
440 def overridemakefilematcher(orig, repo, pats, opts, badfn=None):
443 wctx = repo[None]
441 wctx = repo[None]
444 match, pats = oldmatchandpats(wctx, pats, opts, badfn=badfn)
442 match, pats = oldmatchandpats(wctx, pats, opts, badfn=badfn)
445 return lambda ctx: match
443 return lambda ctx: match
446
444
447 wrappedmatchandpats = extensions.wrappedfunction(
445 wrappedmatchandpats = extensions.wrappedfunction(
448 scmutil, b'matchandpats', overridematchandpats
446 scmutil, b'matchandpats', overridematchandpats
449 )
447 )
450 wrappedmakefilematcher = extensions.wrappedfunction(
448 wrappedmakefilematcher = extensions.wrappedfunction(
451 logcmdutil, b'_makenofollowfilematcher', overridemakefilematcher
449 logcmdutil, b'_makenofollowfilematcher', overridemakefilematcher
452 )
450 )
453 with wrappedmatchandpats, wrappedmakefilematcher:
451 with wrappedmatchandpats, wrappedmakefilematcher:
454 return orig(ui, repo, *pats, **opts)
452 return orig(ui, repo, *pats, **opts)
455
453
456
454
457 @eh.wrapcommand(
455 @eh.wrapcommand(
458 b'verify',
456 b'verify',
459 opts=[
457 opts=[
460 (
458 (
461 b'',
459 b'',
462 b'large',
460 b'large',
463 None,
461 None,
464 _(b'verify that all largefiles in current revision exists'),
462 _(b'verify that all largefiles in current revision exists'),
465 ),
463 ),
466 (
464 (
467 b'',
465 b'',
468 b'lfa',
466 b'lfa',
469 None,
467 None,
470 _(b'verify largefiles in all revisions, not just current'),
468 _(b'verify largefiles in all revisions, not just current'),
471 ),
469 ),
472 (
470 (
473 b'',
471 b'',
474 b'lfc',
472 b'lfc',
475 None,
473 None,
476 _(b'verify local largefile contents, not just existence'),
474 _(b'verify local largefile contents, not just existence'),
477 ),
475 ),
478 ],
476 ],
479 )
477 )
480 def overrideverify(orig, ui, repo, *pats, **opts):
478 def overrideverify(orig, ui, repo, *pats, **opts):
481 large = opts.pop(r'large', False)
479 large = opts.pop(r'large', False)
482 all = opts.pop(r'lfa', False)
480 all = opts.pop(r'lfa', False)
483 contents = opts.pop(r'lfc', False)
481 contents = opts.pop(r'lfc', False)
484
482
485 result = orig(ui, repo, *pats, **opts)
483 result = orig(ui, repo, *pats, **opts)
486 if large or all or contents:
484 if large or all or contents:
487 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
485 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
488 return result
486 return result
489
487
490
488
491 @eh.wrapcommand(
489 @eh.wrapcommand(
492 b'debugstate',
490 b'debugstate',
493 opts=[(b'', b'large', None, _(b'display largefiles dirstate'))],
491 opts=[(b'', b'large', None, _(b'display largefiles dirstate'))],
494 )
492 )
495 def overridedebugstate(orig, ui, repo, *pats, **opts):
493 def overridedebugstate(orig, ui, repo, *pats, **opts):
496 large = opts.pop(r'large', False)
494 large = opts.pop(r'large', False)
497 if large:
495 if large:
498
496
499 class fakerepo(object):
497 class fakerepo(object):
500 dirstate = lfutil.openlfdirstate(ui, repo)
498 dirstate = lfutil.openlfdirstate(ui, repo)
501
499
502 orig(ui, fakerepo, *pats, **opts)
500 orig(ui, fakerepo, *pats, **opts)
503 else:
501 else:
504 orig(ui, repo, *pats, **opts)
502 orig(ui, repo, *pats, **opts)
505
503
506
504
507 # Before starting the manifest merge, merge.updates will call
505 # Before starting the manifest merge, merge.updates will call
508 # _checkunknownfile to check if there are any files in the merged-in
506 # _checkunknownfile to check if there are any files in the merged-in
509 # changeset that collide with unknown files in the working copy.
507 # changeset that collide with unknown files in the working copy.
510 #
508 #
511 # The largefiles are seen as unknown, so this prevents us from merging
509 # The largefiles are seen as unknown, so this prevents us from merging
512 # in a file 'foo' if we already have a largefile with the same name.
510 # in a file 'foo' if we already have a largefile with the same name.
513 #
511 #
514 # The overridden function filters the unknown files by removing any
512 # The overridden function filters the unknown files by removing any
515 # largefiles. This makes the merge proceed and we can then handle this
513 # largefiles. This makes the merge proceed and we can then handle this
516 # case further in the overridden calculateupdates function below.
514 # case further in the overridden calculateupdates function below.
517 @eh.wrapfunction(merge, b'_checkunknownfile')
515 @eh.wrapfunction(merge, b'_checkunknownfile')
518 def overridecheckunknownfile(origfn, repo, wctx, mctx, f, f2=None):
516 def overridecheckunknownfile(origfn, repo, wctx, mctx, f, f2=None):
519 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
517 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
520 return False
518 return False
521 return origfn(repo, wctx, mctx, f, f2)
519 return origfn(repo, wctx, mctx, f, f2)
522
520
523
521
524 # The manifest merge handles conflicts on the manifest level. We want
522 # The manifest merge handles conflicts on the manifest level. We want
525 # to handle changes in largefile-ness of files at this level too.
523 # to handle changes in largefile-ness of files at this level too.
526 #
524 #
527 # The strategy is to run the original calculateupdates and then process
525 # The strategy is to run the original calculateupdates and then process
528 # the action list it outputs. There are two cases we need to deal with:
526 # the action list it outputs. There are two cases we need to deal with:
529 #
527 #
530 # 1. Normal file in p1, largefile in p2. Here the largefile is
528 # 1. Normal file in p1, largefile in p2. Here the largefile is
531 # detected via its standin file, which will enter the working copy
529 # detected via its standin file, which will enter the working copy
532 # with a "get" action. It is not "merge" since the standin is all
530 # with a "get" action. It is not "merge" since the standin is all
533 # Mercurial is concerned with at this level -- the link to the
531 # Mercurial is concerned with at this level -- the link to the
534 # existing normal file is not relevant here.
532 # existing normal file is not relevant here.
535 #
533 #
536 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
534 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
537 # since the largefile will be present in the working copy and
535 # since the largefile will be present in the working copy and
538 # different from the normal file in p2. Mercurial therefore
536 # different from the normal file in p2. Mercurial therefore
539 # triggers a merge action.
537 # triggers a merge action.
540 #
538 #
541 # In both cases, we prompt the user and emit new actions to either
539 # In both cases, we prompt the user and emit new actions to either
542 # remove the standin (if the normal file was kept) or to remove the
540 # remove the standin (if the normal file was kept) or to remove the
543 # normal file and get the standin (if the largefile was kept). The
541 # normal file and get the standin (if the largefile was kept). The
544 # default prompt answer is to use the largefile version since it was
542 # default prompt answer is to use the largefile version since it was
545 # presumably changed on purpose.
543 # presumably changed on purpose.
546 #
544 #
547 # Finally, the merge.applyupdates function will then take care of
545 # Finally, the merge.applyupdates function will then take care of
548 # writing the files into the working copy and lfcommands.updatelfiles
546 # writing the files into the working copy and lfcommands.updatelfiles
549 # will update the largefiles.
547 # will update the largefiles.
550 @eh.wrapfunction(merge, b'calculateupdates')
548 @eh.wrapfunction(merge, b'calculateupdates')
551 def overridecalculateupdates(
549 def overridecalculateupdates(
552 origfn, repo, p1, p2, pas, branchmerge, force, acceptremote, *args, **kwargs
550 origfn, repo, p1, p2, pas, branchmerge, force, acceptremote, *args, **kwargs
553 ):
551 ):
554 overwrite = force and not branchmerge
552 overwrite = force and not branchmerge
555 actions, diverge, renamedelete = origfn(
553 actions, diverge, renamedelete = origfn(
556 repo, p1, p2, pas, branchmerge, force, acceptremote, *args, **kwargs
554 repo, p1, p2, pas, branchmerge, force, acceptremote, *args, **kwargs
557 )
555 )
558
556
559 if overwrite:
557 if overwrite:
560 return actions, diverge, renamedelete
558 return actions, diverge, renamedelete
561
559
562 # Convert to dictionary with filename as key and action as value.
560 # Convert to dictionary with filename as key and action as value.
563 lfiles = set()
561 lfiles = set()
564 for f in actions:
562 for f in actions:
565 splitstandin = lfutil.splitstandin(f)
563 splitstandin = lfutil.splitstandin(f)
566 if splitstandin is not None and splitstandin in p1:
564 if splitstandin is not None and splitstandin in p1:
567 lfiles.add(splitstandin)
565 lfiles.add(splitstandin)
568 elif lfutil.standin(f) in p1:
566 elif lfutil.standin(f) in p1:
569 lfiles.add(f)
567 lfiles.add(f)
570
568
571 for lfile in sorted(lfiles):
569 for lfile in sorted(lfiles):
572 standin = lfutil.standin(lfile)
570 standin = lfutil.standin(lfile)
573 (lm, largs, lmsg) = actions.get(lfile, (None, None, None))
571 (lm, largs, lmsg) = actions.get(lfile, (None, None, None))
574 (sm, sargs, smsg) = actions.get(standin, (None, None, None))
572 (sm, sargs, smsg) = actions.get(standin, (None, None, None))
575 if sm in (b'g', b'dc') and lm != b'r':
573 if sm in (b'g', b'dc') and lm != b'r':
576 if sm == b'dc':
574 if sm == b'dc':
577 f1, f2, fa, move, anc = sargs
575 f1, f2, fa, move, anc = sargs
578 sargs = (p2[f2].flags(), False)
576 sargs = (p2[f2].flags(), False)
579 # Case 1: normal file in the working copy, largefile in
577 # Case 1: normal file in the working copy, largefile in
580 # the second parent
578 # the second parent
581 usermsg = (
579 usermsg = (
582 _(
580 _(
583 b'remote turned local normal file %s into a largefile\n'
581 b'remote turned local normal file %s into a largefile\n'
584 b'use (l)argefile or keep (n)ormal file?'
582 b'use (l)argefile or keep (n)ormal file?'
585 b'$$ &Largefile $$ &Normal file'
583 b'$$ &Largefile $$ &Normal file'
586 )
584 )
587 % lfile
585 % lfile
588 )
586 )
589 if repo.ui.promptchoice(usermsg, 0) == 0: # pick remote largefile
587 if repo.ui.promptchoice(usermsg, 0) == 0: # pick remote largefile
590 actions[lfile] = (b'r', None, b'replaced by standin')
588 actions[lfile] = (b'r', None, b'replaced by standin')
591 actions[standin] = (b'g', sargs, b'replaces standin')
589 actions[standin] = (b'g', sargs, b'replaces standin')
592 else: # keep local normal file
590 else: # keep local normal file
593 actions[lfile] = (b'k', None, b'replaces standin')
591 actions[lfile] = (b'k', None, b'replaces standin')
594 if branchmerge:
592 if branchmerge:
595 actions[standin] = (b'k', None, b'replaced by non-standin')
593 actions[standin] = (b'k', None, b'replaced by non-standin')
596 else:
594 else:
597 actions[standin] = (b'r', None, b'replaced by non-standin')
595 actions[standin] = (b'r', None, b'replaced by non-standin')
598 elif lm in (b'g', b'dc') and sm != b'r':
596 elif lm in (b'g', b'dc') and sm != b'r':
599 if lm == b'dc':
597 if lm == b'dc':
600 f1, f2, fa, move, anc = largs
598 f1, f2, fa, move, anc = largs
601 largs = (p2[f2].flags(), False)
599 largs = (p2[f2].flags(), False)
602 # Case 2: largefile in the working copy, normal file in
600 # Case 2: largefile in the working copy, normal file in
603 # the second parent
601 # the second parent
604 usermsg = (
602 usermsg = (
605 _(
603 _(
606 b'remote turned local largefile %s into a normal file\n'
604 b'remote turned local largefile %s into a normal file\n'
607 b'keep (l)argefile or use (n)ormal file?'
605 b'keep (l)argefile or use (n)ormal file?'
608 b'$$ &Largefile $$ &Normal file'
606 b'$$ &Largefile $$ &Normal file'
609 )
607 )
610 % lfile
608 % lfile
611 )
609 )
612 if repo.ui.promptchoice(usermsg, 0) == 0: # keep local largefile
610 if repo.ui.promptchoice(usermsg, 0) == 0: # keep local largefile
613 if branchmerge:
611 if branchmerge:
614 # largefile can be restored from standin safely
612 # largefile can be restored from standin safely
615 actions[lfile] = (b'k', None, b'replaced by standin')
613 actions[lfile] = (b'k', None, b'replaced by standin')
616 actions[standin] = (b'k', None, b'replaces standin')
614 actions[standin] = (b'k', None, b'replaces standin')
617 else:
615 else:
618 # "lfile" should be marked as "removed" without
616 # "lfile" should be marked as "removed" without
619 # removal of itself
617 # removal of itself
620 actions[lfile] = (
618 actions[lfile] = (
621 b'lfmr',
619 b'lfmr',
622 None,
620 None,
623 b'forget non-standin largefile',
621 b'forget non-standin largefile',
624 )
622 )
625
623
626 # linear-merge should treat this largefile as 're-added'
624 # linear-merge should treat this largefile as 're-added'
627 actions[standin] = (b'a', None, b'keep standin')
625 actions[standin] = (b'a', None, b'keep standin')
628 else: # pick remote normal file
626 else: # pick remote normal file
629 actions[lfile] = (b'g', largs, b'replaces standin')
627 actions[lfile] = (b'g', largs, b'replaces standin')
630 actions[standin] = (b'r', None, b'replaced by non-standin')
628 actions[standin] = (b'r', None, b'replaced by non-standin')
631
629
632 return actions, diverge, renamedelete
630 return actions, diverge, renamedelete
633
631
634
632
635 @eh.wrapfunction(merge, b'recordupdates')
633 @eh.wrapfunction(merge, b'recordupdates')
636 def mergerecordupdates(orig, repo, actions, branchmerge, getfiledata):
634 def mergerecordupdates(orig, repo, actions, branchmerge, getfiledata):
637 if b'lfmr' in actions:
635 if b'lfmr' in actions:
638 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
636 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
639 for lfile, args, msg in actions[b'lfmr']:
637 for lfile, args, msg in actions[b'lfmr']:
640 # this should be executed before 'orig', to execute 'remove'
638 # this should be executed before 'orig', to execute 'remove'
641 # before all other actions
639 # before all other actions
642 repo.dirstate.remove(lfile)
640 repo.dirstate.remove(lfile)
643 # make sure lfile doesn't get synclfdirstate'd as normal
641 # make sure lfile doesn't get synclfdirstate'd as normal
644 lfdirstate.add(lfile)
642 lfdirstate.add(lfile)
645 lfdirstate.write()
643 lfdirstate.write()
646
644
647 return orig(repo, actions, branchmerge, getfiledata)
645 return orig(repo, actions, branchmerge, getfiledata)
648
646
649
647
650 # Override filemerge to prompt the user about how they wish to merge
648 # Override filemerge to prompt the user about how they wish to merge
651 # largefiles. This will handle identical edits without prompting the user.
649 # largefiles. This will handle identical edits without prompting the user.
652 @eh.wrapfunction(filemerge, b'_filemerge')
650 @eh.wrapfunction(filemerge, b'_filemerge')
653 def overridefilemerge(
651 def overridefilemerge(
654 origfn, premerge, repo, wctx, mynode, orig, fcd, fco, fca, labels=None
652 origfn, premerge, repo, wctx, mynode, orig, fcd, fco, fca, labels=None
655 ):
653 ):
656 if not lfutil.isstandin(orig) or fcd.isabsent() or fco.isabsent():
654 if not lfutil.isstandin(orig) or fcd.isabsent() or fco.isabsent():
657 return origfn(
655 return origfn(
658 premerge, repo, wctx, mynode, orig, fcd, fco, fca, labels=labels
656 premerge, repo, wctx, mynode, orig, fcd, fco, fca, labels=labels
659 )
657 )
660
658
661 ahash = lfutil.readasstandin(fca).lower()
659 ahash = lfutil.readasstandin(fca).lower()
662 dhash = lfutil.readasstandin(fcd).lower()
660 dhash = lfutil.readasstandin(fcd).lower()
663 ohash = lfutil.readasstandin(fco).lower()
661 ohash = lfutil.readasstandin(fco).lower()
664 if (
662 if (
665 ohash != ahash
663 ohash != ahash
666 and ohash != dhash
664 and ohash != dhash
667 and (
665 and (
668 dhash == ahash
666 dhash == ahash
669 or repo.ui.promptchoice(
667 or repo.ui.promptchoice(
670 _(
668 _(
671 b'largefile %s has a merge conflict\nancestor was %s\n'
669 b'largefile %s has a merge conflict\nancestor was %s\n'
672 b'you can keep (l)ocal %s or take (o)ther %s.\n'
670 b'you can keep (l)ocal %s or take (o)ther %s.\n'
673 b'what do you want to do?'
671 b'what do you want to do?'
674 b'$$ &Local $$ &Other'
672 b'$$ &Local $$ &Other'
675 )
673 )
676 % (lfutil.splitstandin(orig), ahash, dhash, ohash),
674 % (lfutil.splitstandin(orig), ahash, dhash, ohash),
677 0,
675 0,
678 )
676 )
679 == 1
677 == 1
680 )
678 )
681 ):
679 ):
682 repo.wwrite(fcd.path(), fco.data(), fco.flags())
680 repo.wwrite(fcd.path(), fco.data(), fco.flags())
683 return True, 0, False
681 return True, 0, False
684
682
685
683
686 @eh.wrapfunction(copiesmod, b'pathcopies')
684 @eh.wrapfunction(copiesmod, b'pathcopies')
687 def copiespathcopies(orig, ctx1, ctx2, match=None):
685 def copiespathcopies(orig, ctx1, ctx2, match=None):
688 copies = orig(ctx1, ctx2, match=match)
686 copies = orig(ctx1, ctx2, match=match)
689 updated = {}
687 updated = {}
690
688
691 for k, v in pycompat.iteritems(copies):
689 for k, v in pycompat.iteritems(copies):
692 updated[lfutil.splitstandin(k) or k] = lfutil.splitstandin(v) or v
690 updated[lfutil.splitstandin(k) or k] = lfutil.splitstandin(v) or v
693
691
694 return updated
692 return updated
695
693
696
694
697 # Copy first changes the matchers to match standins instead of
695 # Copy first changes the matchers to match standins instead of
698 # largefiles. Then it overrides util.copyfile in that function it
696 # largefiles. Then it overrides util.copyfile in that function it
699 # checks if the destination largefile already exists. It also keeps a
697 # checks if the destination largefile already exists. It also keeps a
700 # list of copied files so that the largefiles can be copied and the
698 # list of copied files so that the largefiles can be copied and the
701 # dirstate updated.
699 # dirstate updated.
702 @eh.wrapfunction(cmdutil, b'copy')
700 @eh.wrapfunction(cmdutil, b'copy')
703 def overridecopy(orig, ui, repo, pats, opts, rename=False):
701 def overridecopy(orig, ui, repo, pats, opts, rename=False):
704 # doesn't remove largefile on rename
702 # doesn't remove largefile on rename
705 if len(pats) < 2:
703 if len(pats) < 2:
706 # this isn't legal, let the original function deal with it
704 # this isn't legal, let the original function deal with it
707 return orig(ui, repo, pats, opts, rename)
705 return orig(ui, repo, pats, opts, rename)
708
706
709 # This could copy both lfiles and normal files in one command,
707 # This could copy both lfiles and normal files in one command,
710 # but we don't want to do that. First replace their matcher to
708 # but we don't want to do that. First replace their matcher to
711 # only match normal files and run it, then replace it to just
709 # only match normal files and run it, then replace it to just
712 # match largefiles and run it again.
710 # match largefiles and run it again.
713 nonormalfiles = False
711 nonormalfiles = False
714 nolfiles = False
712 nolfiles = False
715 manifest = repo[None].manifest()
713 manifest = repo[None].manifest()
716
714
717 def normalfilesmatchfn(
715 def normalfilesmatchfn(
718 orig,
716 orig,
719 ctx,
717 ctx,
720 pats=(),
718 pats=(),
721 opts=None,
719 opts=None,
722 globbed=False,
720 globbed=False,
723 default=b'relpath',
721 default=b'relpath',
724 badfn=None,
722 badfn=None,
725 ):
723 ):
726 if opts is None:
724 if opts is None:
727 opts = {}
725 opts = {}
728 match = orig(ctx, pats, opts, globbed, default, badfn=badfn)
726 match = orig(ctx, pats, opts, globbed, default, badfn=badfn)
729 return composenormalfilematcher(match, manifest)
727 return composenormalfilematcher(match, manifest)
730
728
731 with extensions.wrappedfunction(scmutil, b'match', normalfilesmatchfn):
729 with extensions.wrappedfunction(scmutil, b'match', normalfilesmatchfn):
732 try:
730 try:
733 result = orig(ui, repo, pats, opts, rename)
731 result = orig(ui, repo, pats, opts, rename)
734 except error.Abort as e:
732 except error.Abort as e:
735 if pycompat.bytestr(e) != _(b'no files to copy'):
733 if pycompat.bytestr(e) != _(b'no files to copy'):
736 raise e
734 raise e
737 else:
735 else:
738 nonormalfiles = True
736 nonormalfiles = True
739 result = 0
737 result = 0
740
738
741 # The first rename can cause our current working directory to be removed.
739 # The first rename can cause our current working directory to be removed.
742 # In that case there is nothing left to copy/rename so just quit.
740 # In that case there is nothing left to copy/rename so just quit.
743 try:
741 try:
744 repo.getcwd()
742 repo.getcwd()
745 except OSError:
743 except OSError:
746 return result
744 return result
747
745
748 def makestandin(relpath):
746 def makestandin(relpath):
749 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
747 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
750 return repo.wvfs.join(lfutil.standin(path))
748 return repo.wvfs.join(lfutil.standin(path))
751
749
752 fullpats = scmutil.expandpats(pats)
750 fullpats = scmutil.expandpats(pats)
753 dest = fullpats[-1]
751 dest = fullpats[-1]
754
752
755 if os.path.isdir(dest):
753 if os.path.isdir(dest):
756 if not os.path.isdir(makestandin(dest)):
754 if not os.path.isdir(makestandin(dest)):
757 os.makedirs(makestandin(dest))
755 os.makedirs(makestandin(dest))
758
756
759 try:
757 try:
760 # When we call orig below it creates the standins but we don't add
758 # When we call orig below it creates the standins but we don't add
761 # them to the dir state until later so lock during that time.
759 # them to the dir state until later so lock during that time.
762 wlock = repo.wlock()
760 wlock = repo.wlock()
763
761
764 manifest = repo[None].manifest()
762 manifest = repo[None].manifest()
765
763
766 def overridematch(
764 def overridematch(
767 orig,
765 orig,
768 ctx,
766 ctx,
769 pats=(),
767 pats=(),
770 opts=None,
768 opts=None,
771 globbed=False,
769 globbed=False,
772 default=b'relpath',
770 default=b'relpath',
773 badfn=None,
771 badfn=None,
774 ):
772 ):
775 if opts is None:
773 if opts is None:
776 opts = {}
774 opts = {}
777 newpats = []
775 newpats = []
778 # The patterns were previously mangled to add the standin
776 # The patterns were previously mangled to add the standin
779 # directory; we need to remove that now
777 # directory; we need to remove that now
780 for pat in pats:
778 for pat in pats:
781 if matchmod.patkind(pat) is None and lfutil.shortname in pat:
779 if matchmod.patkind(pat) is None and lfutil.shortname in pat:
782 newpats.append(pat.replace(lfutil.shortname, b''))
780 newpats.append(pat.replace(lfutil.shortname, b''))
783 else:
781 else:
784 newpats.append(pat)
782 newpats.append(pat)
785 match = orig(ctx, newpats, opts, globbed, default, badfn=badfn)
783 match = orig(ctx, newpats, opts, globbed, default, badfn=badfn)
786 m = copy.copy(match)
784 m = copy.copy(match)
787 lfile = lambda f: lfutil.standin(f) in manifest
785 lfile = lambda f: lfutil.standin(f) in manifest
788 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
786 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
789 m._fileset = set(m._files)
787 m._fileset = set(m._files)
790 origmatchfn = m.matchfn
788 origmatchfn = m.matchfn
791
789
792 def matchfn(f):
790 def matchfn(f):
793 lfile = lfutil.splitstandin(f)
791 lfile = lfutil.splitstandin(f)
794 return (
792 return (
795 lfile is not None
793 lfile is not None
796 and (f in manifest)
794 and (f in manifest)
797 and origmatchfn(lfile)
795 and origmatchfn(lfile)
798 or None
796 or None
799 )
797 )
800
798
801 m.matchfn = matchfn
799 m.matchfn = matchfn
802 return m
800 return m
803
801
804 listpats = []
802 listpats = []
805 for pat in pats:
803 for pat in pats:
806 if matchmod.patkind(pat) is not None:
804 if matchmod.patkind(pat) is not None:
807 listpats.append(pat)
805 listpats.append(pat)
808 else:
806 else:
809 listpats.append(makestandin(pat))
807 listpats.append(makestandin(pat))
810
808
811 copiedfiles = []
809 copiedfiles = []
812
810
813 def overridecopyfile(orig, src, dest, *args, **kwargs):
811 def overridecopyfile(orig, src, dest, *args, **kwargs):
814 if lfutil.shortname in src and dest.startswith(
812 if lfutil.shortname in src and dest.startswith(
815 repo.wjoin(lfutil.shortname)
813 repo.wjoin(lfutil.shortname)
816 ):
814 ):
817 destlfile = dest.replace(lfutil.shortname, b'')
815 destlfile = dest.replace(lfutil.shortname, b'')
818 if not opts[b'force'] and os.path.exists(destlfile):
816 if not opts[b'force'] and os.path.exists(destlfile):
819 raise IOError(
817 raise IOError(
820 b'', _(b'destination largefile already exists')
818 b'', _(b'destination largefile already exists')
821 )
819 )
822 copiedfiles.append((src, dest))
820 copiedfiles.append((src, dest))
823 orig(src, dest, *args, **kwargs)
821 orig(src, dest, *args, **kwargs)
824
822
825 with extensions.wrappedfunction(util, b'copyfile', overridecopyfile):
823 with extensions.wrappedfunction(util, b'copyfile', overridecopyfile):
826 with extensions.wrappedfunction(scmutil, b'match', overridematch):
824 with extensions.wrappedfunction(scmutil, b'match', overridematch):
827 result += orig(ui, repo, listpats, opts, rename)
825 result += orig(ui, repo, listpats, opts, rename)
828
826
829 lfdirstate = lfutil.openlfdirstate(ui, repo)
827 lfdirstate = lfutil.openlfdirstate(ui, repo)
830 for (src, dest) in copiedfiles:
828 for (src, dest) in copiedfiles:
831 if lfutil.shortname in src and dest.startswith(
829 if lfutil.shortname in src and dest.startswith(
832 repo.wjoin(lfutil.shortname)
830 repo.wjoin(lfutil.shortname)
833 ):
831 ):
834 srclfile = src.replace(repo.wjoin(lfutil.standin(b'')), b'')
832 srclfile = src.replace(repo.wjoin(lfutil.standin(b'')), b'')
835 destlfile = dest.replace(repo.wjoin(lfutil.standin(b'')), b'')
833 destlfile = dest.replace(repo.wjoin(lfutil.standin(b'')), b'')
836 destlfiledir = repo.wvfs.dirname(repo.wjoin(destlfile)) or b'.'
834 destlfiledir = repo.wvfs.dirname(repo.wjoin(destlfile)) or b'.'
837 if not os.path.isdir(destlfiledir):
835 if not os.path.isdir(destlfiledir):
838 os.makedirs(destlfiledir)
836 os.makedirs(destlfiledir)
839 if rename:
837 if rename:
840 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
838 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
841
839
842 # The file is gone, but this deletes any empty parent
840 # The file is gone, but this deletes any empty parent
843 # directories as a side-effect.
841 # directories as a side-effect.
844 repo.wvfs.unlinkpath(srclfile, ignoremissing=True)
842 repo.wvfs.unlinkpath(srclfile, ignoremissing=True)
845 lfdirstate.remove(srclfile)
843 lfdirstate.remove(srclfile)
846 else:
844 else:
847 util.copyfile(repo.wjoin(srclfile), repo.wjoin(destlfile))
845 util.copyfile(repo.wjoin(srclfile), repo.wjoin(destlfile))
848
846
849 lfdirstate.add(destlfile)
847 lfdirstate.add(destlfile)
850 lfdirstate.write()
848 lfdirstate.write()
851 except error.Abort as e:
849 except error.Abort as e:
852 if pycompat.bytestr(e) != _(b'no files to copy'):
850 if pycompat.bytestr(e) != _(b'no files to copy'):
853 raise e
851 raise e
854 else:
852 else:
855 nolfiles = True
853 nolfiles = True
856 finally:
854 finally:
857 wlock.release()
855 wlock.release()
858
856
859 if nolfiles and nonormalfiles:
857 if nolfiles and nonormalfiles:
860 raise error.Abort(_(b'no files to copy'))
858 raise error.Abort(_(b'no files to copy'))
861
859
862 return result
860 return result
863
861
864
862
865 # When the user calls revert, we have to be careful to not revert any
863 # When the user calls revert, we have to be careful to not revert any
866 # changes to other largefiles accidentally. This means we have to keep
864 # changes to other largefiles accidentally. This means we have to keep
867 # track of the largefiles that are being reverted so we only pull down
865 # track of the largefiles that are being reverted so we only pull down
868 # the necessary largefiles.
866 # the necessary largefiles.
869 #
867 #
870 # Standins are only updated (to match the hash of largefiles) before
868 # Standins are only updated (to match the hash of largefiles) before
871 # commits. Update the standins then run the original revert, changing
869 # commits. Update the standins then run the original revert, changing
872 # the matcher to hit standins instead of largefiles. Based on the
870 # the matcher to hit standins instead of largefiles. Based on the
873 # resulting standins update the largefiles.
871 # resulting standins update the largefiles.
874 @eh.wrapfunction(cmdutil, b'revert')
872 @eh.wrapfunction(cmdutil, b'revert')
875 def overriderevert(orig, ui, repo, ctx, parents, *pats, **opts):
873 def overriderevert(orig, ui, repo, ctx, parents, *pats, **opts):
876 # Because we put the standins in a bad state (by updating them)
874 # Because we put the standins in a bad state (by updating them)
877 # and then return them to a correct state we need to lock to
875 # and then return them to a correct state we need to lock to
878 # prevent others from changing them in their incorrect state.
876 # prevent others from changing them in their incorrect state.
879 with repo.wlock():
877 with repo.wlock():
880 lfdirstate = lfutil.openlfdirstate(ui, repo)
878 lfdirstate = lfutil.openlfdirstate(ui, repo)
881 s = lfutil.lfdirstatestatus(lfdirstate, repo)
879 s = lfutil.lfdirstatestatus(lfdirstate, repo)
882 lfdirstate.write()
880 lfdirstate.write()
883 for lfile in s.modified:
881 for lfile in s.modified:
884 lfutil.updatestandin(repo, lfile, lfutil.standin(lfile))
882 lfutil.updatestandin(repo, lfile, lfutil.standin(lfile))
885 for lfile in s.deleted:
883 for lfile in s.deleted:
886 fstandin = lfutil.standin(lfile)
884 fstandin = lfutil.standin(lfile)
887 if repo.wvfs.exists(fstandin):
885 if repo.wvfs.exists(fstandin):
888 repo.wvfs.unlink(fstandin)
886 repo.wvfs.unlink(fstandin)
889
887
890 oldstandins = lfutil.getstandinsstate(repo)
888 oldstandins = lfutil.getstandinsstate(repo)
891
889
892 def overridematch(
890 def overridematch(
893 orig,
891 orig,
894 mctx,
892 mctx,
895 pats=(),
893 pats=(),
896 opts=None,
894 opts=None,
897 globbed=False,
895 globbed=False,
898 default=b'relpath',
896 default=b'relpath',
899 badfn=None,
897 badfn=None,
900 ):
898 ):
901 if opts is None:
899 if opts is None:
902 opts = {}
900 opts = {}
903 match = orig(mctx, pats, opts, globbed, default, badfn=badfn)
901 match = orig(mctx, pats, opts, globbed, default, badfn=badfn)
904 m = copy.copy(match)
902 m = copy.copy(match)
905
903
906 # revert supports recursing into subrepos, and though largefiles
904 # revert supports recursing into subrepos, and though largefiles
907 # currently doesn't work correctly in that case, this match is
905 # currently doesn't work correctly in that case, this match is
908 # called, so the lfdirstate above may not be the correct one for
906 # called, so the lfdirstate above may not be the correct one for
909 # this invocation of match.
907 # this invocation of match.
910 lfdirstate = lfutil.openlfdirstate(
908 lfdirstate = lfutil.openlfdirstate(
911 mctx.repo().ui, mctx.repo(), False
909 mctx.repo().ui, mctx.repo(), False
912 )
910 )
913
911
914 wctx = repo[None]
912 wctx = repo[None]
915 matchfiles = []
913 matchfiles = []
916 for f in m._files:
914 for f in m._files:
917 standin = lfutil.standin(f)
915 standin = lfutil.standin(f)
918 if standin in ctx or standin in mctx:
916 if standin in ctx or standin in mctx:
919 matchfiles.append(standin)
917 matchfiles.append(standin)
920 elif standin in wctx or lfdirstate[f] == b'r':
918 elif standin in wctx or lfdirstate[f] == b'r':
921 continue
919 continue
922 else:
920 else:
923 matchfiles.append(f)
921 matchfiles.append(f)
924 m._files = matchfiles
922 m._files = matchfiles
925 m._fileset = set(m._files)
923 m._fileset = set(m._files)
926 origmatchfn = m.matchfn
924 origmatchfn = m.matchfn
927
925
928 def matchfn(f):
926 def matchfn(f):
929 lfile = lfutil.splitstandin(f)
927 lfile = lfutil.splitstandin(f)
930 if lfile is not None:
928 if lfile is not None:
931 return origmatchfn(lfile) and (f in ctx or f in mctx)
929 return origmatchfn(lfile) and (f in ctx or f in mctx)
932 return origmatchfn(f)
930 return origmatchfn(f)
933
931
934 m.matchfn = matchfn
932 m.matchfn = matchfn
935 return m
933 return m
936
934
937 with extensions.wrappedfunction(scmutil, b'match', overridematch):
935 with extensions.wrappedfunction(scmutil, b'match', overridematch):
938 orig(ui, repo, ctx, parents, *pats, **opts)
936 orig(ui, repo, ctx, parents, *pats, **opts)
939
937
940 newstandins = lfutil.getstandinsstate(repo)
938 newstandins = lfutil.getstandinsstate(repo)
941 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
939 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
942 # lfdirstate should be 'normallookup'-ed for updated files,
940 # lfdirstate should be 'normallookup'-ed for updated files,
943 # because reverting doesn't touch dirstate for 'normal' files
941 # because reverting doesn't touch dirstate for 'normal' files
944 # when target revision is explicitly specified: in such case,
942 # when target revision is explicitly specified: in such case,
945 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
943 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
946 # of target (standin) file.
944 # of target (standin) file.
947 lfcommands.updatelfiles(
945 lfcommands.updatelfiles(
948 ui, repo, filelist, printmessage=False, normallookup=True
946 ui, repo, filelist, printmessage=False, normallookup=True
949 )
947 )
950
948
951
949
952 # after pulling changesets, we need to take some extra care to get
950 # after pulling changesets, we need to take some extra care to get
953 # largefiles updated remotely
951 # largefiles updated remotely
954 @eh.wrapcommand(
952 @eh.wrapcommand(
955 b'pull',
953 b'pull',
956 opts=[
954 opts=[
957 (
955 (
958 b'',
956 b'',
959 b'all-largefiles',
957 b'all-largefiles',
960 None,
958 None,
961 _(b'download all pulled versions of largefiles (DEPRECATED)'),
959 _(b'download all pulled versions of largefiles (DEPRECATED)'),
962 ),
960 ),
963 (
961 (
964 b'',
962 b'',
965 b'lfrev',
963 b'lfrev',
966 [],
964 [],
967 _(b'download largefiles for these revisions'),
965 _(b'download largefiles for these revisions'),
968 _(b'REV'),
966 _(b'REV'),
969 ),
967 ),
970 ],
968 ],
971 )
969 )
972 def overridepull(orig, ui, repo, source=None, **opts):
970 def overridepull(orig, ui, repo, source=None, **opts):
973 revsprepull = len(repo)
971 revsprepull = len(repo)
974 if not source:
972 if not source:
975 source = b'default'
973 source = b'default'
976 repo.lfpullsource = source
974 repo.lfpullsource = source
977 result = orig(ui, repo, source, **opts)
975 result = orig(ui, repo, source, **opts)
978 revspostpull = len(repo)
976 revspostpull = len(repo)
979 lfrevs = opts.get(r'lfrev', [])
977 lfrevs = opts.get(r'lfrev', [])
980 if opts.get(r'all_largefiles'):
978 if opts.get(r'all_largefiles'):
981 lfrevs.append(b'pulled()')
979 lfrevs.append(b'pulled()')
982 if lfrevs and revspostpull > revsprepull:
980 if lfrevs and revspostpull > revsprepull:
983 numcached = 0
981 numcached = 0
984 repo.firstpulled = revsprepull # for pulled() revset expression
982 repo.firstpulled = revsprepull # for pulled() revset expression
985 try:
983 try:
986 for rev in scmutil.revrange(repo, lfrevs):
984 for rev in scmutil.revrange(repo, lfrevs):
987 ui.note(_(b'pulling largefiles for revision %d\n') % rev)
985 ui.note(_(b'pulling largefiles for revision %d\n') % rev)
988 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
986 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
989 numcached += len(cached)
987 numcached += len(cached)
990 finally:
988 finally:
991 del repo.firstpulled
989 del repo.firstpulled
992 ui.status(_(b"%d largefiles cached\n") % numcached)
990 ui.status(_(b"%d largefiles cached\n") % numcached)
993 return result
991 return result
994
992
995
993
996 @eh.wrapcommand(
994 @eh.wrapcommand(
997 b'push',
995 b'push',
998 opts=[
996 opts=[
999 (
997 (
1000 b'',
998 b'',
1001 b'lfrev',
999 b'lfrev',
1002 [],
1000 [],
1003 _(b'upload largefiles for these revisions'),
1001 _(b'upload largefiles for these revisions'),
1004 _(b'REV'),
1002 _(b'REV'),
1005 )
1003 )
1006 ],
1004 ],
1007 )
1005 )
1008 def overridepush(orig, ui, repo, *args, **kwargs):
1006 def overridepush(orig, ui, repo, *args, **kwargs):
1009 """Override push command and store --lfrev parameters in opargs"""
1007 """Override push command and store --lfrev parameters in opargs"""
1010 lfrevs = kwargs.pop(r'lfrev', None)
1008 lfrevs = kwargs.pop(r'lfrev', None)
1011 if lfrevs:
1009 if lfrevs:
1012 opargs = kwargs.setdefault(r'opargs', {})
1010 opargs = kwargs.setdefault(r'opargs', {})
1013 opargs[b'lfrevs'] = scmutil.revrange(repo, lfrevs)
1011 opargs[b'lfrevs'] = scmutil.revrange(repo, lfrevs)
1014 return orig(ui, repo, *args, **kwargs)
1012 return orig(ui, repo, *args, **kwargs)
1015
1013
1016
1014
1017 @eh.wrapfunction(exchange, b'pushoperation')
1015 @eh.wrapfunction(exchange, b'pushoperation')
1018 def exchangepushoperation(orig, *args, **kwargs):
1016 def exchangepushoperation(orig, *args, **kwargs):
1019 """Override pushoperation constructor and store lfrevs parameter"""
1017 """Override pushoperation constructor and store lfrevs parameter"""
1020 lfrevs = kwargs.pop(r'lfrevs', None)
1018 lfrevs = kwargs.pop(r'lfrevs', None)
1021 pushop = orig(*args, **kwargs)
1019 pushop = orig(*args, **kwargs)
1022 pushop.lfrevs = lfrevs
1020 pushop.lfrevs = lfrevs
1023 return pushop
1021 return pushop
1024
1022
1025
1023
1026 @eh.revsetpredicate(b'pulled()')
1024 @eh.revsetpredicate(b'pulled()')
1027 def pulledrevsetsymbol(repo, subset, x):
1025 def pulledrevsetsymbol(repo, subset, x):
1028 """Changesets that just has been pulled.
1026 """Changesets that just has been pulled.
1029
1027
1030 Only available with largefiles from pull --lfrev expressions.
1028 Only available with largefiles from pull --lfrev expressions.
1031
1029
1032 .. container:: verbose
1030 .. container:: verbose
1033
1031
1034 Some examples:
1032 Some examples:
1035
1033
1036 - pull largefiles for all new changesets::
1034 - pull largefiles for all new changesets::
1037
1035
1038 hg pull -lfrev "pulled()"
1036 hg pull -lfrev "pulled()"
1039
1037
1040 - pull largefiles for all new branch heads::
1038 - pull largefiles for all new branch heads::
1041
1039
1042 hg pull -lfrev "head(pulled()) and not closed()"
1040 hg pull -lfrev "head(pulled()) and not closed()"
1043
1041
1044 """
1042 """
1045
1043
1046 try:
1044 try:
1047 firstpulled = repo.firstpulled
1045 firstpulled = repo.firstpulled
1048 except AttributeError:
1046 except AttributeError:
1049 raise error.Abort(_(b"pulled() only available in --lfrev"))
1047 raise error.Abort(_(b"pulled() only available in --lfrev"))
1050 return smartset.baseset([r for r in subset if r >= firstpulled])
1048 return smartset.baseset([r for r in subset if r >= firstpulled])
1051
1049
1052
1050
1053 @eh.wrapcommand(
1051 @eh.wrapcommand(
1054 b'clone',
1052 b'clone',
1055 opts=[
1053 opts=[
1056 (
1054 (
1057 b'',
1055 b'',
1058 b'all-largefiles',
1056 b'all-largefiles',
1059 None,
1057 None,
1060 _(b'download all versions of all largefiles'),
1058 _(b'download all versions of all largefiles'),
1061 )
1059 )
1062 ],
1060 ],
1063 )
1061 )
1064 def overrideclone(orig, ui, source, dest=None, **opts):
1062 def overrideclone(orig, ui, source, dest=None, **opts):
1065 d = dest
1063 d = dest
1066 if d is None:
1064 if d is None:
1067 d = hg.defaultdest(source)
1065 d = hg.defaultdest(source)
1068 if opts.get(r'all_largefiles') and not hg.islocal(d):
1066 if opts.get(r'all_largefiles') and not hg.islocal(d):
1069 raise error.Abort(
1067 raise error.Abort(
1070 _(b'--all-largefiles is incompatible with non-local destination %s')
1068 _(b'--all-largefiles is incompatible with non-local destination %s')
1071 % d
1069 % d
1072 )
1070 )
1073
1071
1074 return orig(ui, source, dest, **opts)
1072 return orig(ui, source, dest, **opts)
1075
1073
1076
1074
1077 @eh.wrapfunction(hg, b'clone')
1075 @eh.wrapfunction(hg, b'clone')
1078 def hgclone(orig, ui, opts, *args, **kwargs):
1076 def hgclone(orig, ui, opts, *args, **kwargs):
1079 result = orig(ui, opts, *args, **kwargs)
1077 result = orig(ui, opts, *args, **kwargs)
1080
1078
1081 if result is not None:
1079 if result is not None:
1082 sourcerepo, destrepo = result
1080 sourcerepo, destrepo = result
1083 repo = destrepo.local()
1081 repo = destrepo.local()
1084
1082
1085 # When cloning to a remote repo (like through SSH), no repo is available
1083 # When cloning to a remote repo (like through SSH), no repo is available
1086 # from the peer. Therefore the largefiles can't be downloaded and the
1084 # from the peer. Therefore the largefiles can't be downloaded and the
1087 # hgrc can't be updated.
1085 # hgrc can't be updated.
1088 if not repo:
1086 if not repo:
1089 return result
1087 return result
1090
1088
1091 # Caching is implicitly limited to 'rev' option, since the dest repo was
1089 # Caching is implicitly limited to 'rev' option, since the dest repo was
1092 # truncated at that point. The user may expect a download count with
1090 # truncated at that point. The user may expect a download count with
1093 # this option, so attempt whether or not this is a largefile repo.
1091 # this option, so attempt whether or not this is a largefile repo.
1094 if opts.get(b'all_largefiles'):
1092 if opts.get(b'all_largefiles'):
1095 success, missing = lfcommands.downloadlfiles(ui, repo, None)
1093 success, missing = lfcommands.downloadlfiles(ui, repo, None)
1096
1094
1097 if missing != 0:
1095 if missing != 0:
1098 return None
1096 return None
1099
1097
1100 return result
1098 return result
1101
1099
1102
1100
1103 @eh.wrapcommand(b'rebase', extension=b'rebase')
1101 @eh.wrapcommand(b'rebase', extension=b'rebase')
1104 def overriderebase(orig, ui, repo, **opts):
1102 def overriderebase(orig, ui, repo, **opts):
1105 if not util.safehasattr(repo, b'_largefilesenabled'):
1103 if not util.safehasattr(repo, b'_largefilesenabled'):
1106 return orig(ui, repo, **opts)
1104 return orig(ui, repo, **opts)
1107
1105
1108 resuming = opts.get(r'continue')
1106 resuming = opts.get(r'continue')
1109 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1107 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1110 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1108 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1111 try:
1109 try:
1112 return orig(ui, repo, **opts)
1110 return orig(ui, repo, **opts)
1113 finally:
1111 finally:
1114 repo._lfstatuswriters.pop()
1112 repo._lfstatuswriters.pop()
1115 repo._lfcommithooks.pop()
1113 repo._lfcommithooks.pop()
1116
1114
1117
1115
1118 @eh.wrapcommand(b'archive')
1116 @eh.wrapcommand(b'archive')
1119 def overridearchivecmd(orig, ui, repo, dest, **opts):
1117 def overridearchivecmd(orig, ui, repo, dest, **opts):
1120 repo.unfiltered().lfstatus = True
1118 with lfstatus(repo.unfiltered()):
1121
1122 try:
1123 return orig(ui, repo.unfiltered(), dest, **opts)
1119 return orig(ui, repo.unfiltered(), dest, **opts)
1124 finally:
1125 repo.unfiltered().lfstatus = False
1126
1120
1127
1121
1128 @eh.wrapfunction(webcommands, b'archive')
1122 @eh.wrapfunction(webcommands, b'archive')
1129 def hgwebarchive(orig, web):
1123 def hgwebarchive(orig, web):
1130 web.repo.lfstatus = True
1124 with lfstatus(web.repo):
1131
1132 try:
1133 return orig(web)
1125 return orig(web)
1134 finally:
1135 web.repo.lfstatus = False
1136
1126
1137
1127
1138 @eh.wrapfunction(archival, b'archive')
1128 @eh.wrapfunction(archival, b'archive')
1139 def overridearchive(
1129 def overridearchive(
1140 orig,
1130 orig,
1141 repo,
1131 repo,
1142 dest,
1132 dest,
1143 node,
1133 node,
1144 kind,
1134 kind,
1145 decode=True,
1135 decode=True,
1146 match=None,
1136 match=None,
1147 prefix=b'',
1137 prefix=b'',
1148 mtime=None,
1138 mtime=None,
1149 subrepos=None,
1139 subrepos=None,
1150 ):
1140 ):
1151 # For some reason setting repo.lfstatus in hgwebarchive only changes the
1141 # For some reason setting repo.lfstatus in hgwebarchive only changes the
1152 # unfiltered repo's attr, so check that as well.
1142 # unfiltered repo's attr, so check that as well.
1153 if not repo.lfstatus and not repo.unfiltered().lfstatus:
1143 if not repo.lfstatus and not repo.unfiltered().lfstatus:
1154 return orig(
1144 return orig(
1155 repo, dest, node, kind, decode, match, prefix, mtime, subrepos
1145 repo, dest, node, kind, decode, match, prefix, mtime, subrepos
1156 )
1146 )
1157
1147
1158 # No need to lock because we are only reading history and
1148 # No need to lock because we are only reading history and
1159 # largefile caches, neither of which are modified.
1149 # largefile caches, neither of which are modified.
1160 if node is not None:
1150 if node is not None:
1161 lfcommands.cachelfiles(repo.ui, repo, node)
1151 lfcommands.cachelfiles(repo.ui, repo, node)
1162
1152
1163 if kind not in archival.archivers:
1153 if kind not in archival.archivers:
1164 raise error.Abort(_(b"unknown archive type '%s'") % kind)
1154 raise error.Abort(_(b"unknown archive type '%s'") % kind)
1165
1155
1166 ctx = repo[node]
1156 ctx = repo[node]
1167
1157
1168 if kind == b'files':
1158 if kind == b'files':
1169 if prefix:
1159 if prefix:
1170 raise error.Abort(_(b'cannot give prefix when archiving to files'))
1160 raise error.Abort(_(b'cannot give prefix when archiving to files'))
1171 else:
1161 else:
1172 prefix = archival.tidyprefix(dest, kind, prefix)
1162 prefix = archival.tidyprefix(dest, kind, prefix)
1173
1163
1174 def write(name, mode, islink, getdata):
1164 def write(name, mode, islink, getdata):
1175 if match and not match(name):
1165 if match and not match(name):
1176 return
1166 return
1177 data = getdata()
1167 data = getdata()
1178 if decode:
1168 if decode:
1179 data = repo.wwritedata(name, data)
1169 data = repo.wwritedata(name, data)
1180 archiver.addfile(prefix + name, mode, islink, data)
1170 archiver.addfile(prefix + name, mode, islink, data)
1181
1171
1182 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
1172 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
1183
1173
1184 if repo.ui.configbool(b"ui", b"archivemeta"):
1174 if repo.ui.configbool(b"ui", b"archivemeta"):
1185 write(
1175 write(
1186 b'.hg_archival.txt',
1176 b'.hg_archival.txt',
1187 0o644,
1177 0o644,
1188 False,
1178 False,
1189 lambda: archival.buildmetadata(ctx),
1179 lambda: archival.buildmetadata(ctx),
1190 )
1180 )
1191
1181
1192 for f in ctx:
1182 for f in ctx:
1193 ff = ctx.flags(f)
1183 ff = ctx.flags(f)
1194 getdata = ctx[f].data
1184 getdata = ctx[f].data
1195 lfile = lfutil.splitstandin(f)
1185 lfile = lfutil.splitstandin(f)
1196 if lfile is not None:
1186 if lfile is not None:
1197 if node is not None:
1187 if node is not None:
1198 path = lfutil.findfile(repo, getdata().strip())
1188 path = lfutil.findfile(repo, getdata().strip())
1199
1189
1200 if path is None:
1190 if path is None:
1201 raise error.Abort(
1191 raise error.Abort(
1202 _(
1192 _(
1203 b'largefile %s not found in repo store or system cache'
1193 b'largefile %s not found in repo store or system cache'
1204 )
1194 )
1205 % lfile
1195 % lfile
1206 )
1196 )
1207 else:
1197 else:
1208 path = lfile
1198 path = lfile
1209
1199
1210 f = lfile
1200 f = lfile
1211
1201
1212 getdata = lambda: util.readfile(path)
1202 getdata = lambda: util.readfile(path)
1213 write(f, b'x' in ff and 0o755 or 0o644, b'l' in ff, getdata)
1203 write(f, b'x' in ff and 0o755 or 0o644, b'l' in ff, getdata)
1214
1204
1215 if subrepos:
1205 if subrepos:
1216 for subpath in sorted(ctx.substate):
1206 for subpath in sorted(ctx.substate):
1217 sub = ctx.workingsub(subpath)
1207 sub = ctx.workingsub(subpath)
1218 submatch = matchmod.subdirmatcher(subpath, match)
1208 submatch = matchmod.subdirmatcher(subpath, match)
1219 subprefix = prefix + subpath + b'/'
1209 subprefix = prefix + subpath + b'/'
1220 sub._repo.lfstatus = True
1210 sub._repo.lfstatus = True
1221 sub.archive(archiver, subprefix, submatch)
1211 sub.archive(archiver, subprefix, submatch)
1222
1212
1223 archiver.done()
1213 archiver.done()
1224
1214
1225
1215
1226 @eh.wrapfunction(subrepo.hgsubrepo, b'archive')
1216 @eh.wrapfunction(subrepo.hgsubrepo, b'archive')
1227 def hgsubrepoarchive(orig, repo, archiver, prefix, match=None, decode=True):
1217 def hgsubrepoarchive(orig, repo, archiver, prefix, match=None, decode=True):
1228 lfenabled = util.safehasattr(repo._repo, b'_largefilesenabled')
1218 lfenabled = util.safehasattr(repo._repo, b'_largefilesenabled')
1229 if not lfenabled or not repo._repo.lfstatus:
1219 if not lfenabled or not repo._repo.lfstatus:
1230 return orig(repo, archiver, prefix, match, decode)
1220 return orig(repo, archiver, prefix, match, decode)
1231
1221
1232 repo._get(repo._state + (b'hg',))
1222 repo._get(repo._state + (b'hg',))
1233 rev = repo._state[1]
1223 rev = repo._state[1]
1234 ctx = repo._repo[rev]
1224 ctx = repo._repo[rev]
1235
1225
1236 if ctx.node() is not None:
1226 if ctx.node() is not None:
1237 lfcommands.cachelfiles(repo.ui, repo._repo, ctx.node())
1227 lfcommands.cachelfiles(repo.ui, repo._repo, ctx.node())
1238
1228
1239 def write(name, mode, islink, getdata):
1229 def write(name, mode, islink, getdata):
1240 # At this point, the standin has been replaced with the largefile name,
1230 # At this point, the standin has been replaced with the largefile name,
1241 # so the normal matcher works here without the lfutil variants.
1231 # so the normal matcher works here without the lfutil variants.
1242 if match and not match(f):
1232 if match and not match(f):
1243 return
1233 return
1244 data = getdata()
1234 data = getdata()
1245 if decode:
1235 if decode:
1246 data = repo._repo.wwritedata(name, data)
1236 data = repo._repo.wwritedata(name, data)
1247
1237
1248 archiver.addfile(prefix + name, mode, islink, data)
1238 archiver.addfile(prefix + name, mode, islink, data)
1249
1239
1250 for f in ctx:
1240 for f in ctx:
1251 ff = ctx.flags(f)
1241 ff = ctx.flags(f)
1252 getdata = ctx[f].data
1242 getdata = ctx[f].data
1253 lfile = lfutil.splitstandin(f)
1243 lfile = lfutil.splitstandin(f)
1254 if lfile is not None:
1244 if lfile is not None:
1255 if ctx.node() is not None:
1245 if ctx.node() is not None:
1256 path = lfutil.findfile(repo._repo, getdata().strip())
1246 path = lfutil.findfile(repo._repo, getdata().strip())
1257
1247
1258 if path is None:
1248 if path is None:
1259 raise error.Abort(
1249 raise error.Abort(
1260 _(
1250 _(
1261 b'largefile %s not found in repo store or system cache'
1251 b'largefile %s not found in repo store or system cache'
1262 )
1252 )
1263 % lfile
1253 % lfile
1264 )
1254 )
1265 else:
1255 else:
1266 path = lfile
1256 path = lfile
1267
1257
1268 f = lfile
1258 f = lfile
1269
1259
1270 getdata = lambda: util.readfile(os.path.join(prefix, path))
1260 getdata = lambda: util.readfile(os.path.join(prefix, path))
1271
1261
1272 write(f, b'x' in ff and 0o755 or 0o644, b'l' in ff, getdata)
1262 write(f, b'x' in ff and 0o755 or 0o644, b'l' in ff, getdata)
1273
1263
1274 for subpath in sorted(ctx.substate):
1264 for subpath in sorted(ctx.substate):
1275 sub = ctx.workingsub(subpath)
1265 sub = ctx.workingsub(subpath)
1276 submatch = matchmod.subdirmatcher(subpath, match)
1266 submatch = matchmod.subdirmatcher(subpath, match)
1277 subprefix = prefix + subpath + b'/'
1267 subprefix = prefix + subpath + b'/'
1278 sub._repo.lfstatus = True
1268 sub._repo.lfstatus = True
1279 sub.archive(archiver, subprefix, submatch, decode)
1269 sub.archive(archiver, subprefix, submatch, decode)
1280
1270
1281
1271
1282 # If a largefile is modified, the change is not reflected in its
1272 # If a largefile is modified, the change is not reflected in its
1283 # standin until a commit. cmdutil.bailifchanged() raises an exception
1273 # standin until a commit. cmdutil.bailifchanged() raises an exception
1284 # if the repo has uncommitted changes. Wrap it to also check if
1274 # if the repo has uncommitted changes. Wrap it to also check if
1285 # largefiles were changed. This is used by bisect, backout and fetch.
1275 # largefiles were changed. This is used by bisect, backout and fetch.
1286 @eh.wrapfunction(cmdutil, b'bailifchanged')
1276 @eh.wrapfunction(cmdutil, b'bailifchanged')
1287 def overridebailifchanged(orig, repo, *args, **kwargs):
1277 def overridebailifchanged(orig, repo, *args, **kwargs):
1288 orig(repo, *args, **kwargs)
1278 orig(repo, *args, **kwargs)
1289 repo.lfstatus = True
1279 with lfstatus(repo):
1290 s = repo.status()
1280 s = repo.status()
1291 repo.lfstatus = False
1292 if s.modified or s.added or s.removed or s.deleted:
1281 if s.modified or s.added or s.removed or s.deleted:
1293 raise error.Abort(_(b'uncommitted changes'))
1282 raise error.Abort(_(b'uncommitted changes'))
1294
1283
1295
1284
1296 @eh.wrapfunction(cmdutil, b'postcommitstatus')
1285 @eh.wrapfunction(cmdutil, b'postcommitstatus')
1297 def postcommitstatus(orig, repo, *args, **kwargs):
1286 def postcommitstatus(orig, repo, *args, **kwargs):
1298 repo.lfstatus = True
1287 with lfstatus(repo):
1299 try:
1300 return orig(repo, *args, **kwargs)
1288 return orig(repo, *args, **kwargs)
1301 finally:
1302 repo.lfstatus = False
1303
1289
1304
1290
1305 @eh.wrapfunction(cmdutil, b'forget')
1291 @eh.wrapfunction(cmdutil, b'forget')
1306 def cmdutilforget(
1292 def cmdutilforget(
1307 orig, ui, repo, match, prefix, uipathfn, explicitonly, dryrun, interactive
1293 orig, ui, repo, match, prefix, uipathfn, explicitonly, dryrun, interactive
1308 ):
1294 ):
1309 normalmatcher = composenormalfilematcher(match, repo[None].manifest())
1295 normalmatcher = composenormalfilematcher(match, repo[None].manifest())
1310 bad, forgot = orig(
1296 bad, forgot = orig(
1311 ui,
1297 ui,
1312 repo,
1298 repo,
1313 normalmatcher,
1299 normalmatcher,
1314 prefix,
1300 prefix,
1315 uipathfn,
1301 uipathfn,
1316 explicitonly,
1302 explicitonly,
1317 dryrun,
1303 dryrun,
1318 interactive,
1304 interactive,
1319 )
1305 )
1320 m = composelargefilematcher(match, repo[None].manifest())
1306 m = composelargefilematcher(match, repo[None].manifest())
1321
1307
1322 try:
1308 with lfstatus(repo):
1323 repo.lfstatus = True
1324 s = repo.status(match=m, clean=True)
1309 s = repo.status(match=m, clean=True)
1325 finally:
1326 repo.lfstatus = False
1327 manifest = repo[None].manifest()
1310 manifest = repo[None].manifest()
1328 forget = sorted(s.modified + s.added + s.deleted + s.clean)
1311 forget = sorted(s.modified + s.added + s.deleted + s.clean)
1329 forget = [f for f in forget if lfutil.standin(f) in manifest]
1312 forget = [f for f in forget if lfutil.standin(f) in manifest]
1330
1313
1331 for f in forget:
1314 for f in forget:
1332 fstandin = lfutil.standin(f)
1315 fstandin = lfutil.standin(f)
1333 if fstandin not in repo.dirstate and not repo.wvfs.isdir(fstandin):
1316 if fstandin not in repo.dirstate and not repo.wvfs.isdir(fstandin):
1334 ui.warn(
1317 ui.warn(
1335 _(b'not removing %s: file is already untracked\n') % uipathfn(f)
1318 _(b'not removing %s: file is already untracked\n') % uipathfn(f)
1336 )
1319 )
1337 bad.append(f)
1320 bad.append(f)
1338
1321
1339 for f in forget:
1322 for f in forget:
1340 if ui.verbose or not m.exact(f):
1323 if ui.verbose or not m.exact(f):
1341 ui.status(_(b'removing %s\n') % uipathfn(f))
1324 ui.status(_(b'removing %s\n') % uipathfn(f))
1342
1325
1343 # Need to lock because standin files are deleted then removed from the
1326 # Need to lock because standin files are deleted then removed from the
1344 # repository and we could race in-between.
1327 # repository and we could race in-between.
1345 with repo.wlock():
1328 with repo.wlock():
1346 lfdirstate = lfutil.openlfdirstate(ui, repo)
1329 lfdirstate = lfutil.openlfdirstate(ui, repo)
1347 for f in forget:
1330 for f in forget:
1348 if lfdirstate[f] == b'a':
1331 if lfdirstate[f] == b'a':
1349 lfdirstate.drop(f)
1332 lfdirstate.drop(f)
1350 else:
1333 else:
1351 lfdirstate.remove(f)
1334 lfdirstate.remove(f)
1352 lfdirstate.write()
1335 lfdirstate.write()
1353 standins = [lfutil.standin(f) for f in forget]
1336 standins = [lfutil.standin(f) for f in forget]
1354 for f in standins:
1337 for f in standins:
1355 repo.wvfs.unlinkpath(f, ignoremissing=True)
1338 repo.wvfs.unlinkpath(f, ignoremissing=True)
1356 rejected = repo[None].forget(standins)
1339 rejected = repo[None].forget(standins)
1357
1340
1358 bad.extend(f for f in rejected if f in m.files())
1341 bad.extend(f for f in rejected if f in m.files())
1359 forgot.extend(f for f in forget if f not in rejected)
1342 forgot.extend(f for f in forget if f not in rejected)
1360 return bad, forgot
1343 return bad, forgot
1361
1344
1362
1345
1363 def _getoutgoings(repo, other, missing, addfunc):
1346 def _getoutgoings(repo, other, missing, addfunc):
1364 """get pairs of filename and largefile hash in outgoing revisions
1347 """get pairs of filename and largefile hash in outgoing revisions
1365 in 'missing'.
1348 in 'missing'.
1366
1349
1367 largefiles already existing on 'other' repository are ignored.
1350 largefiles already existing on 'other' repository are ignored.
1368
1351
1369 'addfunc' is invoked with each unique pairs of filename and
1352 'addfunc' is invoked with each unique pairs of filename and
1370 largefile hash value.
1353 largefile hash value.
1371 """
1354 """
1372 knowns = set()
1355 knowns = set()
1373 lfhashes = set()
1356 lfhashes = set()
1374
1357
1375 def dedup(fn, lfhash):
1358 def dedup(fn, lfhash):
1376 k = (fn, lfhash)
1359 k = (fn, lfhash)
1377 if k not in knowns:
1360 if k not in knowns:
1378 knowns.add(k)
1361 knowns.add(k)
1379 lfhashes.add(lfhash)
1362 lfhashes.add(lfhash)
1380
1363
1381 lfutil.getlfilestoupload(repo, missing, dedup)
1364 lfutil.getlfilestoupload(repo, missing, dedup)
1382 if lfhashes:
1365 if lfhashes:
1383 lfexists = storefactory.openstore(repo, other).exists(lfhashes)
1366 lfexists = storefactory.openstore(repo, other).exists(lfhashes)
1384 for fn, lfhash in knowns:
1367 for fn, lfhash in knowns:
1385 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1368 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1386 addfunc(fn, lfhash)
1369 addfunc(fn, lfhash)
1387
1370
1388
1371
1389 def outgoinghook(ui, repo, other, opts, missing):
1372 def outgoinghook(ui, repo, other, opts, missing):
1390 if opts.pop(b'large', None):
1373 if opts.pop(b'large', None):
1391 lfhashes = set()
1374 lfhashes = set()
1392 if ui.debugflag:
1375 if ui.debugflag:
1393 toupload = {}
1376 toupload = {}
1394
1377
1395 def addfunc(fn, lfhash):
1378 def addfunc(fn, lfhash):
1396 if fn not in toupload:
1379 if fn not in toupload:
1397 toupload[fn] = []
1380 toupload[fn] = []
1398 toupload[fn].append(lfhash)
1381 toupload[fn].append(lfhash)
1399 lfhashes.add(lfhash)
1382 lfhashes.add(lfhash)
1400
1383
1401 def showhashes(fn):
1384 def showhashes(fn):
1402 for lfhash in sorted(toupload[fn]):
1385 for lfhash in sorted(toupload[fn]):
1403 ui.debug(b' %s\n' % lfhash)
1386 ui.debug(b' %s\n' % lfhash)
1404
1387
1405 else:
1388 else:
1406 toupload = set()
1389 toupload = set()
1407
1390
1408 def addfunc(fn, lfhash):
1391 def addfunc(fn, lfhash):
1409 toupload.add(fn)
1392 toupload.add(fn)
1410 lfhashes.add(lfhash)
1393 lfhashes.add(lfhash)
1411
1394
1412 def showhashes(fn):
1395 def showhashes(fn):
1413 pass
1396 pass
1414
1397
1415 _getoutgoings(repo, other, missing, addfunc)
1398 _getoutgoings(repo, other, missing, addfunc)
1416
1399
1417 if not toupload:
1400 if not toupload:
1418 ui.status(_(b'largefiles: no files to upload\n'))
1401 ui.status(_(b'largefiles: no files to upload\n'))
1419 else:
1402 else:
1420 ui.status(
1403 ui.status(
1421 _(b'largefiles to upload (%d entities):\n') % (len(lfhashes))
1404 _(b'largefiles to upload (%d entities):\n') % (len(lfhashes))
1422 )
1405 )
1423 for file in sorted(toupload):
1406 for file in sorted(toupload):
1424 ui.status(lfutil.splitstandin(file) + b'\n')
1407 ui.status(lfutil.splitstandin(file) + b'\n')
1425 showhashes(file)
1408 showhashes(file)
1426 ui.status(b'\n')
1409 ui.status(b'\n')
1427
1410
1428
1411
1429 @eh.wrapcommand(
1412 @eh.wrapcommand(
1430 b'outgoing', opts=[(b'', b'large', None, _(b'display outgoing largefiles'))]
1413 b'outgoing', opts=[(b'', b'large', None, _(b'display outgoing largefiles'))]
1431 )
1414 )
1432 def _outgoingcmd(orig, *args, **kwargs):
1415 def _outgoingcmd(orig, *args, **kwargs):
1433 # Nothing to do here other than add the extra help option- the hook above
1416 # Nothing to do here other than add the extra help option- the hook above
1434 # processes it.
1417 # processes it.
1435 return orig(*args, **kwargs)
1418 return orig(*args, **kwargs)
1436
1419
1437
1420
1438 def summaryremotehook(ui, repo, opts, changes):
1421 def summaryremotehook(ui, repo, opts, changes):
1439 largeopt = opts.get(b'large', False)
1422 largeopt = opts.get(b'large', False)
1440 if changes is None:
1423 if changes is None:
1441 if largeopt:
1424 if largeopt:
1442 return (False, True) # only outgoing check is needed
1425 return (False, True) # only outgoing check is needed
1443 else:
1426 else:
1444 return (False, False)
1427 return (False, False)
1445 elif largeopt:
1428 elif largeopt:
1446 url, branch, peer, outgoing = changes[1]
1429 url, branch, peer, outgoing = changes[1]
1447 if peer is None:
1430 if peer is None:
1448 # i18n: column positioning for "hg summary"
1431 # i18n: column positioning for "hg summary"
1449 ui.status(_(b'largefiles: (no remote repo)\n'))
1432 ui.status(_(b'largefiles: (no remote repo)\n'))
1450 return
1433 return
1451
1434
1452 toupload = set()
1435 toupload = set()
1453 lfhashes = set()
1436 lfhashes = set()
1454
1437
1455 def addfunc(fn, lfhash):
1438 def addfunc(fn, lfhash):
1456 toupload.add(fn)
1439 toupload.add(fn)
1457 lfhashes.add(lfhash)
1440 lfhashes.add(lfhash)
1458
1441
1459 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1442 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1460
1443
1461 if not toupload:
1444 if not toupload:
1462 # i18n: column positioning for "hg summary"
1445 # i18n: column positioning for "hg summary"
1463 ui.status(_(b'largefiles: (no files to upload)\n'))
1446 ui.status(_(b'largefiles: (no files to upload)\n'))
1464 else:
1447 else:
1465 # i18n: column positioning for "hg summary"
1448 # i18n: column positioning for "hg summary"
1466 ui.status(
1449 ui.status(
1467 _(b'largefiles: %d entities for %d files to upload\n')
1450 _(b'largefiles: %d entities for %d files to upload\n')
1468 % (len(lfhashes), len(toupload))
1451 % (len(lfhashes), len(toupload))
1469 )
1452 )
1470
1453
1471
1454
1472 @eh.wrapcommand(
1455 @eh.wrapcommand(
1473 b'summary', opts=[(b'', b'large', None, _(b'display outgoing largefiles'))]
1456 b'summary', opts=[(b'', b'large', None, _(b'display outgoing largefiles'))]
1474 )
1457 )
1475 def overridesummary(orig, ui, repo, *pats, **opts):
1458 def overridesummary(orig, ui, repo, *pats, **opts):
1476 try:
1459 with lfstatus(repo):
1477 repo.lfstatus = True
1478 orig(ui, repo, *pats, **opts)
1460 orig(ui, repo, *pats, **opts)
1479 finally:
1480 repo.lfstatus = False
1481
1461
1482
1462
1483 @eh.wrapfunction(scmutil, b'addremove')
1463 @eh.wrapfunction(scmutil, b'addremove')
1484 def scmutiladdremove(orig, repo, matcher, prefix, uipathfn, opts=None):
1464 def scmutiladdremove(orig, repo, matcher, prefix, uipathfn, opts=None):
1485 if opts is None:
1465 if opts is None:
1486 opts = {}
1466 opts = {}
1487 if not lfutil.islfilesrepo(repo):
1467 if not lfutil.islfilesrepo(repo):
1488 return orig(repo, matcher, prefix, uipathfn, opts)
1468 return orig(repo, matcher, prefix, uipathfn, opts)
1489 # Get the list of missing largefiles so we can remove them
1469 # Get the list of missing largefiles so we can remove them
1490 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1470 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1491 unsure, s = lfdirstate.status(
1471 unsure, s = lfdirstate.status(
1492 matchmod.always(),
1472 matchmod.always(),
1493 subrepos=[],
1473 subrepos=[],
1494 ignored=False,
1474 ignored=False,
1495 clean=False,
1475 clean=False,
1496 unknown=False,
1476 unknown=False,
1497 )
1477 )
1498
1478
1499 # Call into the normal remove code, but the removing of the standin, we want
1479 # Call into the normal remove code, but the removing of the standin, we want
1500 # to have handled by original addremove. Monkey patching here makes sure
1480 # to have handled by original addremove. Monkey patching here makes sure
1501 # we don't remove the standin in the largefiles code, preventing a very
1481 # we don't remove the standin in the largefiles code, preventing a very
1502 # confused state later.
1482 # confused state later.
1503 if s.deleted:
1483 if s.deleted:
1504 m = copy.copy(matcher)
1484 m = copy.copy(matcher)
1505
1485
1506 # The m._files and m._map attributes are not changed to the deleted list
1486 # The m._files and m._map attributes are not changed to the deleted list
1507 # because that affects the m.exact() test, which in turn governs whether
1487 # because that affects the m.exact() test, which in turn governs whether
1508 # or not the file name is printed, and how. Simply limit the original
1488 # or not the file name is printed, and how. Simply limit the original
1509 # matches to those in the deleted status list.
1489 # matches to those in the deleted status list.
1510 matchfn = m.matchfn
1490 matchfn = m.matchfn
1511 m.matchfn = lambda f: f in s.deleted and matchfn(f)
1491 m.matchfn = lambda f: f in s.deleted and matchfn(f)
1512
1492
1513 removelargefiles(
1493 removelargefiles(
1514 repo.ui,
1494 repo.ui,
1515 repo,
1495 repo,
1516 True,
1496 True,
1517 m,
1497 m,
1518 uipathfn,
1498 uipathfn,
1519 opts.get(b'dry_run'),
1499 opts.get(b'dry_run'),
1520 **pycompat.strkwargs(opts)
1500 **pycompat.strkwargs(opts)
1521 )
1501 )
1522 # Call into the normal add code, and any files that *should* be added as
1502 # Call into the normal add code, and any files that *should* be added as
1523 # largefiles will be
1503 # largefiles will be
1524 added, bad = addlargefiles(
1504 added, bad = addlargefiles(
1525 repo.ui, repo, True, matcher, uipathfn, **pycompat.strkwargs(opts)
1505 repo.ui, repo, True, matcher, uipathfn, **pycompat.strkwargs(opts)
1526 )
1506 )
1527 # Now that we've handled largefiles, hand off to the original addremove
1507 # Now that we've handled largefiles, hand off to the original addremove
1528 # function to take care of the rest. Make sure it doesn't do anything with
1508 # function to take care of the rest. Make sure it doesn't do anything with
1529 # largefiles by passing a matcher that will ignore them.
1509 # largefiles by passing a matcher that will ignore them.
1530 matcher = composenormalfilematcher(matcher, repo[None].manifest(), added)
1510 matcher = composenormalfilematcher(matcher, repo[None].manifest(), added)
1531 return orig(repo, matcher, prefix, uipathfn, opts)
1511 return orig(repo, matcher, prefix, uipathfn, opts)
1532
1512
1533
1513
1534 # Calling purge with --all will cause the largefiles to be deleted.
1514 # Calling purge with --all will cause the largefiles to be deleted.
1535 # Override repo.status to prevent this from happening.
1515 # Override repo.status to prevent this from happening.
1536 @eh.wrapcommand(b'purge', extension=b'purge')
1516 @eh.wrapcommand(b'purge', extension=b'purge')
1537 def overridepurge(orig, ui, repo, *dirs, **opts):
1517 def overridepurge(orig, ui, repo, *dirs, **opts):
1538 # XXX Monkey patching a repoview will not work. The assigned attribute will
1518 # XXX Monkey patching a repoview will not work. The assigned attribute will
1539 # be set on the unfiltered repo, but we will only lookup attributes in the
1519 # be set on the unfiltered repo, but we will only lookup attributes in the
1540 # unfiltered repo if the lookup in the repoview object itself fails. As the
1520 # unfiltered repo if the lookup in the repoview object itself fails. As the
1541 # monkey patched method exists on the repoview class the lookup will not
1521 # monkey patched method exists on the repoview class the lookup will not
1542 # fail. As a result, the original version will shadow the monkey patched
1522 # fail. As a result, the original version will shadow the monkey patched
1543 # one, defeating the monkey patch.
1523 # one, defeating the monkey patch.
1544 #
1524 #
1545 # As a work around we use an unfiltered repo here. We should do something
1525 # As a work around we use an unfiltered repo here. We should do something
1546 # cleaner instead.
1526 # cleaner instead.
1547 repo = repo.unfiltered()
1527 repo = repo.unfiltered()
1548 oldstatus = repo.status
1528 oldstatus = repo.status
1549
1529
1550 def overridestatus(
1530 def overridestatus(
1551 node1=b'.',
1531 node1=b'.',
1552 node2=None,
1532 node2=None,
1553 match=None,
1533 match=None,
1554 ignored=False,
1534 ignored=False,
1555 clean=False,
1535 clean=False,
1556 unknown=False,
1536 unknown=False,
1557 listsubrepos=False,
1537 listsubrepos=False,
1558 ):
1538 ):
1559 r = oldstatus(
1539 r = oldstatus(
1560 node1, node2, match, ignored, clean, unknown, listsubrepos
1540 node1, node2, match, ignored, clean, unknown, listsubrepos
1561 )
1541 )
1562 lfdirstate = lfutil.openlfdirstate(ui, repo)
1542 lfdirstate = lfutil.openlfdirstate(ui, repo)
1563 unknown = [f for f in r.unknown if lfdirstate[f] == b'?']
1543 unknown = [f for f in r.unknown if lfdirstate[f] == b'?']
1564 ignored = [f for f in r.ignored if lfdirstate[f] == b'?']
1544 ignored = [f for f in r.ignored if lfdirstate[f] == b'?']
1565 return scmutil.status(
1545 return scmutil.status(
1566 r.modified, r.added, r.removed, r.deleted, unknown, ignored, r.clean
1546 r.modified, r.added, r.removed, r.deleted, unknown, ignored, r.clean
1567 )
1547 )
1568
1548
1569 repo.status = overridestatus
1549 repo.status = overridestatus
1570 orig(ui, repo, *dirs, **opts)
1550 orig(ui, repo, *dirs, **opts)
1571 repo.status = oldstatus
1551 repo.status = oldstatus
1572
1552
1573
1553
1574 @eh.wrapcommand(b'rollback')
1554 @eh.wrapcommand(b'rollback')
1575 def overriderollback(orig, ui, repo, **opts):
1555 def overriderollback(orig, ui, repo, **opts):
1576 with repo.wlock():
1556 with repo.wlock():
1577 before = repo.dirstate.parents()
1557 before = repo.dirstate.parents()
1578 orphans = set(
1558 orphans = set(
1579 f
1559 f
1580 for f in repo.dirstate
1560 for f in repo.dirstate
1581 if lfutil.isstandin(f) and repo.dirstate[f] != b'r'
1561 if lfutil.isstandin(f) and repo.dirstate[f] != b'r'
1582 )
1562 )
1583 result = orig(ui, repo, **opts)
1563 result = orig(ui, repo, **opts)
1584 after = repo.dirstate.parents()
1564 after = repo.dirstate.parents()
1585 if before == after:
1565 if before == after:
1586 return result # no need to restore standins
1566 return result # no need to restore standins
1587
1567
1588 pctx = repo[b'.']
1568 pctx = repo[b'.']
1589 for f in repo.dirstate:
1569 for f in repo.dirstate:
1590 if lfutil.isstandin(f):
1570 if lfutil.isstandin(f):
1591 orphans.discard(f)
1571 orphans.discard(f)
1592 if repo.dirstate[f] == b'r':
1572 if repo.dirstate[f] == b'r':
1593 repo.wvfs.unlinkpath(f, ignoremissing=True)
1573 repo.wvfs.unlinkpath(f, ignoremissing=True)
1594 elif f in pctx:
1574 elif f in pctx:
1595 fctx = pctx[f]
1575 fctx = pctx[f]
1596 repo.wwrite(f, fctx.data(), fctx.flags())
1576 repo.wwrite(f, fctx.data(), fctx.flags())
1597 else:
1577 else:
1598 # content of standin is not so important in 'a',
1578 # content of standin is not so important in 'a',
1599 # 'm' or 'n' (coming from the 2nd parent) cases
1579 # 'm' or 'n' (coming from the 2nd parent) cases
1600 lfutil.writestandin(repo, f, b'', False)
1580 lfutil.writestandin(repo, f, b'', False)
1601 for standin in orphans:
1581 for standin in orphans:
1602 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1582 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1603
1583
1604 lfdirstate = lfutil.openlfdirstate(ui, repo)
1584 lfdirstate = lfutil.openlfdirstate(ui, repo)
1605 orphans = set(lfdirstate)
1585 orphans = set(lfdirstate)
1606 lfiles = lfutil.listlfiles(repo)
1586 lfiles = lfutil.listlfiles(repo)
1607 for file in lfiles:
1587 for file in lfiles:
1608 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1588 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1609 orphans.discard(file)
1589 orphans.discard(file)
1610 for lfile in orphans:
1590 for lfile in orphans:
1611 lfdirstate.drop(lfile)
1591 lfdirstate.drop(lfile)
1612 lfdirstate.write()
1592 lfdirstate.write()
1613 return result
1593 return result
1614
1594
1615
1595
1616 @eh.wrapcommand(b'transplant', extension=b'transplant')
1596 @eh.wrapcommand(b'transplant', extension=b'transplant')
1617 def overridetransplant(orig, ui, repo, *revs, **opts):
1597 def overridetransplant(orig, ui, repo, *revs, **opts):
1618 resuming = opts.get(r'continue')
1598 resuming = opts.get(r'continue')
1619 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1599 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1620 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1600 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1621 try:
1601 try:
1622 result = orig(ui, repo, *revs, **opts)
1602 result = orig(ui, repo, *revs, **opts)
1623 finally:
1603 finally:
1624 repo._lfstatuswriters.pop()
1604 repo._lfstatuswriters.pop()
1625 repo._lfcommithooks.pop()
1605 repo._lfcommithooks.pop()
1626 return result
1606 return result
1627
1607
1628
1608
1629 @eh.wrapcommand(b'cat')
1609 @eh.wrapcommand(b'cat')
1630 def overridecat(orig, ui, repo, file1, *pats, **opts):
1610 def overridecat(orig, ui, repo, file1, *pats, **opts):
1631 opts = pycompat.byteskwargs(opts)
1611 opts = pycompat.byteskwargs(opts)
1632 ctx = scmutil.revsingle(repo, opts.get(b'rev'))
1612 ctx = scmutil.revsingle(repo, opts.get(b'rev'))
1633 err = 1
1613 err = 1
1634 notbad = set()
1614 notbad = set()
1635 m = scmutil.match(ctx, (file1,) + pats, opts)
1615 m = scmutil.match(ctx, (file1,) + pats, opts)
1636 origmatchfn = m.matchfn
1616 origmatchfn = m.matchfn
1637
1617
1638 def lfmatchfn(f):
1618 def lfmatchfn(f):
1639 if origmatchfn(f):
1619 if origmatchfn(f):
1640 return True
1620 return True
1641 lf = lfutil.splitstandin(f)
1621 lf = lfutil.splitstandin(f)
1642 if lf is None:
1622 if lf is None:
1643 return False
1623 return False
1644 notbad.add(lf)
1624 notbad.add(lf)
1645 return origmatchfn(lf)
1625 return origmatchfn(lf)
1646
1626
1647 m.matchfn = lfmatchfn
1627 m.matchfn = lfmatchfn
1648 origbadfn = m.bad
1628 origbadfn = m.bad
1649
1629
1650 def lfbadfn(f, msg):
1630 def lfbadfn(f, msg):
1651 if not f in notbad:
1631 if not f in notbad:
1652 origbadfn(f, msg)
1632 origbadfn(f, msg)
1653
1633
1654 m.bad = lfbadfn
1634 m.bad = lfbadfn
1655
1635
1656 origvisitdirfn = m.visitdir
1636 origvisitdirfn = m.visitdir
1657
1637
1658 def lfvisitdirfn(dir):
1638 def lfvisitdirfn(dir):
1659 if dir == lfutil.shortname:
1639 if dir == lfutil.shortname:
1660 return True
1640 return True
1661 ret = origvisitdirfn(dir)
1641 ret = origvisitdirfn(dir)
1662 if ret:
1642 if ret:
1663 return ret
1643 return ret
1664 lf = lfutil.splitstandin(dir)
1644 lf = lfutil.splitstandin(dir)
1665 if lf is None:
1645 if lf is None:
1666 return False
1646 return False
1667 return origvisitdirfn(lf)
1647 return origvisitdirfn(lf)
1668
1648
1669 m.visitdir = lfvisitdirfn
1649 m.visitdir = lfvisitdirfn
1670
1650
1671 for f in ctx.walk(m):
1651 for f in ctx.walk(m):
1672 with cmdutil.makefileobj(ctx, opts.get(b'output'), pathname=f) as fp:
1652 with cmdutil.makefileobj(ctx, opts.get(b'output'), pathname=f) as fp:
1673 lf = lfutil.splitstandin(f)
1653 lf = lfutil.splitstandin(f)
1674 if lf is None or origmatchfn(f):
1654 if lf is None or origmatchfn(f):
1675 # duplicating unreachable code from commands.cat
1655 # duplicating unreachable code from commands.cat
1676 data = ctx[f].data()
1656 data = ctx[f].data()
1677 if opts.get(b'decode'):
1657 if opts.get(b'decode'):
1678 data = repo.wwritedata(f, data)
1658 data = repo.wwritedata(f, data)
1679 fp.write(data)
1659 fp.write(data)
1680 else:
1660 else:
1681 hash = lfutil.readasstandin(ctx[f])
1661 hash = lfutil.readasstandin(ctx[f])
1682 if not lfutil.inusercache(repo.ui, hash):
1662 if not lfutil.inusercache(repo.ui, hash):
1683 store = storefactory.openstore(repo)
1663 store = storefactory.openstore(repo)
1684 success, missing = store.get([(lf, hash)])
1664 success, missing = store.get([(lf, hash)])
1685 if len(success) != 1:
1665 if len(success) != 1:
1686 raise error.Abort(
1666 raise error.Abort(
1687 _(
1667 _(
1688 b'largefile %s is not in cache and could not be '
1668 b'largefile %s is not in cache and could not be '
1689 b'downloaded'
1669 b'downloaded'
1690 )
1670 )
1691 % lf
1671 % lf
1692 )
1672 )
1693 path = lfutil.usercachepath(repo.ui, hash)
1673 path = lfutil.usercachepath(repo.ui, hash)
1694 with open(path, b"rb") as fpin:
1674 with open(path, b"rb") as fpin:
1695 for chunk in util.filechunkiter(fpin):
1675 for chunk in util.filechunkiter(fpin):
1696 fp.write(chunk)
1676 fp.write(chunk)
1697 err = 0
1677 err = 0
1698 return err
1678 return err
1699
1679
1700
1680
1701 @eh.wrapfunction(merge, b'update')
1681 @eh.wrapfunction(merge, b'update')
1702 def mergeupdate(orig, repo, node, branchmerge, force, *args, **kwargs):
1682 def mergeupdate(orig, repo, node, branchmerge, force, *args, **kwargs):
1703 matcher = kwargs.get(r'matcher', None)
1683 matcher = kwargs.get(r'matcher', None)
1704 # note if this is a partial update
1684 # note if this is a partial update
1705 partial = matcher and not matcher.always()
1685 partial = matcher and not matcher.always()
1706 with repo.wlock():
1686 with repo.wlock():
1707 # branch | | |
1687 # branch | | |
1708 # merge | force | partial | action
1688 # merge | force | partial | action
1709 # -------+-------+---------+--------------
1689 # -------+-------+---------+--------------
1710 # x | x | x | linear-merge
1690 # x | x | x | linear-merge
1711 # o | x | x | branch-merge
1691 # o | x | x | branch-merge
1712 # x | o | x | overwrite (as clean update)
1692 # x | o | x | overwrite (as clean update)
1713 # o | o | x | force-branch-merge (*1)
1693 # o | o | x | force-branch-merge (*1)
1714 # x | x | o | (*)
1694 # x | x | o | (*)
1715 # o | x | o | (*)
1695 # o | x | o | (*)
1716 # x | o | o | overwrite (as revert)
1696 # x | o | o | overwrite (as revert)
1717 # o | o | o | (*)
1697 # o | o | o | (*)
1718 #
1698 #
1719 # (*) don't care
1699 # (*) don't care
1720 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1700 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1721
1701
1722 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1702 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1723 unsure, s = lfdirstate.status(
1703 unsure, s = lfdirstate.status(
1724 matchmod.always(),
1704 matchmod.always(),
1725 subrepos=[],
1705 subrepos=[],
1726 ignored=False,
1706 ignored=False,
1727 clean=True,
1707 clean=True,
1728 unknown=False,
1708 unknown=False,
1729 )
1709 )
1730 oldclean = set(s.clean)
1710 oldclean = set(s.clean)
1731 pctx = repo[b'.']
1711 pctx = repo[b'.']
1732 dctx = repo[node]
1712 dctx = repo[node]
1733 for lfile in unsure + s.modified:
1713 for lfile in unsure + s.modified:
1734 lfileabs = repo.wvfs.join(lfile)
1714 lfileabs = repo.wvfs.join(lfile)
1735 if not repo.wvfs.exists(lfileabs):
1715 if not repo.wvfs.exists(lfileabs):
1736 continue
1716 continue
1737 lfhash = lfutil.hashfile(lfileabs)
1717 lfhash = lfutil.hashfile(lfileabs)
1738 standin = lfutil.standin(lfile)
1718 standin = lfutil.standin(lfile)
1739 lfutil.writestandin(
1719 lfutil.writestandin(
1740 repo, standin, lfhash, lfutil.getexecutable(lfileabs)
1720 repo, standin, lfhash, lfutil.getexecutable(lfileabs)
1741 )
1721 )
1742 if standin in pctx and lfhash == lfutil.readasstandin(
1722 if standin in pctx and lfhash == lfutil.readasstandin(
1743 pctx[standin]
1723 pctx[standin]
1744 ):
1724 ):
1745 oldclean.add(lfile)
1725 oldclean.add(lfile)
1746 for lfile in s.added:
1726 for lfile in s.added:
1747 fstandin = lfutil.standin(lfile)
1727 fstandin = lfutil.standin(lfile)
1748 if fstandin not in dctx:
1728 if fstandin not in dctx:
1749 # in this case, content of standin file is meaningless
1729 # in this case, content of standin file is meaningless
1750 # (in dctx, lfile is unknown, or normal file)
1730 # (in dctx, lfile is unknown, or normal file)
1751 continue
1731 continue
1752 lfutil.updatestandin(repo, lfile, fstandin)
1732 lfutil.updatestandin(repo, lfile, fstandin)
1753 # mark all clean largefiles as dirty, just in case the update gets
1733 # mark all clean largefiles as dirty, just in case the update gets
1754 # interrupted before largefiles and lfdirstate are synchronized
1734 # interrupted before largefiles and lfdirstate are synchronized
1755 for lfile in oldclean:
1735 for lfile in oldclean:
1756 lfdirstate.normallookup(lfile)
1736 lfdirstate.normallookup(lfile)
1757 lfdirstate.write()
1737 lfdirstate.write()
1758
1738
1759 oldstandins = lfutil.getstandinsstate(repo)
1739 oldstandins = lfutil.getstandinsstate(repo)
1760 # Make sure the merge runs on disk, not in-memory. largefiles is not a
1740 # Make sure the merge runs on disk, not in-memory. largefiles is not a
1761 # good candidate for in-memory merge (large files, custom dirstate,
1741 # good candidate for in-memory merge (large files, custom dirstate,
1762 # matcher usage).
1742 # matcher usage).
1763 kwargs[r'wc'] = repo[None]
1743 kwargs[r'wc'] = repo[None]
1764 result = orig(repo, node, branchmerge, force, *args, **kwargs)
1744 result = orig(repo, node, branchmerge, force, *args, **kwargs)
1765
1745
1766 newstandins = lfutil.getstandinsstate(repo)
1746 newstandins = lfutil.getstandinsstate(repo)
1767 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1747 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1768
1748
1769 # to avoid leaving all largefiles as dirty and thus rehash them, mark
1749 # to avoid leaving all largefiles as dirty and thus rehash them, mark
1770 # all the ones that didn't change as clean
1750 # all the ones that didn't change as clean
1771 for lfile in oldclean.difference(filelist):
1751 for lfile in oldclean.difference(filelist):
1772 lfdirstate.normal(lfile)
1752 lfdirstate.normal(lfile)
1773 lfdirstate.write()
1753 lfdirstate.write()
1774
1754
1775 if branchmerge or force or partial:
1755 if branchmerge or force or partial:
1776 filelist.extend(s.deleted + s.removed)
1756 filelist.extend(s.deleted + s.removed)
1777
1757
1778 lfcommands.updatelfiles(
1758 lfcommands.updatelfiles(
1779 repo.ui, repo, filelist=filelist, normallookup=partial
1759 repo.ui, repo, filelist=filelist, normallookup=partial
1780 )
1760 )
1781
1761
1782 return result
1762 return result
1783
1763
1784
1764
1785 @eh.wrapfunction(scmutil, b'marktouched')
1765 @eh.wrapfunction(scmutil, b'marktouched')
1786 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1766 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1787 result = orig(repo, files, *args, **kwargs)
1767 result = orig(repo, files, *args, **kwargs)
1788
1768
1789 filelist = []
1769 filelist = []
1790 for f in files:
1770 for f in files:
1791 lf = lfutil.splitstandin(f)
1771 lf = lfutil.splitstandin(f)
1792 if lf is not None:
1772 if lf is not None:
1793 filelist.append(lf)
1773 filelist.append(lf)
1794 if filelist:
1774 if filelist:
1795 lfcommands.updatelfiles(
1775 lfcommands.updatelfiles(
1796 repo.ui,
1776 repo.ui,
1797 repo,
1777 repo,
1798 filelist=filelist,
1778 filelist=filelist,
1799 printmessage=False,
1779 printmessage=False,
1800 normallookup=True,
1780 normallookup=True,
1801 )
1781 )
1802
1782
1803 return result
1783 return result
1804
1784
1805
1785
1806 @eh.wrapfunction(upgrade, b'preservedrequirements')
1786 @eh.wrapfunction(upgrade, b'preservedrequirements')
1807 @eh.wrapfunction(upgrade, b'supporteddestrequirements')
1787 @eh.wrapfunction(upgrade, b'supporteddestrequirements')
1808 def upgraderequirements(orig, repo):
1788 def upgraderequirements(orig, repo):
1809 reqs = orig(repo)
1789 reqs = orig(repo)
1810 if b'largefiles' in repo.requirements:
1790 if b'largefiles' in repo.requirements:
1811 reqs.add(b'largefiles')
1791 reqs.add(b'largefiles')
1812 return reqs
1792 return reqs
1813
1793
1814
1794
1815 _lfscheme = b'largefile://'
1795 _lfscheme = b'largefile://'
1816
1796
1817
1797
1818 @eh.wrapfunction(urlmod, b'open')
1798 @eh.wrapfunction(urlmod, b'open')
1819 def openlargefile(orig, ui, url_, data=None):
1799 def openlargefile(orig, ui, url_, data=None):
1820 if url_.startswith(_lfscheme):
1800 if url_.startswith(_lfscheme):
1821 if data:
1801 if data:
1822 msg = b"cannot use data on a 'largefile://' url"
1802 msg = b"cannot use data on a 'largefile://' url"
1823 raise error.ProgrammingError(msg)
1803 raise error.ProgrammingError(msg)
1824 lfid = url_[len(_lfscheme) :]
1804 lfid = url_[len(_lfscheme) :]
1825 return storefactory.getlfile(ui, lfid)
1805 return storefactory.getlfile(ui, lfid)
1826 else:
1806 else:
1827 return orig(ui, url_, data=data)
1807 return orig(ui, url_, data=data)
General Comments 0
You need to be logged in to leave comments. Login now