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