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