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