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