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