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