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