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