##// END OF EJS Templates
largefiles: make prompt order deterministic...
Mads Kiilerich -
r27904:ee3123e1 default
parent child Browse files
Show More
@@ -1,1398 +1,1398 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 sorted(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 with repo.wlock():
720 with repo.wlock():
721 lfdirstate = lfutil.openlfdirstate(ui, repo)
721 lfdirstate = lfutil.openlfdirstate(ui, repo)
722 s = lfutil.lfdirstatestatus(lfdirstate, repo)
722 s = lfutil.lfdirstatestatus(lfdirstate, repo)
723 lfdirstate.write()
723 lfdirstate.write()
724 for lfile in s.modified:
724 for lfile in s.modified:
725 lfutil.updatestandin(repo, lfutil.standin(lfile))
725 lfutil.updatestandin(repo, lfutil.standin(lfile))
726 for lfile in s.deleted:
726 for lfile in s.deleted:
727 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
727 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
728 os.unlink(repo.wjoin(lfutil.standin(lfile)))
728 os.unlink(repo.wjoin(lfutil.standin(lfile)))
729
729
730 oldstandins = lfutil.getstandinsstate(repo)
730 oldstandins = lfutil.getstandinsstate(repo)
731
731
732 def overridematch(mctx, pats=(), opts=None, globbed=False,
732 def overridematch(mctx, pats=(), opts=None, globbed=False,
733 default='relpath', badfn=None):
733 default='relpath', badfn=None):
734 if opts is None:
734 if opts is None:
735 opts = {}
735 opts = {}
736 match = oldmatch(mctx, pats, opts, globbed, default, badfn=badfn)
736 match = oldmatch(mctx, pats, opts, globbed, default, badfn=badfn)
737 m = copy.copy(match)
737 m = copy.copy(match)
738
738
739 # revert supports recursing into subrepos, and though largefiles
739 # revert supports recursing into subrepos, and though largefiles
740 # currently doesn't work correctly in that case, this match is
740 # currently doesn't work correctly in that case, this match is
741 # 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
742 # this invocation of match.
742 # this invocation of match.
743 lfdirstate = lfutil.openlfdirstate(mctx.repo().ui, mctx.repo(),
743 lfdirstate = lfutil.openlfdirstate(mctx.repo().ui, mctx.repo(),
744 False)
744 False)
745
745
746 def tostandin(f):
746 def tostandin(f):
747 standin = lfutil.standin(f)
747 standin = lfutil.standin(f)
748 if standin in ctx or standin in mctx:
748 if standin in ctx or standin in mctx:
749 return standin
749 return standin
750 elif standin in repo[None] or lfdirstate[f] == 'r':
750 elif standin in repo[None] or lfdirstate[f] == 'r':
751 return None
751 return None
752 return f
752 return f
753 m._files = [tostandin(f) for f in m._files]
753 m._files = [tostandin(f) for f in m._files]
754 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]
755 m._fileroots = set(m._files)
755 m._fileroots = set(m._files)
756 origmatchfn = m.matchfn
756 origmatchfn = m.matchfn
757 def matchfn(f):
757 def matchfn(f):
758 if lfutil.isstandin(f):
758 if lfutil.isstandin(f):
759 return (origmatchfn(lfutil.splitstandin(f)) and
759 return (origmatchfn(lfutil.splitstandin(f)) and
760 (f in ctx or f in mctx))
760 (f in ctx or f in mctx))
761 return origmatchfn(f)
761 return origmatchfn(f)
762 m.matchfn = matchfn
762 m.matchfn = matchfn
763 return m
763 return m
764 oldmatch = installmatchfn(overridematch)
764 oldmatch = installmatchfn(overridematch)
765 try:
765 try:
766 orig(ui, repo, ctx, parents, *pats, **opts)
766 orig(ui, repo, ctx, parents, *pats, **opts)
767 finally:
767 finally:
768 restorematchfn()
768 restorematchfn()
769
769
770 newstandins = lfutil.getstandinsstate(repo)
770 newstandins = lfutil.getstandinsstate(repo)
771 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
771 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
772 # lfdirstate should be 'normallookup'-ed for updated files,
772 # lfdirstate should be 'normallookup'-ed for updated files,
773 # because reverting doesn't touch dirstate for 'normal' files
773 # because reverting doesn't touch dirstate for 'normal' files
774 # when target revision is explicitly specified: in such case,
774 # when target revision is explicitly specified: in such case,
775 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
775 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
776 # of target (standin) file.
776 # of target (standin) file.
777 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
777 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
778 normallookup=True)
778 normallookup=True)
779
779
780 # 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
781 # largefiles updated remotely
781 # largefiles updated remotely
782 def overridepull(orig, ui, repo, source=None, **opts):
782 def overridepull(orig, ui, repo, source=None, **opts):
783 revsprepull = len(repo)
783 revsprepull = len(repo)
784 if not source:
784 if not source:
785 source = 'default'
785 source = 'default'
786 repo.lfpullsource = source
786 repo.lfpullsource = source
787 result = orig(ui, repo, source, **opts)
787 result = orig(ui, repo, source, **opts)
788 revspostpull = len(repo)
788 revspostpull = len(repo)
789 lfrevs = opts.get('lfrev', [])
789 lfrevs = opts.get('lfrev', [])
790 if opts.get('all_largefiles'):
790 if opts.get('all_largefiles'):
791 lfrevs.append('pulled()')
791 lfrevs.append('pulled()')
792 if lfrevs and revspostpull > revsprepull:
792 if lfrevs and revspostpull > revsprepull:
793 numcached = 0
793 numcached = 0
794 repo.firstpulled = revsprepull # for pulled() revset expression
794 repo.firstpulled = revsprepull # for pulled() revset expression
795 try:
795 try:
796 for rev in scmutil.revrange(repo, lfrevs):
796 for rev in scmutil.revrange(repo, lfrevs):
797 ui.note(_('pulling largefiles for revision %s\n') % rev)
797 ui.note(_('pulling largefiles for revision %s\n') % rev)
798 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
798 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
799 numcached += len(cached)
799 numcached += len(cached)
800 finally:
800 finally:
801 del repo.firstpulled
801 del repo.firstpulled
802 ui.status(_("%d largefiles cached\n") % numcached)
802 ui.status(_("%d largefiles cached\n") % numcached)
803 return result
803 return result
804
804
805 revsetpredicate = revset.extpredicate()
805 revsetpredicate = revset.extpredicate()
806
806
807 @revsetpredicate('pulled()')
807 @revsetpredicate('pulled()')
808 def pulledrevsetsymbol(repo, subset, x):
808 def pulledrevsetsymbol(repo, subset, x):
809 """Changesets that just has been pulled.
809 """Changesets that just has been pulled.
810
810
811 Only available with largefiles from pull --lfrev expressions.
811 Only available with largefiles from pull --lfrev expressions.
812
812
813 .. container:: verbose
813 .. container:: verbose
814
814
815 Some examples:
815 Some examples:
816
816
817 - pull largefiles for all new changesets::
817 - pull largefiles for all new changesets::
818
818
819 hg pull -lfrev "pulled()"
819 hg pull -lfrev "pulled()"
820
820
821 - pull largefiles for all new branch heads::
821 - pull largefiles for all new branch heads::
822
822
823 hg pull -lfrev "head(pulled()) and not closed()"
823 hg pull -lfrev "head(pulled()) and not closed()"
824
824
825 """
825 """
826
826
827 try:
827 try:
828 firstpulled = repo.firstpulled
828 firstpulled = repo.firstpulled
829 except AttributeError:
829 except AttributeError:
830 raise error.Abort(_("pulled() only available in --lfrev"))
830 raise error.Abort(_("pulled() only available in --lfrev"))
831 return revset.baseset([r for r in subset if r >= firstpulled])
831 return revset.baseset([r for r in subset if r >= firstpulled])
832
832
833 def overrideclone(orig, ui, source, dest=None, **opts):
833 def overrideclone(orig, ui, source, dest=None, **opts):
834 d = dest
834 d = dest
835 if d is None:
835 if d is None:
836 d = hg.defaultdest(source)
836 d = hg.defaultdest(source)
837 if opts.get('all_largefiles') and not hg.islocal(d):
837 if opts.get('all_largefiles') and not hg.islocal(d):
838 raise error.Abort(_(
838 raise error.Abort(_(
839 '--all-largefiles is incompatible with non-local destination %s') %
839 '--all-largefiles is incompatible with non-local destination %s') %
840 d)
840 d)
841
841
842 return orig(ui, source, dest, **opts)
842 return orig(ui, source, dest, **opts)
843
843
844 def hgclone(orig, ui, opts, *args, **kwargs):
844 def hgclone(orig, ui, opts, *args, **kwargs):
845 result = orig(ui, opts, *args, **kwargs)
845 result = orig(ui, opts, *args, **kwargs)
846
846
847 if result is not None:
847 if result is not None:
848 sourcerepo, destrepo = result
848 sourcerepo, destrepo = result
849 repo = destrepo.local()
849 repo = destrepo.local()
850
850
851 # 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
852 # 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
853 # hgrc can't be updated.
853 # hgrc can't be updated.
854 if not repo:
854 if not repo:
855 return result
855 return result
856
856
857 # If largefiles is required for this repo, permanently enable it locally
857 # If largefiles is required for this repo, permanently enable it locally
858 if 'largefiles' in repo.requirements:
858 if 'largefiles' in repo.requirements:
859 fp = repo.vfs('hgrc', 'a', text=True)
859 fp = repo.vfs('hgrc', 'a', text=True)
860 try:
860 try:
861 fp.write('\n[extensions]\nlargefiles=\n')
861 fp.write('\n[extensions]\nlargefiles=\n')
862 finally:
862 finally:
863 fp.close()
863 fp.close()
864
864
865 # 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
866 # 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
867 # 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.
868 if opts.get('all_largefiles'):
868 if opts.get('all_largefiles'):
869 success, missing = lfcommands.downloadlfiles(ui, repo, None)
869 success, missing = lfcommands.downloadlfiles(ui, repo, None)
870
870
871 if missing != 0:
871 if missing != 0:
872 return None
872 return None
873
873
874 return result
874 return result
875
875
876 def overriderebase(orig, ui, repo, **opts):
876 def overriderebase(orig, ui, repo, **opts):
877 if not util.safehasattr(repo, '_largefilesenabled'):
877 if not util.safehasattr(repo, '_largefilesenabled'):
878 return orig(ui, repo, **opts)
878 return orig(ui, repo, **opts)
879
879
880 resuming = opts.get('continue')
880 resuming = opts.get('continue')
881 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
881 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
882 repo._lfstatuswriters.append(lambda *msg, **opts: None)
882 repo._lfstatuswriters.append(lambda *msg, **opts: None)
883 try:
883 try:
884 return orig(ui, repo, **opts)
884 return orig(ui, repo, **opts)
885 finally:
885 finally:
886 repo._lfstatuswriters.pop()
886 repo._lfstatuswriters.pop()
887 repo._lfcommithooks.pop()
887 repo._lfcommithooks.pop()
888
888
889 def overridearchivecmd(orig, ui, repo, dest, **opts):
889 def overridearchivecmd(orig, ui, repo, dest, **opts):
890 repo.unfiltered().lfstatus = True
890 repo.unfiltered().lfstatus = True
891
891
892 try:
892 try:
893 return orig(ui, repo.unfiltered(), dest, **opts)
893 return orig(ui, repo.unfiltered(), dest, **opts)
894 finally:
894 finally:
895 repo.unfiltered().lfstatus = False
895 repo.unfiltered().lfstatus = False
896
896
897 def hgwebarchive(orig, web, req, tmpl):
897 def hgwebarchive(orig, web, req, tmpl):
898 web.repo.lfstatus = True
898 web.repo.lfstatus = True
899
899
900 try:
900 try:
901 return orig(web, req, tmpl)
901 return orig(web, req, tmpl)
902 finally:
902 finally:
903 web.repo.lfstatus = False
903 web.repo.lfstatus = False
904
904
905 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
905 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
906 prefix='', mtime=None, subrepos=None):
906 prefix='', mtime=None, subrepos=None):
907 # For some reason setting repo.lfstatus in hgwebarchive only changes the
907 # For some reason setting repo.lfstatus in hgwebarchive only changes the
908 # unfiltered repo's attr, so check that as well.
908 # unfiltered repo's attr, so check that as well.
909 if not repo.lfstatus and not repo.unfiltered().lfstatus:
909 if not repo.lfstatus and not repo.unfiltered().lfstatus:
910 return orig(repo, dest, node, kind, decode, matchfn, prefix, mtime,
910 return orig(repo, dest, node, kind, decode, matchfn, prefix, mtime,
911 subrepos)
911 subrepos)
912
912
913 # No need to lock because we are only reading history and
913 # No need to lock because we are only reading history and
914 # largefile caches, neither of which are modified.
914 # largefile caches, neither of which are modified.
915 if node is not None:
915 if node is not None:
916 lfcommands.cachelfiles(repo.ui, repo, node)
916 lfcommands.cachelfiles(repo.ui, repo, node)
917
917
918 if kind not in archival.archivers:
918 if kind not in archival.archivers:
919 raise error.Abort(_("unknown archive type '%s'") % kind)
919 raise error.Abort(_("unknown archive type '%s'") % kind)
920
920
921 ctx = repo[node]
921 ctx = repo[node]
922
922
923 if kind == 'files':
923 if kind == 'files':
924 if prefix:
924 if prefix:
925 raise error.Abort(
925 raise error.Abort(
926 _('cannot give prefix when archiving to files'))
926 _('cannot give prefix when archiving to files'))
927 else:
927 else:
928 prefix = archival.tidyprefix(dest, kind, prefix)
928 prefix = archival.tidyprefix(dest, kind, prefix)
929
929
930 def write(name, mode, islink, getdata):
930 def write(name, mode, islink, getdata):
931 if matchfn and not matchfn(name):
931 if matchfn and not matchfn(name):
932 return
932 return
933 data = getdata()
933 data = getdata()
934 if decode:
934 if decode:
935 data = repo.wwritedata(name, data)
935 data = repo.wwritedata(name, data)
936 archiver.addfile(prefix + name, mode, islink, data)
936 archiver.addfile(prefix + name, mode, islink, data)
937
937
938 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
938 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
939
939
940 if repo.ui.configbool("ui", "archivemeta", True):
940 if repo.ui.configbool("ui", "archivemeta", True):
941 write('.hg_archival.txt', 0o644, False,
941 write('.hg_archival.txt', 0o644, False,
942 lambda: archival.buildmetadata(ctx))
942 lambda: archival.buildmetadata(ctx))
943
943
944 for f in ctx:
944 for f in ctx:
945 ff = ctx.flags(f)
945 ff = ctx.flags(f)
946 getdata = ctx[f].data
946 getdata = ctx[f].data
947 if lfutil.isstandin(f):
947 if lfutil.isstandin(f):
948 if node is not None:
948 if node is not None:
949 path = lfutil.findfile(repo, getdata().strip())
949 path = lfutil.findfile(repo, getdata().strip())
950
950
951 if path is None:
951 if path is None:
952 raise error.Abort(
952 raise error.Abort(
953 _('largefile %s not found in repo store or system cache')
953 _('largefile %s not found in repo store or system cache')
954 % lfutil.splitstandin(f))
954 % lfutil.splitstandin(f))
955 else:
955 else:
956 path = lfutil.splitstandin(f)
956 path = lfutil.splitstandin(f)
957
957
958 f = lfutil.splitstandin(f)
958 f = lfutil.splitstandin(f)
959
959
960 getdata = lambda: util.readfile(path)
960 getdata = lambda: util.readfile(path)
961 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)
962
962
963 if subrepos:
963 if subrepos:
964 for subpath in sorted(ctx.substate):
964 for subpath in sorted(ctx.substate):
965 sub = ctx.workingsub(subpath)
965 sub = ctx.workingsub(subpath)
966 submatch = match_.narrowmatcher(subpath, matchfn)
966 submatch = match_.narrowmatcher(subpath, matchfn)
967 sub._repo.lfstatus = True
967 sub._repo.lfstatus = True
968 sub.archive(archiver, prefix, submatch)
968 sub.archive(archiver, prefix, submatch)
969
969
970 archiver.done()
970 archiver.done()
971
971
972 def hgsubrepoarchive(orig, repo, archiver, prefix, match=None):
972 def hgsubrepoarchive(orig, repo, archiver, prefix, match=None):
973 if not repo._repo.lfstatus:
973 if not repo._repo.lfstatus:
974 return orig(repo, archiver, prefix, match)
974 return orig(repo, archiver, prefix, match)
975
975
976 repo._get(repo._state + ('hg',))
976 repo._get(repo._state + ('hg',))
977 rev = repo._state[1]
977 rev = repo._state[1]
978 ctx = repo._repo[rev]
978 ctx = repo._repo[rev]
979
979
980 if ctx.node() is not None:
980 if ctx.node() is not None:
981 lfcommands.cachelfiles(repo.ui, repo._repo, ctx.node())
981 lfcommands.cachelfiles(repo.ui, repo._repo, ctx.node())
982
982
983 def write(name, mode, islink, getdata):
983 def write(name, mode, islink, getdata):
984 # 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,
985 # so the normal matcher works here without the lfutil variants.
985 # so the normal matcher works here without the lfutil variants.
986 if match and not match(f):
986 if match and not match(f):
987 return
987 return
988 data = getdata()
988 data = getdata()
989
989
990 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
990 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
991
991
992 for f in ctx:
992 for f in ctx:
993 ff = ctx.flags(f)
993 ff = ctx.flags(f)
994 getdata = ctx[f].data
994 getdata = ctx[f].data
995 if lfutil.isstandin(f):
995 if lfutil.isstandin(f):
996 if ctx.node() is not None:
996 if ctx.node() is not None:
997 path = lfutil.findfile(repo._repo, getdata().strip())
997 path = lfutil.findfile(repo._repo, getdata().strip())
998
998
999 if path is None:
999 if path is None:
1000 raise error.Abort(
1000 raise error.Abort(
1001 _('largefile %s not found in repo store or system cache')
1001 _('largefile %s not found in repo store or system cache')
1002 % lfutil.splitstandin(f))
1002 % lfutil.splitstandin(f))
1003 else:
1003 else:
1004 path = lfutil.splitstandin(f)
1004 path = lfutil.splitstandin(f)
1005
1005
1006 f = lfutil.splitstandin(f)
1006 f = lfutil.splitstandin(f)
1007
1007
1008 getdata = lambda: util.readfile(os.path.join(prefix, path))
1008 getdata = lambda: util.readfile(os.path.join(prefix, path))
1009
1009
1010 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)
1011
1011
1012 for subpath in sorted(ctx.substate):
1012 for subpath in sorted(ctx.substate):
1013 sub = ctx.workingsub(subpath)
1013 sub = ctx.workingsub(subpath)
1014 submatch = match_.narrowmatcher(subpath, match)
1014 submatch = match_.narrowmatcher(subpath, match)
1015 sub._repo.lfstatus = True
1015 sub._repo.lfstatus = True
1016 sub.archive(archiver, prefix + repo._path + '/', submatch)
1016 sub.archive(archiver, prefix + repo._path + '/', submatch)
1017
1017
1018 # 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
1019 # standin until a commit. cmdutil.bailifchanged() raises an exception
1019 # standin until a commit. cmdutil.bailifchanged() raises an exception
1020 # 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
1021 # largefiles were changed. This is used by bisect, backout and fetch.
1021 # largefiles were changed. This is used by bisect, backout and fetch.
1022 def overridebailifchanged(orig, repo, *args, **kwargs):
1022 def overridebailifchanged(orig, repo, *args, **kwargs):
1023 orig(repo, *args, **kwargs)
1023 orig(repo, *args, **kwargs)
1024 repo.lfstatus = True
1024 repo.lfstatus = True
1025 s = repo.status()
1025 s = repo.status()
1026 repo.lfstatus = False
1026 repo.lfstatus = False
1027 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:
1028 raise error.Abort(_('uncommitted changes'))
1028 raise error.Abort(_('uncommitted changes'))
1029
1029
1030 def cmdutilforget(orig, ui, repo, match, prefix, explicitonly):
1030 def cmdutilforget(orig, ui, repo, match, prefix, explicitonly):
1031 normalmatcher = composenormalfilematcher(match, repo[None].manifest())
1031 normalmatcher = composenormalfilematcher(match, repo[None].manifest())
1032 bad, forgot = orig(ui, repo, normalmatcher, prefix, explicitonly)
1032 bad, forgot = orig(ui, repo, normalmatcher, prefix, explicitonly)
1033 m = composelargefilematcher(match, repo[None].manifest())
1033 m = composelargefilematcher(match, repo[None].manifest())
1034
1034
1035 try:
1035 try:
1036 repo.lfstatus = True
1036 repo.lfstatus = True
1037 s = repo.status(match=m, clean=True)
1037 s = repo.status(match=m, clean=True)
1038 finally:
1038 finally:
1039 repo.lfstatus = False
1039 repo.lfstatus = False
1040 forget = sorted(s.modified + s.added + s.deleted + s.clean)
1040 forget = sorted(s.modified + s.added + s.deleted + s.clean)
1041 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()]
1042
1042
1043 for f in forget:
1043 for f in forget:
1044 if lfutil.standin(f) not in repo.dirstate and not \
1044 if lfutil.standin(f) not in repo.dirstate and not \
1045 repo.wvfs.isdir(lfutil.standin(f)):
1045 repo.wvfs.isdir(lfutil.standin(f)):
1046 ui.warn(_('not removing %s: file is already untracked\n')
1046 ui.warn(_('not removing %s: file is already untracked\n')
1047 % m.rel(f))
1047 % m.rel(f))
1048 bad.append(f)
1048 bad.append(f)
1049
1049
1050 for f in forget:
1050 for f in forget:
1051 if ui.verbose or not m.exact(f):
1051 if ui.verbose or not m.exact(f):
1052 ui.status(_('removing %s\n') % m.rel(f))
1052 ui.status(_('removing %s\n') % m.rel(f))
1053
1053
1054 # 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
1055 # repository and we could race in-between.
1055 # repository and we could race in-between.
1056 with repo.wlock():
1056 with repo.wlock():
1057 lfdirstate = lfutil.openlfdirstate(ui, repo)
1057 lfdirstate = lfutil.openlfdirstate(ui, repo)
1058 for f in forget:
1058 for f in forget:
1059 if lfdirstate[f] == 'a':
1059 if lfdirstate[f] == 'a':
1060 lfdirstate.drop(f)
1060 lfdirstate.drop(f)
1061 else:
1061 else:
1062 lfdirstate.remove(f)
1062 lfdirstate.remove(f)
1063 lfdirstate.write()
1063 lfdirstate.write()
1064 standins = [lfutil.standin(f) for f in forget]
1064 standins = [lfutil.standin(f) for f in forget]
1065 for f in standins:
1065 for f in standins:
1066 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1066 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1067 rejected = repo[None].forget(standins)
1067 rejected = repo[None].forget(standins)
1068
1068
1069 bad.extend(f for f in rejected if f in m.files())
1069 bad.extend(f for f in rejected if f in m.files())
1070 forgot.extend(f for f in forget if f not in rejected)
1070 forgot.extend(f for f in forget if f not in rejected)
1071 return bad, forgot
1071 return bad, forgot
1072
1072
1073 def _getoutgoings(repo, other, missing, addfunc):
1073 def _getoutgoings(repo, other, missing, addfunc):
1074 """get pairs of filename and largefile hash in outgoing revisions
1074 """get pairs of filename and largefile hash in outgoing revisions
1075 in 'missing'.
1075 in 'missing'.
1076
1076
1077 largefiles already existing on 'other' repository are ignored.
1077 largefiles already existing on 'other' repository are ignored.
1078
1078
1079 'addfunc' is invoked with each unique pairs of filename and
1079 'addfunc' is invoked with each unique pairs of filename and
1080 largefile hash value.
1080 largefile hash value.
1081 """
1081 """
1082 knowns = set()
1082 knowns = set()
1083 lfhashes = set()
1083 lfhashes = set()
1084 def dedup(fn, lfhash):
1084 def dedup(fn, lfhash):
1085 k = (fn, lfhash)
1085 k = (fn, lfhash)
1086 if k not in knowns:
1086 if k not in knowns:
1087 knowns.add(k)
1087 knowns.add(k)
1088 lfhashes.add(lfhash)
1088 lfhashes.add(lfhash)
1089 lfutil.getlfilestoupload(repo, missing, dedup)
1089 lfutil.getlfilestoupload(repo, missing, dedup)
1090 if lfhashes:
1090 if lfhashes:
1091 lfexists = basestore._openstore(repo, other).exists(lfhashes)
1091 lfexists = basestore._openstore(repo, other).exists(lfhashes)
1092 for fn, lfhash in knowns:
1092 for fn, lfhash in knowns:
1093 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1093 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1094 addfunc(fn, lfhash)
1094 addfunc(fn, lfhash)
1095
1095
1096 def outgoinghook(ui, repo, other, opts, missing):
1096 def outgoinghook(ui, repo, other, opts, missing):
1097 if opts.pop('large', None):
1097 if opts.pop('large', None):
1098 lfhashes = set()
1098 lfhashes = set()
1099 if ui.debugflag:
1099 if ui.debugflag:
1100 toupload = {}
1100 toupload = {}
1101 def addfunc(fn, lfhash):
1101 def addfunc(fn, lfhash):
1102 if fn not in toupload:
1102 if fn not in toupload:
1103 toupload[fn] = []
1103 toupload[fn] = []
1104 toupload[fn].append(lfhash)
1104 toupload[fn].append(lfhash)
1105 lfhashes.add(lfhash)
1105 lfhashes.add(lfhash)
1106 def showhashes(fn):
1106 def showhashes(fn):
1107 for lfhash in sorted(toupload[fn]):
1107 for lfhash in sorted(toupload[fn]):
1108 ui.debug(' %s\n' % (lfhash))
1108 ui.debug(' %s\n' % (lfhash))
1109 else:
1109 else:
1110 toupload = set()
1110 toupload = set()
1111 def addfunc(fn, lfhash):
1111 def addfunc(fn, lfhash):
1112 toupload.add(fn)
1112 toupload.add(fn)
1113 lfhashes.add(lfhash)
1113 lfhashes.add(lfhash)
1114 def showhashes(fn):
1114 def showhashes(fn):
1115 pass
1115 pass
1116 _getoutgoings(repo, other, missing, addfunc)
1116 _getoutgoings(repo, other, missing, addfunc)
1117
1117
1118 if not toupload:
1118 if not toupload:
1119 ui.status(_('largefiles: no files to upload\n'))
1119 ui.status(_('largefiles: no files to upload\n'))
1120 else:
1120 else:
1121 ui.status(_('largefiles to upload (%d entities):\n')
1121 ui.status(_('largefiles to upload (%d entities):\n')
1122 % (len(lfhashes)))
1122 % (len(lfhashes)))
1123 for file in sorted(toupload):
1123 for file in sorted(toupload):
1124 ui.status(lfutil.splitstandin(file) + '\n')
1124 ui.status(lfutil.splitstandin(file) + '\n')
1125 showhashes(file)
1125 showhashes(file)
1126 ui.status('\n')
1126 ui.status('\n')
1127
1127
1128 def summaryremotehook(ui, repo, opts, changes):
1128 def summaryremotehook(ui, repo, opts, changes):
1129 largeopt = opts.get('large', False)
1129 largeopt = opts.get('large', False)
1130 if changes is None:
1130 if changes is None:
1131 if largeopt:
1131 if largeopt:
1132 return (False, True) # only outgoing check is needed
1132 return (False, True) # only outgoing check is needed
1133 else:
1133 else:
1134 return (False, False)
1134 return (False, False)
1135 elif largeopt:
1135 elif largeopt:
1136 url, branch, peer, outgoing = changes[1]
1136 url, branch, peer, outgoing = changes[1]
1137 if peer is None:
1137 if peer is None:
1138 # i18n: column positioning for "hg summary"
1138 # i18n: column positioning for "hg summary"
1139 ui.status(_('largefiles: (no remote repo)\n'))
1139 ui.status(_('largefiles: (no remote repo)\n'))
1140 return
1140 return
1141
1141
1142 toupload = set()
1142 toupload = set()
1143 lfhashes = set()
1143 lfhashes = set()
1144 def addfunc(fn, lfhash):
1144 def addfunc(fn, lfhash):
1145 toupload.add(fn)
1145 toupload.add(fn)
1146 lfhashes.add(lfhash)
1146 lfhashes.add(lfhash)
1147 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1147 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1148
1148
1149 if not toupload:
1149 if not toupload:
1150 # i18n: column positioning for "hg summary"
1150 # i18n: column positioning for "hg summary"
1151 ui.status(_('largefiles: (no files to upload)\n'))
1151 ui.status(_('largefiles: (no files to upload)\n'))
1152 else:
1152 else:
1153 # i18n: column positioning for "hg summary"
1153 # i18n: column positioning for "hg summary"
1154 ui.status(_('largefiles: %d entities for %d files to upload\n')
1154 ui.status(_('largefiles: %d entities for %d files to upload\n')
1155 % (len(lfhashes), len(toupload)))
1155 % (len(lfhashes), len(toupload)))
1156
1156
1157 def overridesummary(orig, ui, repo, *pats, **opts):
1157 def overridesummary(orig, ui, repo, *pats, **opts):
1158 try:
1158 try:
1159 repo.lfstatus = True
1159 repo.lfstatus = True
1160 orig(ui, repo, *pats, **opts)
1160 orig(ui, repo, *pats, **opts)
1161 finally:
1161 finally:
1162 repo.lfstatus = False
1162 repo.lfstatus = False
1163
1163
1164 def scmutiladdremove(orig, repo, matcher, prefix, opts=None, dry_run=None,
1164 def scmutiladdremove(orig, repo, matcher, prefix, opts=None, dry_run=None,
1165 similarity=None):
1165 similarity=None):
1166 if opts is None:
1166 if opts is None:
1167 opts = {}
1167 opts = {}
1168 if not lfutil.islfilesrepo(repo):
1168 if not lfutil.islfilesrepo(repo):
1169 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1169 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1170 # Get the list of missing largefiles so we can remove them
1170 # Get the list of missing largefiles so we can remove them
1171 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1171 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1172 unsure, s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [],
1172 unsure, s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [],
1173 False, False, False)
1173 False, False, False)
1174
1174
1175 # Call into the normal remove code, but the removing of the standin, we want
1175 # Call into the normal remove code, but the removing of the standin, we want
1176 # to have handled by original addremove. Monkey patching here makes sure
1176 # to have handled by original addremove. Monkey patching here makes sure
1177 # we don't remove the standin in the largefiles code, preventing a very
1177 # we don't remove the standin in the largefiles code, preventing a very
1178 # confused state later.
1178 # confused state later.
1179 if s.deleted:
1179 if s.deleted:
1180 m = copy.copy(matcher)
1180 m = copy.copy(matcher)
1181
1181
1182 # The m._files and m._map attributes are not changed to the deleted list
1182 # The m._files and m._map attributes are not changed to the deleted list
1183 # because that affects the m.exact() test, which in turn governs whether
1183 # because that affects the m.exact() test, which in turn governs whether
1184 # or not the file name is printed, and how. Simply limit the original
1184 # or not the file name is printed, and how. Simply limit the original
1185 # matches to those in the deleted status list.
1185 # matches to those in the deleted status list.
1186 matchfn = m.matchfn
1186 matchfn = m.matchfn
1187 m.matchfn = lambda f: f in s.deleted and matchfn(f)
1187 m.matchfn = lambda f: f in s.deleted and matchfn(f)
1188
1188
1189 removelargefiles(repo.ui, repo, True, m, **opts)
1189 removelargefiles(repo.ui, repo, True, m, **opts)
1190 # Call into the normal add code, and any files that *should* be added as
1190 # Call into the normal add code, and any files that *should* be added as
1191 # largefiles will be
1191 # largefiles will be
1192 added, bad = addlargefiles(repo.ui, repo, True, matcher, **opts)
1192 added, bad = addlargefiles(repo.ui, repo, True, matcher, **opts)
1193 # Now that we've handled largefiles, hand off to the original addremove
1193 # Now that we've handled largefiles, hand off to the original addremove
1194 # function to take care of the rest. Make sure it doesn't do anything with
1194 # function to take care of the rest. Make sure it doesn't do anything with
1195 # largefiles by passing a matcher that will ignore them.
1195 # largefiles by passing a matcher that will ignore them.
1196 matcher = composenormalfilematcher(matcher, repo[None].manifest(), added)
1196 matcher = composenormalfilematcher(matcher, repo[None].manifest(), added)
1197 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1197 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1198
1198
1199 # Calling purge with --all will cause the largefiles to be deleted.
1199 # Calling purge with --all will cause the largefiles to be deleted.
1200 # Override repo.status to prevent this from happening.
1200 # Override repo.status to prevent this from happening.
1201 def overridepurge(orig, ui, repo, *dirs, **opts):
1201 def overridepurge(orig, ui, repo, *dirs, **opts):
1202 # XXX Monkey patching a repoview will not work. The assigned attribute will
1202 # XXX Monkey patching a repoview will not work. The assigned attribute will
1203 # be set on the unfiltered repo, but we will only lookup attributes in the
1203 # be set on the unfiltered repo, but we will only lookup attributes in the
1204 # unfiltered repo if the lookup in the repoview object itself fails. As the
1204 # unfiltered repo if the lookup in the repoview object itself fails. As the
1205 # monkey patched method exists on the repoview class the lookup will not
1205 # monkey patched method exists on the repoview class the lookup will not
1206 # fail. As a result, the original version will shadow the monkey patched
1206 # fail. As a result, the original version will shadow the monkey patched
1207 # one, defeating the monkey patch.
1207 # one, defeating the monkey patch.
1208 #
1208 #
1209 # As a work around we use an unfiltered repo here. We should do something
1209 # As a work around we use an unfiltered repo here. We should do something
1210 # cleaner instead.
1210 # cleaner instead.
1211 repo = repo.unfiltered()
1211 repo = repo.unfiltered()
1212 oldstatus = repo.status
1212 oldstatus = repo.status
1213 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1213 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1214 clean=False, unknown=False, listsubrepos=False):
1214 clean=False, unknown=False, listsubrepos=False):
1215 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1215 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1216 listsubrepos)
1216 listsubrepos)
1217 lfdirstate = lfutil.openlfdirstate(ui, repo)
1217 lfdirstate = lfutil.openlfdirstate(ui, repo)
1218 unknown = [f for f in r.unknown if lfdirstate[f] == '?']
1218 unknown = [f for f in r.unknown if lfdirstate[f] == '?']
1219 ignored = [f for f in r.ignored if lfdirstate[f] == '?']
1219 ignored = [f for f in r.ignored if lfdirstate[f] == '?']
1220 return scmutil.status(r.modified, r.added, r.removed, r.deleted,
1220 return scmutil.status(r.modified, r.added, r.removed, r.deleted,
1221 unknown, ignored, r.clean)
1221 unknown, ignored, r.clean)
1222 repo.status = overridestatus
1222 repo.status = overridestatus
1223 orig(ui, repo, *dirs, **opts)
1223 orig(ui, repo, *dirs, **opts)
1224 repo.status = oldstatus
1224 repo.status = oldstatus
1225 def overriderollback(orig, ui, repo, **opts):
1225 def overriderollback(orig, ui, repo, **opts):
1226 with repo.wlock():
1226 with repo.wlock():
1227 before = repo.dirstate.parents()
1227 before = repo.dirstate.parents()
1228 orphans = set(f for f in repo.dirstate
1228 orphans = set(f for f in repo.dirstate
1229 if lfutil.isstandin(f) and repo.dirstate[f] != 'r')
1229 if lfutil.isstandin(f) and repo.dirstate[f] != 'r')
1230 result = orig(ui, repo, **opts)
1230 result = orig(ui, repo, **opts)
1231 after = repo.dirstate.parents()
1231 after = repo.dirstate.parents()
1232 if before == after:
1232 if before == after:
1233 return result # no need to restore standins
1233 return result # no need to restore standins
1234
1234
1235 pctx = repo['.']
1235 pctx = repo['.']
1236 for f in repo.dirstate:
1236 for f in repo.dirstate:
1237 if lfutil.isstandin(f):
1237 if lfutil.isstandin(f):
1238 orphans.discard(f)
1238 orphans.discard(f)
1239 if repo.dirstate[f] == 'r':
1239 if repo.dirstate[f] == 'r':
1240 repo.wvfs.unlinkpath(f, ignoremissing=True)
1240 repo.wvfs.unlinkpath(f, ignoremissing=True)
1241 elif f in pctx:
1241 elif f in pctx:
1242 fctx = pctx[f]
1242 fctx = pctx[f]
1243 repo.wwrite(f, fctx.data(), fctx.flags())
1243 repo.wwrite(f, fctx.data(), fctx.flags())
1244 else:
1244 else:
1245 # content of standin is not so important in 'a',
1245 # content of standin is not so important in 'a',
1246 # 'm' or 'n' (coming from the 2nd parent) cases
1246 # 'm' or 'n' (coming from the 2nd parent) cases
1247 lfutil.writestandin(repo, f, '', False)
1247 lfutil.writestandin(repo, f, '', False)
1248 for standin in orphans:
1248 for standin in orphans:
1249 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1249 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1250
1250
1251 lfdirstate = lfutil.openlfdirstate(ui, repo)
1251 lfdirstate = lfutil.openlfdirstate(ui, repo)
1252 orphans = set(lfdirstate)
1252 orphans = set(lfdirstate)
1253 lfiles = lfutil.listlfiles(repo)
1253 lfiles = lfutil.listlfiles(repo)
1254 for file in lfiles:
1254 for file in lfiles:
1255 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1255 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1256 orphans.discard(file)
1256 orphans.discard(file)
1257 for lfile in orphans:
1257 for lfile in orphans:
1258 lfdirstate.drop(lfile)
1258 lfdirstate.drop(lfile)
1259 lfdirstate.write()
1259 lfdirstate.write()
1260 return result
1260 return result
1261
1261
1262 def overridetransplant(orig, ui, repo, *revs, **opts):
1262 def overridetransplant(orig, ui, repo, *revs, **opts):
1263 resuming = opts.get('continue')
1263 resuming = opts.get('continue')
1264 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1264 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1265 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1265 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1266 try:
1266 try:
1267 result = orig(ui, repo, *revs, **opts)
1267 result = orig(ui, repo, *revs, **opts)
1268 finally:
1268 finally:
1269 repo._lfstatuswriters.pop()
1269 repo._lfstatuswriters.pop()
1270 repo._lfcommithooks.pop()
1270 repo._lfcommithooks.pop()
1271 return result
1271 return result
1272
1272
1273 def overridecat(orig, ui, repo, file1, *pats, **opts):
1273 def overridecat(orig, ui, repo, file1, *pats, **opts):
1274 ctx = scmutil.revsingle(repo, opts.get('rev'))
1274 ctx = scmutil.revsingle(repo, opts.get('rev'))
1275 err = 1
1275 err = 1
1276 notbad = set()
1276 notbad = set()
1277 m = scmutil.match(ctx, (file1,) + pats, opts)
1277 m = scmutil.match(ctx, (file1,) + pats, opts)
1278 origmatchfn = m.matchfn
1278 origmatchfn = m.matchfn
1279 def lfmatchfn(f):
1279 def lfmatchfn(f):
1280 if origmatchfn(f):
1280 if origmatchfn(f):
1281 return True
1281 return True
1282 lf = lfutil.splitstandin(f)
1282 lf = lfutil.splitstandin(f)
1283 if lf is None:
1283 if lf is None:
1284 return False
1284 return False
1285 notbad.add(lf)
1285 notbad.add(lf)
1286 return origmatchfn(lf)
1286 return origmatchfn(lf)
1287 m.matchfn = lfmatchfn
1287 m.matchfn = lfmatchfn
1288 origbadfn = m.bad
1288 origbadfn = m.bad
1289 def lfbadfn(f, msg):
1289 def lfbadfn(f, msg):
1290 if not f in notbad:
1290 if not f in notbad:
1291 origbadfn(f, msg)
1291 origbadfn(f, msg)
1292 m.bad = lfbadfn
1292 m.bad = lfbadfn
1293
1293
1294 origvisitdirfn = m.visitdir
1294 origvisitdirfn = m.visitdir
1295 def lfvisitdirfn(dir):
1295 def lfvisitdirfn(dir):
1296 if dir == lfutil.shortname:
1296 if dir == lfutil.shortname:
1297 return True
1297 return True
1298 ret = origvisitdirfn(dir)
1298 ret = origvisitdirfn(dir)
1299 if ret:
1299 if ret:
1300 return ret
1300 return ret
1301 lf = lfutil.splitstandin(dir)
1301 lf = lfutil.splitstandin(dir)
1302 if lf is None:
1302 if lf is None:
1303 return False
1303 return False
1304 return origvisitdirfn(lf)
1304 return origvisitdirfn(lf)
1305 m.visitdir = lfvisitdirfn
1305 m.visitdir = lfvisitdirfn
1306
1306
1307 for f in ctx.walk(m):
1307 for f in ctx.walk(m):
1308 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1308 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1309 pathname=f)
1309 pathname=f)
1310 lf = lfutil.splitstandin(f)
1310 lf = lfutil.splitstandin(f)
1311 if lf is None or origmatchfn(f):
1311 if lf is None or origmatchfn(f):
1312 # duplicating unreachable code from commands.cat
1312 # duplicating unreachable code from commands.cat
1313 data = ctx[f].data()
1313 data = ctx[f].data()
1314 if opts.get('decode'):
1314 if opts.get('decode'):
1315 data = repo.wwritedata(f, data)
1315 data = repo.wwritedata(f, data)
1316 fp.write(data)
1316 fp.write(data)
1317 else:
1317 else:
1318 hash = lfutil.readstandin(repo, lf, ctx.rev())
1318 hash = lfutil.readstandin(repo, lf, ctx.rev())
1319 if not lfutil.inusercache(repo.ui, hash):
1319 if not lfutil.inusercache(repo.ui, hash):
1320 store = basestore._openstore(repo)
1320 store = basestore._openstore(repo)
1321 success, missing = store.get([(lf, hash)])
1321 success, missing = store.get([(lf, hash)])
1322 if len(success) != 1:
1322 if len(success) != 1:
1323 raise error.Abort(
1323 raise error.Abort(
1324 _('largefile %s is not in cache and could not be '
1324 _('largefile %s is not in cache and could not be '
1325 'downloaded') % lf)
1325 'downloaded') % lf)
1326 path = lfutil.usercachepath(repo.ui, hash)
1326 path = lfutil.usercachepath(repo.ui, hash)
1327 fpin = open(path, "rb")
1327 fpin = open(path, "rb")
1328 for chunk in util.filechunkiter(fpin, 128 * 1024):
1328 for chunk in util.filechunkiter(fpin, 128 * 1024):
1329 fp.write(chunk)
1329 fp.write(chunk)
1330 fpin.close()
1330 fpin.close()
1331 fp.close()
1331 fp.close()
1332 err = 0
1332 err = 0
1333 return err
1333 return err
1334
1334
1335 def mergeupdate(orig, repo, node, branchmerge, force,
1335 def mergeupdate(orig, repo, node, branchmerge, force,
1336 *args, **kwargs):
1336 *args, **kwargs):
1337 matcher = kwargs.get('matcher', None)
1337 matcher = kwargs.get('matcher', None)
1338 # note if this is a partial update
1338 # note if this is a partial update
1339 partial = matcher and not matcher.always()
1339 partial = matcher and not matcher.always()
1340 with repo.wlock():
1340 with repo.wlock():
1341 # branch | | |
1341 # branch | | |
1342 # merge | force | partial | action
1342 # merge | force | partial | action
1343 # -------+-------+---------+--------------
1343 # -------+-------+---------+--------------
1344 # x | x | x | linear-merge
1344 # x | x | x | linear-merge
1345 # o | x | x | branch-merge
1345 # o | x | x | branch-merge
1346 # x | o | x | overwrite (as clean update)
1346 # x | o | x | overwrite (as clean update)
1347 # o | o | x | force-branch-merge (*1)
1347 # o | o | x | force-branch-merge (*1)
1348 # x | x | o | (*)
1348 # x | x | o | (*)
1349 # o | x | o | (*)
1349 # o | x | o | (*)
1350 # x | o | o | overwrite (as revert)
1350 # x | o | o | overwrite (as revert)
1351 # o | o | o | (*)
1351 # o | o | o | (*)
1352 #
1352 #
1353 # (*) don't care
1353 # (*) don't care
1354 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1354 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1355
1355
1356 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1356 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1357 unsure, s = lfdirstate.status(match_.always(repo.root,
1357 unsure, s = lfdirstate.status(match_.always(repo.root,
1358 repo.getcwd()),
1358 repo.getcwd()),
1359 [], False, False, False)
1359 [], False, False, False)
1360 pctx = repo['.']
1360 pctx = repo['.']
1361 for lfile in unsure + s.modified:
1361 for lfile in unsure + s.modified:
1362 lfileabs = repo.wvfs.join(lfile)
1362 lfileabs = repo.wvfs.join(lfile)
1363 if not os.path.exists(lfileabs):
1363 if not os.path.exists(lfileabs):
1364 continue
1364 continue
1365 lfhash = lfutil.hashrepofile(repo, lfile)
1365 lfhash = lfutil.hashrepofile(repo, lfile)
1366 standin = lfutil.standin(lfile)
1366 standin = lfutil.standin(lfile)
1367 lfutil.writestandin(repo, standin, lfhash,
1367 lfutil.writestandin(repo, standin, lfhash,
1368 lfutil.getexecutable(lfileabs))
1368 lfutil.getexecutable(lfileabs))
1369 if (standin in pctx and
1369 if (standin in pctx and
1370 lfhash == lfutil.readstandin(repo, lfile, '.')):
1370 lfhash == lfutil.readstandin(repo, lfile, '.')):
1371 lfdirstate.normal(lfile)
1371 lfdirstate.normal(lfile)
1372 for lfile in s.added:
1372 for lfile in s.added:
1373 lfutil.updatestandin(repo, lfutil.standin(lfile))
1373 lfutil.updatestandin(repo, lfutil.standin(lfile))
1374 lfdirstate.write()
1374 lfdirstate.write()
1375
1375
1376 oldstandins = lfutil.getstandinsstate(repo)
1376 oldstandins = lfutil.getstandinsstate(repo)
1377
1377
1378 result = orig(repo, node, branchmerge, force, *args, **kwargs)
1378 result = orig(repo, node, branchmerge, force, *args, **kwargs)
1379
1379
1380 newstandins = lfutil.getstandinsstate(repo)
1380 newstandins = lfutil.getstandinsstate(repo)
1381 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1381 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1382 if branchmerge or force or partial:
1382 if branchmerge or force or partial:
1383 filelist.extend(s.deleted + s.removed)
1383 filelist.extend(s.deleted + s.removed)
1384
1384
1385 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1385 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1386 normallookup=partial)
1386 normallookup=partial)
1387
1387
1388 return result
1388 return result
1389
1389
1390 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1390 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1391 result = orig(repo, files, *args, **kwargs)
1391 result = orig(repo, files, *args, **kwargs)
1392
1392
1393 filelist = [lfutil.splitstandin(f) for f in files if lfutil.isstandin(f)]
1393 filelist = [lfutil.splitstandin(f) for f in files if lfutil.isstandin(f)]
1394 if filelist:
1394 if filelist:
1395 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1395 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1396 printmessage=False, normallookup=True)
1396 printmessage=False, normallookup=True)
1397
1397
1398 return result
1398 return result
General Comments 0
You need to be logged in to leave comments. Login now