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