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