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