##// END OF EJS Templates
largefiles: copy override, install matchfn outside the try/except restoring it
Mads Kiilerich -
r21091:dd584d1a default
parent child Browse files
Show More
@@ -1,1169 +1,1169 b''
1 # Copyright 2009-2010 Gregory P. Ward
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
4 # Copyright 2010-2011 Unity Technologies
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
10
10
11 import os
11 import os
12 import copy
12 import copy
13
13
14 from mercurial import hg, commands, util, cmdutil, scmutil, match as match_, \
14 from mercurial import hg, commands, util, cmdutil, scmutil, match as match_, \
15 archival, merge, pathutil, revset
15 archival, merge, pathutil, revset
16 from mercurial.i18n import _
16 from mercurial.i18n import _
17 from mercurial.node import hex
17 from mercurial.node import hex
18 from hgext import rebase
18 from hgext import rebase
19
19
20 import lfutil
20 import lfutil
21 import lfcommands
21 import lfcommands
22 import basestore
22 import basestore
23
23
24 # -- Utility functions: commonly/repeatedly needed functionality ---------------
24 # -- Utility functions: commonly/repeatedly needed functionality ---------------
25
25
26 def installnormalfilesmatchfn(manifest):
26 def installnormalfilesmatchfn(manifest):
27 '''installmatchfn with a matchfn that ignores all largefiles'''
27 '''installmatchfn with a matchfn that ignores all largefiles'''
28 oldmatch = None # for the closure
28 oldmatch = None # for the closure
29 def overridematch(ctx, pats=[], opts={}, globbed=False,
29 def overridematch(ctx, pats=[], opts={}, globbed=False,
30 default='relpath'):
30 default='relpath'):
31 match = oldmatch(ctx, pats, opts, globbed, default)
31 match = oldmatch(ctx, pats, opts, globbed, default)
32 m = copy.copy(match)
32 m = copy.copy(match)
33 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
33 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
34 manifest)
34 manifest)
35 m._files = filter(notlfile, m._files)
35 m._files = filter(notlfile, m._files)
36 m._fmap = set(m._files)
36 m._fmap = set(m._files)
37 m._always = False
37 m._always = False
38 origmatchfn = m.matchfn
38 origmatchfn = m.matchfn
39 m.matchfn = lambda f: notlfile(f) and origmatchfn(f) or None
39 m.matchfn = lambda f: notlfile(f) and origmatchfn(f) or None
40 return m
40 return m
41 oldmatch = installmatchfn(overridematch)
41 oldmatch = installmatchfn(overridematch)
42
42
43 def installmatchfn(f):
43 def installmatchfn(f):
44 '''monkey patch the scmutil module with a custom match function.
44 '''monkey patch the scmutil module with a custom match function.
45 Warning: it is monkey patching the _module_ on runtime! Not thread safe!'''
45 Warning: it is monkey patching the _module_ on runtime! Not thread safe!'''
46 oldmatch = scmutil.match
46 oldmatch = scmutil.match
47 setattr(f, 'oldmatch', oldmatch)
47 setattr(f, 'oldmatch', oldmatch)
48 scmutil.match = f
48 scmutil.match = f
49 return oldmatch
49 return oldmatch
50
50
51 def restorematchfn():
51 def restorematchfn():
52 '''restores scmutil.match to what it was before installmatchfn
52 '''restores scmutil.match to what it was before installmatchfn
53 was called. no-op if scmutil.match is its original function.
53 was called. no-op if scmutil.match is its original function.
54
54
55 Note that n calls to installmatchfn will require n calls to
55 Note that n calls to installmatchfn will require n calls to
56 restore matchfn to reverse'''
56 restore matchfn to reverse'''
57 scmutil.match = getattr(scmutil.match, 'oldmatch', scmutil.match)
57 scmutil.match = getattr(scmutil.match, 'oldmatch', scmutil.match)
58
58
59 def addlargefiles(ui, repo, *pats, **opts):
59 def addlargefiles(ui, repo, *pats, **opts):
60 large = opts.pop('large', None)
60 large = opts.pop('large', None)
61 lfsize = lfutil.getminsize(
61 lfsize = lfutil.getminsize(
62 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
62 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
63
63
64 lfmatcher = None
64 lfmatcher = None
65 if lfutil.islfilesrepo(repo):
65 if lfutil.islfilesrepo(repo):
66 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
66 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
67 if lfpats:
67 if lfpats:
68 lfmatcher = match_.match(repo.root, '', list(lfpats))
68 lfmatcher = match_.match(repo.root, '', list(lfpats))
69
69
70 lfnames = []
70 lfnames = []
71 m = scmutil.match(repo[None], pats, opts)
71 m = scmutil.match(repo[None], pats, opts)
72 m.bad = lambda x, y: None
72 m.bad = lambda x, y: None
73 wctx = repo[None]
73 wctx = repo[None]
74 for f in repo.walk(m):
74 for f in repo.walk(m):
75 exact = m.exact(f)
75 exact = m.exact(f)
76 lfile = lfutil.standin(f) in wctx
76 lfile = lfutil.standin(f) in wctx
77 nfile = f in wctx
77 nfile = f in wctx
78 exists = lfile or nfile
78 exists = lfile or nfile
79
79
80 # Don't warn the user when they attempt to add a normal tracked file.
80 # Don't warn the user when they attempt to add a normal tracked file.
81 # The normal add code will do that for us.
81 # The normal add code will do that for us.
82 if exact and exists:
82 if exact and exists:
83 if lfile:
83 if lfile:
84 ui.warn(_('%s already a largefile\n') % f)
84 ui.warn(_('%s already a largefile\n') % f)
85 continue
85 continue
86
86
87 if (exact or not exists) and not lfutil.isstandin(f):
87 if (exact or not exists) and not lfutil.isstandin(f):
88 wfile = repo.wjoin(f)
88 wfile = repo.wjoin(f)
89
89
90 # In case the file was removed previously, but not committed
90 # In case the file was removed previously, but not committed
91 # (issue3507)
91 # (issue3507)
92 if not os.path.exists(wfile):
92 if not os.path.exists(wfile):
93 continue
93 continue
94
94
95 abovemin = (lfsize and
95 abovemin = (lfsize and
96 os.lstat(wfile).st_size >= lfsize * 1024 * 1024)
96 os.lstat(wfile).st_size >= lfsize * 1024 * 1024)
97 if large or abovemin or (lfmatcher and lfmatcher(f)):
97 if large or abovemin or (lfmatcher and lfmatcher(f)):
98 lfnames.append(f)
98 lfnames.append(f)
99 if ui.verbose or not exact:
99 if ui.verbose or not exact:
100 ui.status(_('adding %s as a largefile\n') % m.rel(f))
100 ui.status(_('adding %s as a largefile\n') % m.rel(f))
101
101
102 bad = []
102 bad = []
103 standins = []
103 standins = []
104
104
105 # Need to lock, otherwise there could be a race condition between
105 # Need to lock, otherwise there could be a race condition between
106 # when standins are created and added to the repo.
106 # when standins are created and added to the repo.
107 wlock = repo.wlock()
107 wlock = repo.wlock()
108 try:
108 try:
109 if not opts.get('dry_run'):
109 if not opts.get('dry_run'):
110 lfdirstate = lfutil.openlfdirstate(ui, repo)
110 lfdirstate = lfutil.openlfdirstate(ui, repo)
111 for f in lfnames:
111 for f in lfnames:
112 standinname = lfutil.standin(f)
112 standinname = lfutil.standin(f)
113 lfutil.writestandin(repo, standinname, hash='',
113 lfutil.writestandin(repo, standinname, hash='',
114 executable=lfutil.getexecutable(repo.wjoin(f)))
114 executable=lfutil.getexecutable(repo.wjoin(f)))
115 standins.append(standinname)
115 standins.append(standinname)
116 if lfdirstate[f] == 'r':
116 if lfdirstate[f] == 'r':
117 lfdirstate.normallookup(f)
117 lfdirstate.normallookup(f)
118 else:
118 else:
119 lfdirstate.add(f)
119 lfdirstate.add(f)
120 lfdirstate.write()
120 lfdirstate.write()
121 bad += [lfutil.splitstandin(f)
121 bad += [lfutil.splitstandin(f)
122 for f in repo[None].add(standins)
122 for f in repo[None].add(standins)
123 if f in m.files()]
123 if f in m.files()]
124 finally:
124 finally:
125 wlock.release()
125 wlock.release()
126 return bad
126 return bad
127
127
128 def removelargefiles(ui, repo, *pats, **opts):
128 def removelargefiles(ui, repo, *pats, **opts):
129 after = opts.get('after')
129 after = opts.get('after')
130 if not pats and not after:
130 if not pats and not after:
131 raise util.Abort(_('no files specified'))
131 raise util.Abort(_('no files specified'))
132 m = scmutil.match(repo[None], pats, opts)
132 m = scmutil.match(repo[None], pats, opts)
133 try:
133 try:
134 repo.lfstatus = True
134 repo.lfstatus = True
135 s = repo.status(match=m, clean=True)
135 s = repo.status(match=m, clean=True)
136 finally:
136 finally:
137 repo.lfstatus = False
137 repo.lfstatus = False
138 manifest = repo[None].manifest()
138 manifest = repo[None].manifest()
139 modified, added, deleted, clean = [[f for f in list
139 modified, added, deleted, clean = [[f for f in list
140 if lfutil.standin(f) in manifest]
140 if lfutil.standin(f) in manifest]
141 for list in [s[0], s[1], s[3], s[6]]]
141 for list in [s[0], s[1], s[3], s[6]]]
142
142
143 def warn(files, msg):
143 def warn(files, msg):
144 for f in files:
144 for f in files:
145 ui.warn(msg % m.rel(f))
145 ui.warn(msg % m.rel(f))
146 return int(len(files) > 0)
146 return int(len(files) > 0)
147
147
148 result = 0
148 result = 0
149
149
150 if after:
150 if after:
151 remove, forget = deleted, []
151 remove, forget = deleted, []
152 result = warn(modified + added + clean,
152 result = warn(modified + added + clean,
153 _('not removing %s: file still exists\n'))
153 _('not removing %s: file still exists\n'))
154 else:
154 else:
155 remove, forget = deleted + clean, []
155 remove, forget = deleted + clean, []
156 result = warn(modified, _('not removing %s: file is modified (use -f'
156 result = warn(modified, _('not removing %s: file is modified (use -f'
157 ' to force removal)\n'))
157 ' to force removal)\n'))
158 result = warn(added, _('not removing %s: file has been marked for add'
158 result = warn(added, _('not removing %s: file has been marked for add'
159 ' (use forget to undo)\n')) or result
159 ' (use forget to undo)\n')) or result
160
160
161 for f in sorted(remove + forget):
161 for f in sorted(remove + forget):
162 if ui.verbose or not m.exact(f):
162 if ui.verbose or not m.exact(f):
163 ui.status(_('removing %s\n') % m.rel(f))
163 ui.status(_('removing %s\n') % m.rel(f))
164
164
165 # Need to lock because standin files are deleted then removed from the
165 # Need to lock because standin files are deleted then removed from the
166 # repository and we could race in-between.
166 # repository and we could race in-between.
167 wlock = repo.wlock()
167 wlock = repo.wlock()
168 try:
168 try:
169 lfdirstate = lfutil.openlfdirstate(ui, repo)
169 lfdirstate = lfutil.openlfdirstate(ui, repo)
170 for f in remove:
170 for f in remove:
171 if not after:
171 if not after:
172 # If this is being called by addremove, notify the user that we
172 # If this is being called by addremove, notify the user that we
173 # are removing the file.
173 # are removing the file.
174 if getattr(repo, "_isaddremove", False):
174 if getattr(repo, "_isaddremove", False):
175 ui.status(_('removing %s\n') % f)
175 ui.status(_('removing %s\n') % f)
176 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
176 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
177 lfdirstate.remove(f)
177 lfdirstate.remove(f)
178 lfdirstate.write()
178 lfdirstate.write()
179 forget = [lfutil.standin(f) for f in forget]
179 forget = [lfutil.standin(f) for f in forget]
180 remove = [lfutil.standin(f) for f in remove]
180 remove = [lfutil.standin(f) for f in remove]
181 repo[None].forget(forget)
181 repo[None].forget(forget)
182 # If this is being called by addremove, let the original addremove
182 # If this is being called by addremove, let the original addremove
183 # function handle this.
183 # function handle this.
184 if not getattr(repo, "_isaddremove", False):
184 if not getattr(repo, "_isaddremove", False):
185 for f in remove:
185 for f in remove:
186 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
186 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
187 repo[None].forget(remove)
187 repo[None].forget(remove)
188 finally:
188 finally:
189 wlock.release()
189 wlock.release()
190
190
191 return result
191 return result
192
192
193 # For overriding mercurial.hgweb.webcommands so that largefiles will
193 # For overriding mercurial.hgweb.webcommands so that largefiles will
194 # appear at their right place in the manifests.
194 # appear at their right place in the manifests.
195 def decodepath(orig, path):
195 def decodepath(orig, path):
196 return lfutil.splitstandin(path) or path
196 return lfutil.splitstandin(path) or path
197
197
198 # -- Wrappers: modify existing commands --------------------------------
198 # -- Wrappers: modify existing commands --------------------------------
199
199
200 # Add works by going through the files that the user wanted to add and
200 # Add works by going through the files that the user wanted to add and
201 # checking if they should be added as largefiles. Then it makes a new
201 # checking if they should be added as largefiles. Then it makes a new
202 # matcher which matches only the normal files and runs the original
202 # matcher which matches only the normal files and runs the original
203 # version of add.
203 # version of add.
204 def overrideadd(orig, ui, repo, *pats, **opts):
204 def overrideadd(orig, ui, repo, *pats, **opts):
205 normal = opts.pop('normal')
205 normal = opts.pop('normal')
206 if normal:
206 if normal:
207 if opts.get('large'):
207 if opts.get('large'):
208 raise util.Abort(_('--normal cannot be used with --large'))
208 raise util.Abort(_('--normal cannot be used with --large'))
209 return orig(ui, repo, *pats, **opts)
209 return orig(ui, repo, *pats, **opts)
210 bad = addlargefiles(ui, repo, *pats, **opts)
210 bad = addlargefiles(ui, repo, *pats, **opts)
211 installnormalfilesmatchfn(repo[None].manifest())
211 installnormalfilesmatchfn(repo[None].manifest())
212 result = orig(ui, repo, *pats, **opts)
212 result = orig(ui, repo, *pats, **opts)
213 restorematchfn()
213 restorematchfn()
214
214
215 return (result == 1 or bad) and 1 or 0
215 return (result == 1 or bad) and 1 or 0
216
216
217 def overrideremove(orig, ui, repo, *pats, **opts):
217 def overrideremove(orig, ui, repo, *pats, **opts):
218 installnormalfilesmatchfn(repo[None].manifest())
218 installnormalfilesmatchfn(repo[None].manifest())
219 result = orig(ui, repo, *pats, **opts)
219 result = orig(ui, repo, *pats, **opts)
220 restorematchfn()
220 restorematchfn()
221 return removelargefiles(ui, repo, *pats, **opts) or result
221 return removelargefiles(ui, repo, *pats, **opts) or result
222
222
223 def overridestatusfn(orig, repo, rev2, **opts):
223 def overridestatusfn(orig, repo, rev2, **opts):
224 try:
224 try:
225 repo._repo.lfstatus = True
225 repo._repo.lfstatus = True
226 return orig(repo, rev2, **opts)
226 return orig(repo, rev2, **opts)
227 finally:
227 finally:
228 repo._repo.lfstatus = False
228 repo._repo.lfstatus = False
229
229
230 def overridestatus(orig, ui, repo, *pats, **opts):
230 def overridestatus(orig, ui, repo, *pats, **opts):
231 try:
231 try:
232 repo.lfstatus = True
232 repo.lfstatus = True
233 return orig(ui, repo, *pats, **opts)
233 return orig(ui, repo, *pats, **opts)
234 finally:
234 finally:
235 repo.lfstatus = False
235 repo.lfstatus = False
236
236
237 def overridedirty(orig, repo, ignoreupdate=False):
237 def overridedirty(orig, repo, ignoreupdate=False):
238 try:
238 try:
239 repo._repo.lfstatus = True
239 repo._repo.lfstatus = True
240 return orig(repo, ignoreupdate)
240 return orig(repo, ignoreupdate)
241 finally:
241 finally:
242 repo._repo.lfstatus = False
242 repo._repo.lfstatus = False
243
243
244 def overridelog(orig, ui, repo, *pats, **opts):
244 def overridelog(orig, ui, repo, *pats, **opts):
245 def overridematch(ctx, pats=[], opts={}, globbed=False,
245 def overridematch(ctx, pats=[], opts={}, globbed=False,
246 default='relpath'):
246 default='relpath'):
247 """Matcher that merges root directory with .hglf, suitable for log.
247 """Matcher that merges root directory with .hglf, suitable for log.
248 It is still possible to match .hglf directly.
248 It is still possible to match .hglf directly.
249 For any listed files run log on the standin too.
249 For any listed files run log on the standin too.
250 matchfn tries both the given filename and with .hglf stripped.
250 matchfn tries both the given filename and with .hglf stripped.
251 """
251 """
252 match = oldmatch(ctx, pats, opts, globbed, default)
252 match = oldmatch(ctx, pats, opts, globbed, default)
253 m = copy.copy(match)
253 m = copy.copy(match)
254 for i in range(0, len(m._files)):
254 for i in range(0, len(m._files)):
255 standin = lfutil.standin(m._files[i])
255 standin = lfutil.standin(m._files[i])
256 if standin in repo[ctx.node()]:
256 if standin in repo[ctx.node()]:
257 m._files[i] = standin
257 m._files[i] = standin
258 m._fmap = set(m._files)
258 m._fmap = set(m._files)
259 m._always = False
259 m._always = False
260 origmatchfn = m.matchfn
260 origmatchfn = m.matchfn
261 def lfmatchfn(f):
261 def lfmatchfn(f):
262 lf = lfutil.splitstandin(f)
262 lf = lfutil.splitstandin(f)
263 if lf is not None and origmatchfn(lf):
263 if lf is not None and origmatchfn(lf):
264 return True
264 return True
265 r = origmatchfn(f)
265 r = origmatchfn(f)
266 return r
266 return r
267 m.matchfn = lfmatchfn
267 m.matchfn = lfmatchfn
268 return m
268 return m
269 oldmatch = installmatchfn(overridematch)
269 oldmatch = installmatchfn(overridematch)
270 try:
270 try:
271 repo.lfstatus = True
271 repo.lfstatus = True
272 return orig(ui, repo, *pats, **opts)
272 return orig(ui, repo, *pats, **opts)
273 finally:
273 finally:
274 repo.lfstatus = False
274 repo.lfstatus = False
275 restorematchfn()
275 restorematchfn()
276
276
277 def overrideverify(orig, ui, repo, *pats, **opts):
277 def overrideverify(orig, ui, repo, *pats, **opts):
278 large = opts.pop('large', False)
278 large = opts.pop('large', False)
279 all = opts.pop('lfa', False)
279 all = opts.pop('lfa', False)
280 contents = opts.pop('lfc', False)
280 contents = opts.pop('lfc', False)
281
281
282 result = orig(ui, repo, *pats, **opts)
282 result = orig(ui, repo, *pats, **opts)
283 if large or all or contents:
283 if large or all or contents:
284 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
284 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
285 return result
285 return result
286
286
287 def overridedebugstate(orig, ui, repo, *pats, **opts):
287 def overridedebugstate(orig, ui, repo, *pats, **opts):
288 large = opts.pop('large', False)
288 large = opts.pop('large', False)
289 if large:
289 if large:
290 class fakerepo(object):
290 class fakerepo(object):
291 dirstate = lfutil.openlfdirstate(ui, repo)
291 dirstate = lfutil.openlfdirstate(ui, repo)
292 orig(ui, fakerepo, *pats, **opts)
292 orig(ui, fakerepo, *pats, **opts)
293 else:
293 else:
294 orig(ui, repo, *pats, **opts)
294 orig(ui, repo, *pats, **opts)
295
295
296 # Override needs to refresh standins so that update's normal merge
296 # Override needs to refresh standins so that update's normal merge
297 # will go through properly. Then the other update hook (overriding repo.update)
297 # will go through properly. Then the other update hook (overriding repo.update)
298 # will get the new files. Filemerge is also overridden so that the merge
298 # will get the new files. Filemerge is also overridden so that the merge
299 # will merge standins correctly.
299 # will merge standins correctly.
300 def overrideupdate(orig, ui, repo, *pats, **opts):
300 def overrideupdate(orig, ui, repo, *pats, **opts):
301 # Need to lock between the standins getting updated and their
301 # Need to lock between the standins getting updated and their
302 # largefiles getting updated
302 # largefiles getting updated
303 wlock = repo.wlock()
303 wlock = repo.wlock()
304 try:
304 try:
305 lfdirstate = lfutil.openlfdirstate(ui, repo)
305 lfdirstate = lfutil.openlfdirstate(ui, repo)
306 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()),
306 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()),
307 [], False, False, False)
307 [], False, False, False)
308 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
308 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
309
309
310 if opts['check']:
310 if opts['check']:
311 mod = len(modified) > 0
311 mod = len(modified) > 0
312 for lfile in unsure:
312 for lfile in unsure:
313 standin = lfutil.standin(lfile)
313 standin = lfutil.standin(lfile)
314 if repo['.'][standin].data().strip() != \
314 if repo['.'][standin].data().strip() != \
315 lfutil.hashfile(repo.wjoin(lfile)):
315 lfutil.hashfile(repo.wjoin(lfile)):
316 mod = True
316 mod = True
317 else:
317 else:
318 lfdirstate.normal(lfile)
318 lfdirstate.normal(lfile)
319 lfdirstate.write()
319 lfdirstate.write()
320 if mod:
320 if mod:
321 raise util.Abort(_('uncommitted changes'))
321 raise util.Abort(_('uncommitted changes'))
322 # XXX handle removed differently
322 # XXX handle removed differently
323 if not opts['clean']:
323 if not opts['clean']:
324 for lfile in unsure + modified + added:
324 for lfile in unsure + modified + added:
325 lfutil.updatestandin(repo, lfutil.standin(lfile))
325 lfutil.updatestandin(repo, lfutil.standin(lfile))
326 return orig(ui, repo, *pats, **opts)
326 return orig(ui, repo, *pats, **opts)
327 finally:
327 finally:
328 wlock.release()
328 wlock.release()
329
329
330 # Before starting the manifest merge, merge.updates will call
330 # Before starting the manifest merge, merge.updates will call
331 # _checkunknown to check if there are any files in the merged-in
331 # _checkunknown to check if there are any files in the merged-in
332 # changeset that collide with unknown files in the working copy.
332 # changeset that collide with unknown files in the working copy.
333 #
333 #
334 # The largefiles are seen as unknown, so this prevents us from merging
334 # The largefiles are seen as unknown, so this prevents us from merging
335 # in a file 'foo' if we already have a largefile with the same name.
335 # in a file 'foo' if we already have a largefile with the same name.
336 #
336 #
337 # The overridden function filters the unknown files by removing any
337 # The overridden function filters the unknown files by removing any
338 # largefiles. This makes the merge proceed and we can then handle this
338 # largefiles. This makes the merge proceed and we can then handle this
339 # case further in the overridden manifestmerge function below.
339 # case further in the overridden manifestmerge function below.
340 def overridecheckunknownfile(origfn, repo, wctx, mctx, f):
340 def overridecheckunknownfile(origfn, repo, wctx, mctx, f):
341 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
341 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
342 return False
342 return False
343 return origfn(repo, wctx, mctx, f)
343 return origfn(repo, wctx, mctx, f)
344
344
345 # The manifest merge handles conflicts on the manifest level. We want
345 # The manifest merge handles conflicts on the manifest level. We want
346 # to handle changes in largefile-ness of files at this level too.
346 # to handle changes in largefile-ness of files at this level too.
347 #
347 #
348 # The strategy is to run the original manifestmerge and then process
348 # The strategy is to run the original manifestmerge and then process
349 # the action list it outputs. There are two cases we need to deal with:
349 # the action list it outputs. There are two cases we need to deal with:
350 #
350 #
351 # 1. Normal file in p1, largefile in p2. Here the largefile is
351 # 1. Normal file in p1, largefile in p2. Here the largefile is
352 # detected via its standin file, which will enter the working copy
352 # detected via its standin file, which will enter the working copy
353 # with a "get" action. It is not "merge" since the standin is all
353 # with a "get" action. It is not "merge" since the standin is all
354 # Mercurial is concerned with at this level -- the link to the
354 # Mercurial is concerned with at this level -- the link to the
355 # existing normal file is not relevant here.
355 # existing normal file is not relevant here.
356 #
356 #
357 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
357 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
358 # since the largefile will be present in the working copy and
358 # since the largefile will be present in the working copy and
359 # different from the normal file in p2. Mercurial therefore
359 # different from the normal file in p2. Mercurial therefore
360 # triggers a merge action.
360 # triggers a merge action.
361 #
361 #
362 # In both cases, we prompt the user and emit new actions to either
362 # In both cases, we prompt the user and emit new actions to either
363 # remove the standin (if the normal file was kept) or to remove the
363 # remove the standin (if the normal file was kept) or to remove the
364 # normal file and get the standin (if the largefile was kept). The
364 # normal file and get the standin (if the largefile was kept). The
365 # default prompt answer is to use the largefile version since it was
365 # default prompt answer is to use the largefile version since it was
366 # presumably changed on purpose.
366 # presumably changed on purpose.
367 #
367 #
368 # Finally, the merge.applyupdates function will then take care of
368 # Finally, the merge.applyupdates function will then take care of
369 # writing the files into the working copy and lfcommands.updatelfiles
369 # writing the files into the working copy and lfcommands.updatelfiles
370 # will update the largefiles.
370 # will update the largefiles.
371 def overridecalculateupdates(origfn, repo, p1, p2, pas, branchmerge, force,
371 def overridecalculateupdates(origfn, repo, p1, p2, pas, branchmerge, force,
372 partial, acceptremote, followcopies):
372 partial, acceptremote, followcopies):
373 overwrite = force and not branchmerge
373 overwrite = force and not branchmerge
374 actions = origfn(repo, p1, p2, pas, branchmerge, force, partial,
374 actions = origfn(repo, p1, p2, pas, branchmerge, force, partial,
375 acceptremote, followcopies)
375 acceptremote, followcopies)
376
376
377 if overwrite:
377 if overwrite:
378 return actions
378 return actions
379
379
380 removes = set(a[0] for a in actions if a[1] == 'r')
380 removes = set(a[0] for a in actions if a[1] == 'r')
381 processed = []
381 processed = []
382
382
383 for action in actions:
383 for action in actions:
384 f, m, args, msg = action
384 f, m, args, msg = action
385
385
386 splitstandin = f and lfutil.splitstandin(f)
386 splitstandin = f and lfutil.splitstandin(f)
387 if (m == "g" and splitstandin is not None and
387 if (m == "g" and splitstandin is not None and
388 splitstandin in p1 and splitstandin not in removes):
388 splitstandin in p1 and splitstandin not in removes):
389 # Case 1: normal file in the working copy, largefile in
389 # Case 1: normal file in the working copy, largefile in
390 # the second parent
390 # the second parent
391 lfile = splitstandin
391 lfile = splitstandin
392 standin = f
392 standin = f
393 msg = _('remote turned local normal file %s into a largefile\n'
393 msg = _('remote turned local normal file %s into a largefile\n'
394 'use (l)argefile or keep (n)ormal file?'
394 'use (l)argefile or keep (n)ormal file?'
395 '$$ &Largefile $$ &Normal file') % lfile
395 '$$ &Largefile $$ &Normal file') % lfile
396 if repo.ui.promptchoice(msg, 0) == 0:
396 if repo.ui.promptchoice(msg, 0) == 0:
397 processed.append((lfile, "r", None, msg))
397 processed.append((lfile, "r", None, msg))
398 processed.append((standin, "g", (p2.flags(standin),), msg))
398 processed.append((standin, "g", (p2.flags(standin),), msg))
399 else:
399 else:
400 processed.append((standin, "r", None, msg))
400 processed.append((standin, "r", None, msg))
401 elif (m == "g" and
401 elif (m == "g" and
402 lfutil.standin(f) in p1 and lfutil.standin(f) not in removes):
402 lfutil.standin(f) in p1 and lfutil.standin(f) not in removes):
403 # Case 2: largefile in the working copy, normal file in
403 # Case 2: largefile in the working copy, normal file in
404 # the second parent
404 # the second parent
405 standin = lfutil.standin(f)
405 standin = lfutil.standin(f)
406 lfile = f
406 lfile = f
407 msg = _('remote turned local largefile %s into a normal file\n'
407 msg = _('remote turned local largefile %s into a normal file\n'
408 'keep (l)argefile or use (n)ormal file?'
408 'keep (l)argefile or use (n)ormal file?'
409 '$$ &Largefile $$ &Normal file') % lfile
409 '$$ &Largefile $$ &Normal file') % lfile
410 if repo.ui.promptchoice(msg, 0) == 0:
410 if repo.ui.promptchoice(msg, 0) == 0:
411 processed.append((lfile, "r", None, msg))
411 processed.append((lfile, "r", None, msg))
412 else:
412 else:
413 processed.append((standin, "r", None, msg))
413 processed.append((standin, "r", None, msg))
414 processed.append((lfile, "g", (p2.flags(lfile),), msg))
414 processed.append((lfile, "g", (p2.flags(lfile),), msg))
415 else:
415 else:
416 processed.append(action)
416 processed.append(action)
417
417
418 return processed
418 return processed
419
419
420 # Override filemerge to prompt the user about how they wish to merge
420 # Override filemerge to prompt the user about how they wish to merge
421 # largefiles. This will handle identical edits without prompting the user.
421 # largefiles. This will handle identical edits without prompting the user.
422 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca):
422 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca):
423 if not lfutil.isstandin(orig):
423 if not lfutil.isstandin(orig):
424 return origfn(repo, mynode, orig, fcd, fco, fca)
424 return origfn(repo, mynode, orig, fcd, fco, fca)
425
425
426 ahash = fca.data().strip().lower()
426 ahash = fca.data().strip().lower()
427 dhash = fcd.data().strip().lower()
427 dhash = fcd.data().strip().lower()
428 ohash = fco.data().strip().lower()
428 ohash = fco.data().strip().lower()
429 if (ohash != ahash and
429 if (ohash != ahash and
430 ohash != dhash and
430 ohash != dhash and
431 (dhash == ahash or
431 (dhash == ahash or
432 repo.ui.promptchoice(
432 repo.ui.promptchoice(
433 _('largefile %s has a merge conflict\nancestor was %s\n'
433 _('largefile %s has a merge conflict\nancestor was %s\n'
434 'keep (l)ocal %s or\ntake (o)ther %s?'
434 'keep (l)ocal %s or\ntake (o)ther %s?'
435 '$$ &Local $$ &Other') %
435 '$$ &Local $$ &Other') %
436 (lfutil.splitstandin(orig), ahash, dhash, ohash),
436 (lfutil.splitstandin(orig), ahash, dhash, ohash),
437 0) == 1)):
437 0) == 1)):
438 repo.wwrite(fcd.path(), fco.data(), fco.flags())
438 repo.wwrite(fcd.path(), fco.data(), fco.flags())
439 return 0
439 return 0
440
440
441 # Copy first changes the matchers to match standins instead of
441 # Copy first changes the matchers to match standins instead of
442 # largefiles. Then it overrides util.copyfile in that function it
442 # largefiles. Then it overrides util.copyfile in that function it
443 # checks if the destination largefile already exists. It also keeps a
443 # checks if the destination largefile already exists. It also keeps a
444 # list of copied files so that the largefiles can be copied and the
444 # list of copied files so that the largefiles can be copied and the
445 # dirstate updated.
445 # dirstate updated.
446 def overridecopy(orig, ui, repo, pats, opts, rename=False):
446 def overridecopy(orig, ui, repo, pats, opts, rename=False):
447 # doesn't remove largefile on rename
447 # doesn't remove largefile on rename
448 if len(pats) < 2:
448 if len(pats) < 2:
449 # this isn't legal, let the original function deal with it
449 # this isn't legal, let the original function deal with it
450 return orig(ui, repo, pats, opts, rename)
450 return orig(ui, repo, pats, opts, rename)
451
451
452 def makestandin(relpath):
452 def makestandin(relpath):
453 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
453 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
454 return os.path.join(repo.wjoin(lfutil.standin(path)))
454 return os.path.join(repo.wjoin(lfutil.standin(path)))
455
455
456 fullpats = scmutil.expandpats(pats)
456 fullpats = scmutil.expandpats(pats)
457 dest = fullpats[-1]
457 dest = fullpats[-1]
458
458
459 if os.path.isdir(dest):
459 if os.path.isdir(dest):
460 if not os.path.isdir(makestandin(dest)):
460 if not os.path.isdir(makestandin(dest)):
461 os.makedirs(makestandin(dest))
461 os.makedirs(makestandin(dest))
462 # This could copy both lfiles and normal files in one command,
462 # This could copy both lfiles and normal files in one command,
463 # but we don't want to do that. First replace their matcher to
463 # but we don't want to do that. First replace their matcher to
464 # only match normal files and run it, then replace it to just
464 # only match normal files and run it, then replace it to just
465 # match largefiles and run it again.
465 # match largefiles and run it again.
466 nonormalfiles = False
466 nonormalfiles = False
467 nolfiles = False
467 nolfiles = False
468 installnormalfilesmatchfn(repo[None].manifest())
468 try:
469 try:
469 try:
470 try:
470 installnormalfilesmatchfn(repo[None].manifest())
471 result = orig(ui, repo, pats, opts, rename)
471 result = orig(ui, repo, pats, opts, rename)
472 except util.Abort, e:
472 except util.Abort, e:
473 if str(e) != _('no files to copy'):
473 if str(e) != _('no files to copy'):
474 raise e
474 raise e
475 else:
475 else:
476 nonormalfiles = True
476 nonormalfiles = True
477 result = 0
477 result = 0
478 finally:
478 finally:
479 restorematchfn()
479 restorematchfn()
480
480
481 # The first rename can cause our current working directory to be removed.
481 # The first rename can cause our current working directory to be removed.
482 # In that case there is nothing left to copy/rename so just quit.
482 # In that case there is nothing left to copy/rename so just quit.
483 try:
483 try:
484 repo.getcwd()
484 repo.getcwd()
485 except OSError:
485 except OSError:
486 return result
486 return result
487
487
488 try:
488 try:
489 try:
489 try:
490 # When we call orig below it creates the standins but we don't add
490 # When we call orig below it creates the standins but we don't add
491 # them to the dir state until later so lock during that time.
491 # them to the dir state until later so lock during that time.
492 wlock = repo.wlock()
492 wlock = repo.wlock()
493
493
494 manifest = repo[None].manifest()
494 manifest = repo[None].manifest()
495 oldmatch = None # for the closure
495 oldmatch = None # for the closure
496 def overridematch(ctx, pats=[], opts={}, globbed=False,
496 def overridematch(ctx, pats=[], opts={}, globbed=False,
497 default='relpath'):
497 default='relpath'):
498 newpats = []
498 newpats = []
499 # The patterns were previously mangled to add the standin
499 # The patterns were previously mangled to add the standin
500 # directory; we need to remove that now
500 # directory; we need to remove that now
501 for pat in pats:
501 for pat in pats:
502 if match_.patkind(pat) is None and lfutil.shortname in pat:
502 if match_.patkind(pat) is None and lfutil.shortname in pat:
503 newpats.append(pat.replace(lfutil.shortname, ''))
503 newpats.append(pat.replace(lfutil.shortname, ''))
504 else:
504 else:
505 newpats.append(pat)
505 newpats.append(pat)
506 match = oldmatch(ctx, newpats, opts, globbed, default)
506 match = oldmatch(ctx, newpats, opts, globbed, default)
507 m = copy.copy(match)
507 m = copy.copy(match)
508 lfile = lambda f: lfutil.standin(f) in manifest
508 lfile = lambda f: lfutil.standin(f) in manifest
509 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
509 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
510 m._fmap = set(m._files)
510 m._fmap = set(m._files)
511 m._always = False
511 m._always = False
512 origmatchfn = m.matchfn
512 origmatchfn = m.matchfn
513 m.matchfn = lambda f: (lfutil.isstandin(f) and
513 m.matchfn = lambda f: (lfutil.isstandin(f) and
514 (f in manifest) and
514 (f in manifest) and
515 origmatchfn(lfutil.splitstandin(f)) or
515 origmatchfn(lfutil.splitstandin(f)) or
516 None)
516 None)
517 return m
517 return m
518 oldmatch = installmatchfn(overridematch)
518 oldmatch = installmatchfn(overridematch)
519 listpats = []
519 listpats = []
520 for pat in pats:
520 for pat in pats:
521 if match_.patkind(pat) is not None:
521 if match_.patkind(pat) is not None:
522 listpats.append(pat)
522 listpats.append(pat)
523 else:
523 else:
524 listpats.append(makestandin(pat))
524 listpats.append(makestandin(pat))
525
525
526 try:
526 try:
527 origcopyfile = util.copyfile
527 origcopyfile = util.copyfile
528 copiedfiles = []
528 copiedfiles = []
529 def overridecopyfile(src, dest):
529 def overridecopyfile(src, dest):
530 if (lfutil.shortname in src and
530 if (lfutil.shortname in src and
531 dest.startswith(repo.wjoin(lfutil.shortname))):
531 dest.startswith(repo.wjoin(lfutil.shortname))):
532 destlfile = dest.replace(lfutil.shortname, '')
532 destlfile = dest.replace(lfutil.shortname, '')
533 if not opts['force'] and os.path.exists(destlfile):
533 if not opts['force'] and os.path.exists(destlfile):
534 raise IOError('',
534 raise IOError('',
535 _('destination largefile already exists'))
535 _('destination largefile already exists'))
536 copiedfiles.append((src, dest))
536 copiedfiles.append((src, dest))
537 origcopyfile(src, dest)
537 origcopyfile(src, dest)
538
538
539 util.copyfile = overridecopyfile
539 util.copyfile = overridecopyfile
540 result += orig(ui, repo, listpats, opts, rename)
540 result += orig(ui, repo, listpats, opts, rename)
541 finally:
541 finally:
542 util.copyfile = origcopyfile
542 util.copyfile = origcopyfile
543
543
544 lfdirstate = lfutil.openlfdirstate(ui, repo)
544 lfdirstate = lfutil.openlfdirstate(ui, repo)
545 for (src, dest) in copiedfiles:
545 for (src, dest) in copiedfiles:
546 if (lfutil.shortname in src and
546 if (lfutil.shortname in src and
547 dest.startswith(repo.wjoin(lfutil.shortname))):
547 dest.startswith(repo.wjoin(lfutil.shortname))):
548 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
548 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
549 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
549 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
550 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
550 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
551 if not os.path.isdir(destlfiledir):
551 if not os.path.isdir(destlfiledir):
552 os.makedirs(destlfiledir)
552 os.makedirs(destlfiledir)
553 if rename:
553 if rename:
554 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
554 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
555 lfdirstate.remove(srclfile)
555 lfdirstate.remove(srclfile)
556 else:
556 else:
557 util.copyfile(repo.wjoin(srclfile),
557 util.copyfile(repo.wjoin(srclfile),
558 repo.wjoin(destlfile))
558 repo.wjoin(destlfile))
559
559
560 lfdirstate.add(destlfile)
560 lfdirstate.add(destlfile)
561 lfdirstate.write()
561 lfdirstate.write()
562 except util.Abort, e:
562 except util.Abort, e:
563 if str(e) != _('no files to copy'):
563 if str(e) != _('no files to copy'):
564 raise e
564 raise e
565 else:
565 else:
566 nolfiles = True
566 nolfiles = True
567 finally:
567 finally:
568 restorematchfn()
568 restorematchfn()
569 wlock.release()
569 wlock.release()
570
570
571 if nolfiles and nonormalfiles:
571 if nolfiles and nonormalfiles:
572 raise util.Abort(_('no files to copy'))
572 raise util.Abort(_('no files to copy'))
573
573
574 return result
574 return result
575
575
576 # When the user calls revert, we have to be careful to not revert any
576 # When the user calls revert, we have to be careful to not revert any
577 # changes to other largefiles accidentally. This means we have to keep
577 # changes to other largefiles accidentally. This means we have to keep
578 # track of the largefiles that are being reverted so we only pull down
578 # track of the largefiles that are being reverted so we only pull down
579 # the necessary largefiles.
579 # the necessary largefiles.
580 #
580 #
581 # Standins are only updated (to match the hash of largefiles) before
581 # Standins are only updated (to match the hash of largefiles) before
582 # commits. Update the standins then run the original revert, changing
582 # commits. Update the standins then run the original revert, changing
583 # the matcher to hit standins instead of largefiles. Based on the
583 # the matcher to hit standins instead of largefiles. Based on the
584 # resulting standins update the largefiles. Then return the standins
584 # resulting standins update the largefiles. Then return the standins
585 # to their proper state
585 # to their proper state
586 def overriderevert(orig, ui, repo, *pats, **opts):
586 def overriderevert(orig, ui, repo, *pats, **opts):
587 # Because we put the standins in a bad state (by updating them)
587 # Because we put the standins in a bad state (by updating them)
588 # and then return them to a correct state we need to lock to
588 # and then return them to a correct state we need to lock to
589 # prevent others from changing them in their incorrect state.
589 # prevent others from changing them in their incorrect state.
590 wlock = repo.wlock()
590 wlock = repo.wlock()
591 try:
591 try:
592 lfdirstate = lfutil.openlfdirstate(ui, repo)
592 lfdirstate = lfutil.openlfdirstate(ui, repo)
593 (modified, added, removed, missing, unknown, ignored, clean) = \
593 (modified, added, removed, missing, unknown, ignored, clean) = \
594 lfutil.lfdirstatestatus(lfdirstate, repo, repo['.'].rev())
594 lfutil.lfdirstatestatus(lfdirstate, repo, repo['.'].rev())
595 lfdirstate.write()
595 lfdirstate.write()
596 for lfile in modified:
596 for lfile in modified:
597 lfutil.updatestandin(repo, lfutil.standin(lfile))
597 lfutil.updatestandin(repo, lfutil.standin(lfile))
598 for lfile in missing:
598 for lfile in missing:
599 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
599 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
600 os.unlink(repo.wjoin(lfutil.standin(lfile)))
600 os.unlink(repo.wjoin(lfutil.standin(lfile)))
601
601
602 try:
602 try:
603 ctx = scmutil.revsingle(repo, opts.get('rev'))
603 ctx = scmutil.revsingle(repo, opts.get('rev'))
604 oldmatch = None # for the closure
604 oldmatch = None # for the closure
605 def overridematch(ctx, pats=[], opts={}, globbed=False,
605 def overridematch(ctx, pats=[], opts={}, globbed=False,
606 default='relpath'):
606 default='relpath'):
607 match = oldmatch(ctx, pats, opts, globbed, default)
607 match = oldmatch(ctx, pats, opts, globbed, default)
608 m = copy.copy(match)
608 m = copy.copy(match)
609 def tostandin(f):
609 def tostandin(f):
610 if lfutil.standin(f) in ctx:
610 if lfutil.standin(f) in ctx:
611 return lfutil.standin(f)
611 return lfutil.standin(f)
612 elif lfutil.standin(f) in repo[None]:
612 elif lfutil.standin(f) in repo[None]:
613 return None
613 return None
614 return f
614 return f
615 m._files = [tostandin(f) for f in m._files]
615 m._files = [tostandin(f) for f in m._files]
616 m._files = [f for f in m._files if f is not None]
616 m._files = [f for f in m._files if f is not None]
617 m._fmap = set(m._files)
617 m._fmap = set(m._files)
618 m._always = False
618 m._always = False
619 origmatchfn = m.matchfn
619 origmatchfn = m.matchfn
620 def matchfn(f):
620 def matchfn(f):
621 if lfutil.isstandin(f):
621 if lfutil.isstandin(f):
622 # We need to keep track of what largefiles are being
622 # We need to keep track of what largefiles are being
623 # matched so we know which ones to update later --
623 # matched so we know which ones to update later --
624 # otherwise we accidentally revert changes to other
624 # otherwise we accidentally revert changes to other
625 # largefiles. This is repo-specific, so duckpunch the
625 # largefiles. This is repo-specific, so duckpunch the
626 # repo object to keep the list of largefiles for us
626 # repo object to keep the list of largefiles for us
627 # later.
627 # later.
628 if origmatchfn(lfutil.splitstandin(f)) and \
628 if origmatchfn(lfutil.splitstandin(f)) and \
629 (f in repo[None] or f in ctx):
629 (f in repo[None] or f in ctx):
630 lfileslist = getattr(repo, '_lfilestoupdate', [])
630 lfileslist = getattr(repo, '_lfilestoupdate', [])
631 lfileslist.append(lfutil.splitstandin(f))
631 lfileslist.append(lfutil.splitstandin(f))
632 repo._lfilestoupdate = lfileslist
632 repo._lfilestoupdate = lfileslist
633 return True
633 return True
634 else:
634 else:
635 return False
635 return False
636 return origmatchfn(f)
636 return origmatchfn(f)
637 m.matchfn = matchfn
637 m.matchfn = matchfn
638 return m
638 return m
639 oldmatch = installmatchfn(overridematch)
639 oldmatch = installmatchfn(overridematch)
640 scmutil.match
640 scmutil.match
641 matches = overridematch(repo[None], pats, opts)
641 matches = overridematch(repo[None], pats, opts)
642 orig(ui, repo, *pats, **opts)
642 orig(ui, repo, *pats, **opts)
643 finally:
643 finally:
644 restorematchfn()
644 restorematchfn()
645 lfileslist = getattr(repo, '_lfilestoupdate', [])
645 lfileslist = getattr(repo, '_lfilestoupdate', [])
646 lfcommands.updatelfiles(ui, repo, filelist=lfileslist,
646 lfcommands.updatelfiles(ui, repo, filelist=lfileslist,
647 printmessage=False)
647 printmessage=False)
648
648
649 # empty out the largefiles list so we start fresh next time
649 # empty out the largefiles list so we start fresh next time
650 repo._lfilestoupdate = []
650 repo._lfilestoupdate = []
651 for lfile in modified:
651 for lfile in modified:
652 if lfile in lfileslist:
652 if lfile in lfileslist:
653 if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\
653 if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\
654 in repo['.']:
654 in repo['.']:
655 lfutil.writestandin(repo, lfutil.standin(lfile),
655 lfutil.writestandin(repo, lfutil.standin(lfile),
656 repo['.'][lfile].data().strip(),
656 repo['.'][lfile].data().strip(),
657 'x' in repo['.'][lfile].flags())
657 'x' in repo['.'][lfile].flags())
658 lfdirstate = lfutil.openlfdirstate(ui, repo)
658 lfdirstate = lfutil.openlfdirstate(ui, repo)
659 for lfile in added:
659 for lfile in added:
660 standin = lfutil.standin(lfile)
660 standin = lfutil.standin(lfile)
661 if standin not in ctx and (standin in matches or opts.get('all')):
661 if standin not in ctx and (standin in matches or opts.get('all')):
662 if lfile in lfdirstate:
662 if lfile in lfdirstate:
663 lfdirstate.drop(lfile)
663 lfdirstate.drop(lfile)
664 util.unlinkpath(repo.wjoin(standin))
664 util.unlinkpath(repo.wjoin(standin))
665 lfdirstate.write()
665 lfdirstate.write()
666 finally:
666 finally:
667 wlock.release()
667 wlock.release()
668
668
669 def hgupdaterepo(orig, repo, node, overwrite):
669 def hgupdaterepo(orig, repo, node, overwrite):
670 if not overwrite:
670 if not overwrite:
671 # Only call updatelfiles on the standins that have changed to save time
671 # Only call updatelfiles on the standins that have changed to save time
672 oldstandins = lfutil.getstandinsstate(repo)
672 oldstandins = lfutil.getstandinsstate(repo)
673
673
674 result = orig(repo, node, overwrite)
674 result = orig(repo, node, overwrite)
675
675
676 filelist = None
676 filelist = None
677 if not overwrite:
677 if not overwrite:
678 newstandins = lfutil.getstandinsstate(repo)
678 newstandins = lfutil.getstandinsstate(repo)
679 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
679 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
680 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist)
680 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist)
681 return result
681 return result
682
682
683 def hgmerge(orig, repo, node, force=None, remind=True):
683 def hgmerge(orig, repo, node, force=None, remind=True):
684 result = orig(repo, node, force, remind)
684 result = orig(repo, node, force, remind)
685 lfcommands.updatelfiles(repo.ui, repo)
685 lfcommands.updatelfiles(repo.ui, repo)
686 return result
686 return result
687
687
688 # When we rebase a repository with remotely changed largefiles, we need to
688 # When we rebase a repository with remotely changed largefiles, we need to
689 # take some extra care so that the largefiles are correctly updated in the
689 # take some extra care so that the largefiles are correctly updated in the
690 # working copy
690 # working copy
691 def overridepull(orig, ui, repo, source=None, **opts):
691 def overridepull(orig, ui, repo, source=None, **opts):
692 revsprepull = len(repo)
692 revsprepull = len(repo)
693 if not source:
693 if not source:
694 source = 'default'
694 source = 'default'
695 repo.lfpullsource = source
695 repo.lfpullsource = source
696 if opts.get('rebase', False):
696 if opts.get('rebase', False):
697 repo._isrebasing = True
697 repo._isrebasing = True
698 try:
698 try:
699 if opts.get('update'):
699 if opts.get('update'):
700 del opts['update']
700 del opts['update']
701 ui.debug('--update and --rebase are not compatible, ignoring '
701 ui.debug('--update and --rebase are not compatible, ignoring '
702 'the update flag\n')
702 'the update flag\n')
703 del opts['rebase']
703 del opts['rebase']
704 origpostincoming = commands.postincoming
704 origpostincoming = commands.postincoming
705 def _dummy(*args, **kwargs):
705 def _dummy(*args, **kwargs):
706 pass
706 pass
707 commands.postincoming = _dummy
707 commands.postincoming = _dummy
708 try:
708 try:
709 result = commands.pull(ui, repo, source, **opts)
709 result = commands.pull(ui, repo, source, **opts)
710 finally:
710 finally:
711 commands.postincoming = origpostincoming
711 commands.postincoming = origpostincoming
712 revspostpull = len(repo)
712 revspostpull = len(repo)
713 if revspostpull > revsprepull:
713 if revspostpull > revsprepull:
714 result = result or rebase.rebase(ui, repo)
714 result = result or rebase.rebase(ui, repo)
715 finally:
715 finally:
716 repo._isrebasing = False
716 repo._isrebasing = False
717 else:
717 else:
718 result = orig(ui, repo, source, **opts)
718 result = orig(ui, repo, source, **opts)
719 revspostpull = len(repo)
719 revspostpull = len(repo)
720 lfrevs = opts.get('lfrev', [])
720 lfrevs = opts.get('lfrev', [])
721 if opts.get('all_largefiles'):
721 if opts.get('all_largefiles'):
722 lfrevs.append('pulled()')
722 lfrevs.append('pulled()')
723 if lfrevs and revspostpull > revsprepull:
723 if lfrevs and revspostpull > revsprepull:
724 numcached = 0
724 numcached = 0
725 repo.firstpulled = revsprepull # for pulled() revset expression
725 repo.firstpulled = revsprepull # for pulled() revset expression
726 try:
726 try:
727 for rev in scmutil.revrange(repo, lfrevs):
727 for rev in scmutil.revrange(repo, lfrevs):
728 ui.note(_('pulling largefiles for revision %s\n') % rev)
728 ui.note(_('pulling largefiles for revision %s\n') % rev)
729 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
729 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
730 numcached += len(cached)
730 numcached += len(cached)
731 finally:
731 finally:
732 del repo.firstpulled
732 del repo.firstpulled
733 ui.status(_("%d largefiles cached\n") % numcached)
733 ui.status(_("%d largefiles cached\n") % numcached)
734 return result
734 return result
735
735
736 def pulledrevsetsymbol(repo, subset, x):
736 def pulledrevsetsymbol(repo, subset, x):
737 """``pulled()``
737 """``pulled()``
738 Changesets that just has been pulled.
738 Changesets that just has been pulled.
739
739
740 Only available with largefiles from pull --lfrev expressions.
740 Only available with largefiles from pull --lfrev expressions.
741
741
742 .. container:: verbose
742 .. container:: verbose
743
743
744 Some examples:
744 Some examples:
745
745
746 - pull largefiles for all new changesets::
746 - pull largefiles for all new changesets::
747
747
748 hg pull -lfrev "pulled()"
748 hg pull -lfrev "pulled()"
749
749
750 - pull largefiles for all new branch heads::
750 - pull largefiles for all new branch heads::
751
751
752 hg pull -lfrev "head(pulled()) and not closed()"
752 hg pull -lfrev "head(pulled()) and not closed()"
753
753
754 """
754 """
755
755
756 try:
756 try:
757 firstpulled = repo.firstpulled
757 firstpulled = repo.firstpulled
758 except AttributeError:
758 except AttributeError:
759 raise util.Abort(_("pulled() only available in --lfrev"))
759 raise util.Abort(_("pulled() only available in --lfrev"))
760 return revset.baseset([r for r in subset if r >= firstpulled])
760 return revset.baseset([r for r in subset if r >= firstpulled])
761
761
762 def overrideclone(orig, ui, source, dest=None, **opts):
762 def overrideclone(orig, ui, source, dest=None, **opts):
763 d = dest
763 d = dest
764 if d is None:
764 if d is None:
765 d = hg.defaultdest(source)
765 d = hg.defaultdest(source)
766 if opts.get('all_largefiles') and not hg.islocal(d):
766 if opts.get('all_largefiles') and not hg.islocal(d):
767 raise util.Abort(_(
767 raise util.Abort(_(
768 '--all-largefiles is incompatible with non-local destination %s' %
768 '--all-largefiles is incompatible with non-local destination %s' %
769 d))
769 d))
770
770
771 return orig(ui, source, dest, **opts)
771 return orig(ui, source, dest, **opts)
772
772
773 def hgclone(orig, ui, opts, *args, **kwargs):
773 def hgclone(orig, ui, opts, *args, **kwargs):
774 result = orig(ui, opts, *args, **kwargs)
774 result = orig(ui, opts, *args, **kwargs)
775
775
776 if result is not None:
776 if result is not None:
777 sourcerepo, destrepo = result
777 sourcerepo, destrepo = result
778 repo = destrepo.local()
778 repo = destrepo.local()
779
779
780 # Caching is implicitly limited to 'rev' option, since the dest repo was
780 # Caching is implicitly limited to 'rev' option, since the dest repo was
781 # truncated at that point. The user may expect a download count with
781 # truncated at that point. The user may expect a download count with
782 # this option, so attempt whether or not this is a largefile repo.
782 # this option, so attempt whether or not this is a largefile repo.
783 if opts.get('all_largefiles'):
783 if opts.get('all_largefiles'):
784 success, missing = lfcommands.downloadlfiles(ui, repo, None)
784 success, missing = lfcommands.downloadlfiles(ui, repo, None)
785
785
786 if missing != 0:
786 if missing != 0:
787 return None
787 return None
788
788
789 return result
789 return result
790
790
791 def overriderebase(orig, ui, repo, **opts):
791 def overriderebase(orig, ui, repo, **opts):
792 repo._isrebasing = True
792 repo._isrebasing = True
793 try:
793 try:
794 return orig(ui, repo, **opts)
794 return orig(ui, repo, **opts)
795 finally:
795 finally:
796 repo._isrebasing = False
796 repo._isrebasing = False
797
797
798 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
798 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
799 prefix=None, mtime=None, subrepos=None):
799 prefix=None, mtime=None, subrepos=None):
800 # No need to lock because we are only reading history and
800 # No need to lock because we are only reading history and
801 # largefile caches, neither of which are modified.
801 # largefile caches, neither of which are modified.
802 lfcommands.cachelfiles(repo.ui, repo, node)
802 lfcommands.cachelfiles(repo.ui, repo, node)
803
803
804 if kind not in archival.archivers:
804 if kind not in archival.archivers:
805 raise util.Abort(_("unknown archive type '%s'") % kind)
805 raise util.Abort(_("unknown archive type '%s'") % kind)
806
806
807 ctx = repo[node]
807 ctx = repo[node]
808
808
809 if kind == 'files':
809 if kind == 'files':
810 if prefix:
810 if prefix:
811 raise util.Abort(
811 raise util.Abort(
812 _('cannot give prefix when archiving to files'))
812 _('cannot give prefix when archiving to files'))
813 else:
813 else:
814 prefix = archival.tidyprefix(dest, kind, prefix)
814 prefix = archival.tidyprefix(dest, kind, prefix)
815
815
816 def write(name, mode, islink, getdata):
816 def write(name, mode, islink, getdata):
817 if matchfn and not matchfn(name):
817 if matchfn and not matchfn(name):
818 return
818 return
819 data = getdata()
819 data = getdata()
820 if decode:
820 if decode:
821 data = repo.wwritedata(name, data)
821 data = repo.wwritedata(name, data)
822 archiver.addfile(prefix + name, mode, islink, data)
822 archiver.addfile(prefix + name, mode, islink, data)
823
823
824 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
824 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
825
825
826 if repo.ui.configbool("ui", "archivemeta", True):
826 if repo.ui.configbool("ui", "archivemeta", True):
827 def metadata():
827 def metadata():
828 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
828 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
829 hex(repo.changelog.node(0)), hex(node), ctx.branch())
829 hex(repo.changelog.node(0)), hex(node), ctx.branch())
830
830
831 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
831 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
832 if repo.tagtype(t) == 'global')
832 if repo.tagtype(t) == 'global')
833 if not tags:
833 if not tags:
834 repo.ui.pushbuffer()
834 repo.ui.pushbuffer()
835 opts = {'template': '{latesttag}\n{latesttagdistance}',
835 opts = {'template': '{latesttag}\n{latesttagdistance}',
836 'style': '', 'patch': None, 'git': None}
836 'style': '', 'patch': None, 'git': None}
837 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
837 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
838 ltags, dist = repo.ui.popbuffer().split('\n')
838 ltags, dist = repo.ui.popbuffer().split('\n')
839 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
839 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
840 tags += 'latesttagdistance: %s\n' % dist
840 tags += 'latesttagdistance: %s\n' % dist
841
841
842 return base + tags
842 return base + tags
843
843
844 write('.hg_archival.txt', 0644, False, metadata)
844 write('.hg_archival.txt', 0644, False, metadata)
845
845
846 for f in ctx:
846 for f in ctx:
847 ff = ctx.flags(f)
847 ff = ctx.flags(f)
848 getdata = ctx[f].data
848 getdata = ctx[f].data
849 if lfutil.isstandin(f):
849 if lfutil.isstandin(f):
850 path = lfutil.findfile(repo, getdata().strip())
850 path = lfutil.findfile(repo, getdata().strip())
851 if path is None:
851 if path is None:
852 raise util.Abort(
852 raise util.Abort(
853 _('largefile %s not found in repo store or system cache')
853 _('largefile %s not found in repo store or system cache')
854 % lfutil.splitstandin(f))
854 % lfutil.splitstandin(f))
855 f = lfutil.splitstandin(f)
855 f = lfutil.splitstandin(f)
856
856
857 def getdatafn():
857 def getdatafn():
858 fd = None
858 fd = None
859 try:
859 try:
860 fd = open(path, 'rb')
860 fd = open(path, 'rb')
861 return fd.read()
861 return fd.read()
862 finally:
862 finally:
863 if fd:
863 if fd:
864 fd.close()
864 fd.close()
865
865
866 getdata = getdatafn
866 getdata = getdatafn
867 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
867 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
868
868
869 if subrepos:
869 if subrepos:
870 for subpath in sorted(ctx.substate):
870 for subpath in sorted(ctx.substate):
871 sub = ctx.sub(subpath)
871 sub = ctx.sub(subpath)
872 submatch = match_.narrowmatcher(subpath, matchfn)
872 submatch = match_.narrowmatcher(subpath, matchfn)
873 sub.archive(repo.ui, archiver, prefix, submatch)
873 sub.archive(repo.ui, archiver, prefix, submatch)
874
874
875 archiver.done()
875 archiver.done()
876
876
877 def hgsubrepoarchive(orig, repo, ui, archiver, prefix, match=None):
877 def hgsubrepoarchive(orig, repo, ui, archiver, prefix, match=None):
878 repo._get(repo._state + ('hg',))
878 repo._get(repo._state + ('hg',))
879 rev = repo._state[1]
879 rev = repo._state[1]
880 ctx = repo._repo[rev]
880 ctx = repo._repo[rev]
881
881
882 lfcommands.cachelfiles(ui, repo._repo, ctx.node())
882 lfcommands.cachelfiles(ui, repo._repo, ctx.node())
883
883
884 def write(name, mode, islink, getdata):
884 def write(name, mode, islink, getdata):
885 # At this point, the standin has been replaced with the largefile name,
885 # At this point, the standin has been replaced with the largefile name,
886 # so the normal matcher works here without the lfutil variants.
886 # so the normal matcher works here without the lfutil variants.
887 if match and not match(f):
887 if match and not match(f):
888 return
888 return
889 data = getdata()
889 data = getdata()
890
890
891 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
891 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
892
892
893 for f in ctx:
893 for f in ctx:
894 ff = ctx.flags(f)
894 ff = ctx.flags(f)
895 getdata = ctx[f].data
895 getdata = ctx[f].data
896 if lfutil.isstandin(f):
896 if lfutil.isstandin(f):
897 path = lfutil.findfile(repo._repo, getdata().strip())
897 path = lfutil.findfile(repo._repo, getdata().strip())
898 if path is None:
898 if path is None:
899 raise util.Abort(
899 raise util.Abort(
900 _('largefile %s not found in repo store or system cache')
900 _('largefile %s not found in repo store or system cache')
901 % lfutil.splitstandin(f))
901 % lfutil.splitstandin(f))
902 f = lfutil.splitstandin(f)
902 f = lfutil.splitstandin(f)
903
903
904 def getdatafn():
904 def getdatafn():
905 fd = None
905 fd = None
906 try:
906 try:
907 fd = open(os.path.join(prefix, path), 'rb')
907 fd = open(os.path.join(prefix, path), 'rb')
908 return fd.read()
908 return fd.read()
909 finally:
909 finally:
910 if fd:
910 if fd:
911 fd.close()
911 fd.close()
912
912
913 getdata = getdatafn
913 getdata = getdatafn
914
914
915 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
915 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
916
916
917 for subpath in sorted(ctx.substate):
917 for subpath in sorted(ctx.substate):
918 sub = ctx.sub(subpath)
918 sub = ctx.sub(subpath)
919 submatch = match_.narrowmatcher(subpath, match)
919 submatch = match_.narrowmatcher(subpath, match)
920 sub.archive(ui, archiver, os.path.join(prefix, repo._path) + '/',
920 sub.archive(ui, archiver, os.path.join(prefix, repo._path) + '/',
921 submatch)
921 submatch)
922
922
923 # If a largefile is modified, the change is not reflected in its
923 # If a largefile is modified, the change is not reflected in its
924 # standin until a commit. cmdutil.bailifchanged() raises an exception
924 # standin until a commit. cmdutil.bailifchanged() raises an exception
925 # if the repo has uncommitted changes. Wrap it to also check if
925 # if the repo has uncommitted changes. Wrap it to also check if
926 # largefiles were changed. This is used by bisect and backout.
926 # largefiles were changed. This is used by bisect and backout.
927 def overridebailifchanged(orig, repo):
927 def overridebailifchanged(orig, repo):
928 orig(repo)
928 orig(repo)
929 repo.lfstatus = True
929 repo.lfstatus = True
930 modified, added, removed, deleted = repo.status()[:4]
930 modified, added, removed, deleted = repo.status()[:4]
931 repo.lfstatus = False
931 repo.lfstatus = False
932 if modified or added or removed or deleted:
932 if modified or added or removed or deleted:
933 raise util.Abort(_('uncommitted changes'))
933 raise util.Abort(_('uncommitted changes'))
934
934
935 # Fetch doesn't use cmdutil.bailifchanged so override it to add the check
935 # Fetch doesn't use cmdutil.bailifchanged so override it to add the check
936 def overridefetch(orig, ui, repo, *pats, **opts):
936 def overridefetch(orig, ui, repo, *pats, **opts):
937 repo.lfstatus = True
937 repo.lfstatus = True
938 modified, added, removed, deleted = repo.status()[:4]
938 modified, added, removed, deleted = repo.status()[:4]
939 repo.lfstatus = False
939 repo.lfstatus = False
940 if modified or added or removed or deleted:
940 if modified or added or removed or deleted:
941 raise util.Abort(_('uncommitted changes'))
941 raise util.Abort(_('uncommitted changes'))
942 return orig(ui, repo, *pats, **opts)
942 return orig(ui, repo, *pats, **opts)
943
943
944 def overrideforget(orig, ui, repo, *pats, **opts):
944 def overrideforget(orig, ui, repo, *pats, **opts):
945 installnormalfilesmatchfn(repo[None].manifest())
945 installnormalfilesmatchfn(repo[None].manifest())
946 result = orig(ui, repo, *pats, **opts)
946 result = orig(ui, repo, *pats, **opts)
947 restorematchfn()
947 restorematchfn()
948 m = scmutil.match(repo[None], pats, opts)
948 m = scmutil.match(repo[None], pats, opts)
949
949
950 try:
950 try:
951 repo.lfstatus = True
951 repo.lfstatus = True
952 s = repo.status(match=m, clean=True)
952 s = repo.status(match=m, clean=True)
953 finally:
953 finally:
954 repo.lfstatus = False
954 repo.lfstatus = False
955 forget = sorted(s[0] + s[1] + s[3] + s[6])
955 forget = sorted(s[0] + s[1] + s[3] + s[6])
956 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
956 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
957
957
958 for f in forget:
958 for f in forget:
959 if lfutil.standin(f) not in repo.dirstate and not \
959 if lfutil.standin(f) not in repo.dirstate and not \
960 os.path.isdir(m.rel(lfutil.standin(f))):
960 os.path.isdir(m.rel(lfutil.standin(f))):
961 ui.warn(_('not removing %s: file is already untracked\n')
961 ui.warn(_('not removing %s: file is already untracked\n')
962 % m.rel(f))
962 % m.rel(f))
963 result = 1
963 result = 1
964
964
965 for f in forget:
965 for f in forget:
966 if ui.verbose or not m.exact(f):
966 if ui.verbose or not m.exact(f):
967 ui.status(_('removing %s\n') % m.rel(f))
967 ui.status(_('removing %s\n') % m.rel(f))
968
968
969 # Need to lock because standin files are deleted then removed from the
969 # Need to lock because standin files are deleted then removed from the
970 # repository and we could race in-between.
970 # repository and we could race in-between.
971 wlock = repo.wlock()
971 wlock = repo.wlock()
972 try:
972 try:
973 lfdirstate = lfutil.openlfdirstate(ui, repo)
973 lfdirstate = lfutil.openlfdirstate(ui, repo)
974 for f in forget:
974 for f in forget:
975 if lfdirstate[f] == 'a':
975 if lfdirstate[f] == 'a':
976 lfdirstate.drop(f)
976 lfdirstate.drop(f)
977 else:
977 else:
978 lfdirstate.remove(f)
978 lfdirstate.remove(f)
979 lfdirstate.write()
979 lfdirstate.write()
980 standins = [lfutil.standin(f) for f in forget]
980 standins = [lfutil.standin(f) for f in forget]
981 for f in standins:
981 for f in standins:
982 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
982 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
983 repo[None].forget(standins)
983 repo[None].forget(standins)
984 finally:
984 finally:
985 wlock.release()
985 wlock.release()
986
986
987 return result
987 return result
988
988
989 def outgoinghook(ui, repo, other, opts, missing):
989 def outgoinghook(ui, repo, other, opts, missing):
990 if opts.pop('large', None):
990 if opts.pop('large', None):
991 toupload = set()
991 toupload = set()
992 lfutil.getlfilestoupload(repo, missing,
992 lfutil.getlfilestoupload(repo, missing,
993 lambda fn, lfhash: toupload.add(fn))
993 lambda fn, lfhash: toupload.add(fn))
994 if not toupload:
994 if not toupload:
995 ui.status(_('largefiles: no files to upload\n'))
995 ui.status(_('largefiles: no files to upload\n'))
996 else:
996 else:
997 ui.status(_('largefiles to upload:\n'))
997 ui.status(_('largefiles to upload:\n'))
998 for file in sorted(toupload):
998 for file in sorted(toupload):
999 ui.status(lfutil.splitstandin(file) + '\n')
999 ui.status(lfutil.splitstandin(file) + '\n')
1000 ui.status('\n')
1000 ui.status('\n')
1001
1001
1002 def summaryremotehook(ui, repo, opts, changes):
1002 def summaryremotehook(ui, repo, opts, changes):
1003 largeopt = opts.get('large', False)
1003 largeopt = opts.get('large', False)
1004 if changes is None:
1004 if changes is None:
1005 if largeopt:
1005 if largeopt:
1006 return (False, True) # only outgoing check is needed
1006 return (False, True) # only outgoing check is needed
1007 else:
1007 else:
1008 return (False, False)
1008 return (False, False)
1009 elif largeopt:
1009 elif largeopt:
1010 url, branch, peer, outgoing = changes[1]
1010 url, branch, peer, outgoing = changes[1]
1011 if peer is None:
1011 if peer is None:
1012 # i18n: column positioning for "hg summary"
1012 # i18n: column positioning for "hg summary"
1013 ui.status(_('largefiles: (no remote repo)\n'))
1013 ui.status(_('largefiles: (no remote repo)\n'))
1014 return
1014 return
1015
1015
1016 toupload = set()
1016 toupload = set()
1017 lfutil.getlfilestoupload(repo, outgoing.missing,
1017 lfutil.getlfilestoupload(repo, outgoing.missing,
1018 lambda fn, lfhash: toupload.add(fn))
1018 lambda fn, lfhash: toupload.add(fn))
1019 if not toupload:
1019 if not toupload:
1020 # i18n: column positioning for "hg summary"
1020 # i18n: column positioning for "hg summary"
1021 ui.status(_('largefiles: (no files to upload)\n'))
1021 ui.status(_('largefiles: (no files to upload)\n'))
1022 else:
1022 else:
1023 # i18n: column positioning for "hg summary"
1023 # i18n: column positioning for "hg summary"
1024 ui.status(_('largefiles: %d to upload\n') % len(toupload))
1024 ui.status(_('largefiles: %d to upload\n') % len(toupload))
1025
1025
1026 def overridesummary(orig, ui, repo, *pats, **opts):
1026 def overridesummary(orig, ui, repo, *pats, **opts):
1027 try:
1027 try:
1028 repo.lfstatus = True
1028 repo.lfstatus = True
1029 orig(ui, repo, *pats, **opts)
1029 orig(ui, repo, *pats, **opts)
1030 finally:
1030 finally:
1031 repo.lfstatus = False
1031 repo.lfstatus = False
1032
1032
1033 def scmutiladdremove(orig, repo, pats=[], opts={}, dry_run=None,
1033 def scmutiladdremove(orig, repo, pats=[], opts={}, dry_run=None,
1034 similarity=None):
1034 similarity=None):
1035 if not lfutil.islfilesrepo(repo):
1035 if not lfutil.islfilesrepo(repo):
1036 return orig(repo, pats, opts, dry_run, similarity)
1036 return orig(repo, pats, opts, dry_run, similarity)
1037 # Get the list of missing largefiles so we can remove them
1037 # Get the list of missing largefiles so we can remove them
1038 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1038 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1039 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
1039 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
1040 False, False)
1040 False, False)
1041 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
1041 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
1042
1042
1043 # Call into the normal remove code, but the removing of the standin, we want
1043 # Call into the normal remove code, but the removing of the standin, we want
1044 # to have handled by original addremove. Monkey patching here makes sure
1044 # to have handled by original addremove. Monkey patching here makes sure
1045 # we don't remove the standin in the largefiles code, preventing a very
1045 # we don't remove the standin in the largefiles code, preventing a very
1046 # confused state later.
1046 # confused state later.
1047 if missing:
1047 if missing:
1048 m = [repo.wjoin(f) for f in missing]
1048 m = [repo.wjoin(f) for f in missing]
1049 repo._isaddremove = True
1049 repo._isaddremove = True
1050 removelargefiles(repo.ui, repo, *m, **opts)
1050 removelargefiles(repo.ui, repo, *m, **opts)
1051 repo._isaddremove = False
1051 repo._isaddremove = False
1052 # Call into the normal add code, and any files that *should* be added as
1052 # Call into the normal add code, and any files that *should* be added as
1053 # largefiles will be
1053 # largefiles will be
1054 addlargefiles(repo.ui, repo, *pats, **opts)
1054 addlargefiles(repo.ui, repo, *pats, **opts)
1055 # Now that we've handled largefiles, hand off to the original addremove
1055 # Now that we've handled largefiles, hand off to the original addremove
1056 # function to take care of the rest. Make sure it doesn't do anything with
1056 # function to take care of the rest. Make sure it doesn't do anything with
1057 # largefiles by installing a matcher that will ignore them.
1057 # largefiles by installing a matcher that will ignore them.
1058 installnormalfilesmatchfn(repo[None].manifest())
1058 installnormalfilesmatchfn(repo[None].manifest())
1059 result = orig(repo, pats, opts, dry_run, similarity)
1059 result = orig(repo, pats, opts, dry_run, similarity)
1060 restorematchfn()
1060 restorematchfn()
1061 return result
1061 return result
1062
1062
1063 # Calling purge with --all will cause the largefiles to be deleted.
1063 # Calling purge with --all will cause the largefiles to be deleted.
1064 # Override repo.status to prevent this from happening.
1064 # Override repo.status to prevent this from happening.
1065 def overridepurge(orig, ui, repo, *dirs, **opts):
1065 def overridepurge(orig, ui, repo, *dirs, **opts):
1066 # XXX large file status is buggy when used on repo proxy.
1066 # XXX large file status is buggy when used on repo proxy.
1067 # XXX this needs to be investigate.
1067 # XXX this needs to be investigate.
1068 repo = repo.unfiltered()
1068 repo = repo.unfiltered()
1069 oldstatus = repo.status
1069 oldstatus = repo.status
1070 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1070 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1071 clean=False, unknown=False, listsubrepos=False):
1071 clean=False, unknown=False, listsubrepos=False):
1072 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1072 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1073 listsubrepos)
1073 listsubrepos)
1074 lfdirstate = lfutil.openlfdirstate(ui, repo)
1074 lfdirstate = lfutil.openlfdirstate(ui, repo)
1075 modified, added, removed, deleted, unknown, ignored, clean = r
1075 modified, added, removed, deleted, unknown, ignored, clean = r
1076 unknown = [f for f in unknown if lfdirstate[f] == '?']
1076 unknown = [f for f in unknown if lfdirstate[f] == '?']
1077 ignored = [f for f in ignored if lfdirstate[f] == '?']
1077 ignored = [f for f in ignored if lfdirstate[f] == '?']
1078 return modified, added, removed, deleted, unknown, ignored, clean
1078 return modified, added, removed, deleted, unknown, ignored, clean
1079 repo.status = overridestatus
1079 repo.status = overridestatus
1080 orig(ui, repo, *dirs, **opts)
1080 orig(ui, repo, *dirs, **opts)
1081 repo.status = oldstatus
1081 repo.status = oldstatus
1082
1082
1083 def overriderollback(orig, ui, repo, **opts):
1083 def overriderollback(orig, ui, repo, **opts):
1084 result = orig(ui, repo, **opts)
1084 result = orig(ui, repo, **opts)
1085 merge.update(repo, node=None, branchmerge=False, force=True,
1085 merge.update(repo, node=None, branchmerge=False, force=True,
1086 partial=lfutil.isstandin)
1086 partial=lfutil.isstandin)
1087 wlock = repo.wlock()
1087 wlock = repo.wlock()
1088 try:
1088 try:
1089 lfdirstate = lfutil.openlfdirstate(ui, repo)
1089 lfdirstate = lfutil.openlfdirstate(ui, repo)
1090 lfiles = lfutil.listlfiles(repo)
1090 lfiles = lfutil.listlfiles(repo)
1091 oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
1091 oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
1092 for file in lfiles:
1092 for file in lfiles:
1093 if file in oldlfiles:
1093 if file in oldlfiles:
1094 lfdirstate.normallookup(file)
1094 lfdirstate.normallookup(file)
1095 else:
1095 else:
1096 lfdirstate.add(file)
1096 lfdirstate.add(file)
1097 lfdirstate.write()
1097 lfdirstate.write()
1098 finally:
1098 finally:
1099 wlock.release()
1099 wlock.release()
1100 return result
1100 return result
1101
1101
1102 def overridetransplant(orig, ui, repo, *revs, **opts):
1102 def overridetransplant(orig, ui, repo, *revs, **opts):
1103 try:
1103 try:
1104 oldstandins = lfutil.getstandinsstate(repo)
1104 oldstandins = lfutil.getstandinsstate(repo)
1105 repo._istransplanting = True
1105 repo._istransplanting = True
1106 result = orig(ui, repo, *revs, **opts)
1106 result = orig(ui, repo, *revs, **opts)
1107 newstandins = lfutil.getstandinsstate(repo)
1107 newstandins = lfutil.getstandinsstate(repo)
1108 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1108 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1109 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1109 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1110 printmessage=True)
1110 printmessage=True)
1111 finally:
1111 finally:
1112 repo._istransplanting = False
1112 repo._istransplanting = False
1113 return result
1113 return result
1114
1114
1115 def overridecat(orig, ui, repo, file1, *pats, **opts):
1115 def overridecat(orig, ui, repo, file1, *pats, **opts):
1116 ctx = scmutil.revsingle(repo, opts.get('rev'))
1116 ctx = scmutil.revsingle(repo, opts.get('rev'))
1117 err = 1
1117 err = 1
1118 notbad = set()
1118 notbad = set()
1119 m = scmutil.match(ctx, (file1,) + pats, opts)
1119 m = scmutil.match(ctx, (file1,) + pats, opts)
1120 origmatchfn = m.matchfn
1120 origmatchfn = m.matchfn
1121 def lfmatchfn(f):
1121 def lfmatchfn(f):
1122 if origmatchfn(f):
1122 if origmatchfn(f):
1123 return True
1123 return True
1124 lf = lfutil.splitstandin(f)
1124 lf = lfutil.splitstandin(f)
1125 if lf is None:
1125 if lf is None:
1126 return False
1126 return False
1127 notbad.add(lf)
1127 notbad.add(lf)
1128 return origmatchfn(lf)
1128 return origmatchfn(lf)
1129 m.matchfn = lfmatchfn
1129 m.matchfn = lfmatchfn
1130 origbadfn = m.bad
1130 origbadfn = m.bad
1131 def lfbadfn(f, msg):
1131 def lfbadfn(f, msg):
1132 if not f in notbad:
1132 if not f in notbad:
1133 origbadfn(f, msg)
1133 origbadfn(f, msg)
1134 m.bad = lfbadfn
1134 m.bad = lfbadfn
1135 for f in ctx.walk(m):
1135 for f in ctx.walk(m):
1136 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1136 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1137 pathname=f)
1137 pathname=f)
1138 lf = lfutil.splitstandin(f)
1138 lf = lfutil.splitstandin(f)
1139 if lf is None or origmatchfn(f):
1139 if lf is None or origmatchfn(f):
1140 # duplicating unreachable code from commands.cat
1140 # duplicating unreachable code from commands.cat
1141 data = ctx[f].data()
1141 data = ctx[f].data()
1142 if opts.get('decode'):
1142 if opts.get('decode'):
1143 data = repo.wwritedata(f, data)
1143 data = repo.wwritedata(f, data)
1144 fp.write(data)
1144 fp.write(data)
1145 else:
1145 else:
1146 hash = lfutil.readstandin(repo, lf, ctx.rev())
1146 hash = lfutil.readstandin(repo, lf, ctx.rev())
1147 if not lfutil.inusercache(repo.ui, hash):
1147 if not lfutil.inusercache(repo.ui, hash):
1148 store = basestore._openstore(repo)
1148 store = basestore._openstore(repo)
1149 success, missing = store.get([(lf, hash)])
1149 success, missing = store.get([(lf, hash)])
1150 if len(success) != 1:
1150 if len(success) != 1:
1151 raise util.Abort(
1151 raise util.Abort(
1152 _('largefile %s is not in cache and could not be '
1152 _('largefile %s is not in cache and could not be '
1153 'downloaded') % lf)
1153 'downloaded') % lf)
1154 path = lfutil.usercachepath(repo.ui, hash)
1154 path = lfutil.usercachepath(repo.ui, hash)
1155 fpin = open(path, "rb")
1155 fpin = open(path, "rb")
1156 for chunk in util.filechunkiter(fpin, 128 * 1024):
1156 for chunk in util.filechunkiter(fpin, 128 * 1024):
1157 fp.write(chunk)
1157 fp.write(chunk)
1158 fpin.close()
1158 fpin.close()
1159 fp.close()
1159 fp.close()
1160 err = 0
1160 err = 0
1161 return err
1161 return err
1162
1162
1163 def mercurialsinkbefore(orig, sink):
1163 def mercurialsinkbefore(orig, sink):
1164 sink.repo._isconverting = True
1164 sink.repo._isconverting = True
1165 orig(sink)
1165 orig(sink)
1166
1166
1167 def mercurialsinkafter(orig, sink):
1167 def mercurialsinkafter(orig, sink):
1168 sink.repo._isconverting = False
1168 sink.repo._isconverting = False
1169 orig(sink)
1169 orig(sink)
General Comments 0
You need to be logged in to leave comments. Login now