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