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