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