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