##// END OF EJS Templates
largefiles: rewrite merge code using dictionary with entry per file...
Martin von Zweigbergk -
r23529:38e55e55 default
parent child Browse files
Show More
@@ -1,1282 +1,1286
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.
429 actionbyfile = {}
430 for m, l in actions.iteritems():
431 for f, args, msg in l:
432 actionbyfile[f] = m, args, msg
433
428 removes = set(a[0] for a in actions['r'])
434 removes = set(a[0] for a in actions['r'])
429
435
430 newglist = []
431 lfmr = [] # LargeFiles: Mark as Removed
432 for action in actions['g']:
436 for action in actions['g']:
433 f, args, msg = action
437 f, args, msg = action
434 splitstandin = f and lfutil.splitstandin(f)
438 splitstandin = f and lfutil.splitstandin(f)
435 if (splitstandin is not None and
439 if (splitstandin is not None and
436 splitstandin in p1 and splitstandin not in removes):
440 splitstandin in p1 and splitstandin not in removes):
437 # Case 1: normal file in the working copy, largefile in
441 # Case 1: normal file in the working copy, largefile in
438 # the second parent
442 # the second parent
439 lfile = splitstandin
443 lfile = splitstandin
440 standin = f
444 standin = f
441 usermsg = _('remote turned local normal file %s into a largefile\n'
445 usermsg = _('remote turned local normal file %s into a largefile\n'
442 'use (l)argefile or keep (n)ormal file?'
446 'use (l)argefile or keep (n)ormal file?'
443 '$$ &Largefile $$ &Normal file') % lfile
447 '$$ &Largefile $$ &Normal file') % lfile
444 if repo.ui.promptchoice(usermsg, 0) == 0: # pick remote largefile
448 if repo.ui.promptchoice(usermsg, 0) == 0: # pick remote largefile
445 actions['r'].append((lfile, None, 'replaced by standin'))
449 actionbyfile[lfile] = ('r', None, 'replaced by standin')
446 newglist.append(action)
447 else: # keep local normal file
450 else: # keep local normal file
448 if branchmerge:
451 if branchmerge:
449 actions['k'].append((standin, None,
452 actionbyfile[standin] = ('k', None,
450 'replaced by non-standin'))
453 'replaced by non-standin')
451 else:
454 else:
452 actions['r'].append((standin, None,
455 actionbyfile[standin] = ('r', None,
453 'replaced by non-standin'))
456 'replaced by non-standin')
454 elif lfutil.standin(f) in p1 and lfutil.standin(f) not in removes:
457 elif lfutil.standin(f) in p1 and lfutil.standin(f) not in removes:
455 # Case 2: largefile in the working copy, normal file in
458 # Case 2: largefile in the working copy, normal file in
456 # the second parent
459 # the second parent
457 standin = lfutil.standin(f)
460 standin = lfutil.standin(f)
458 lfile = f
461 lfile = f
459 usermsg = _('remote turned local largefile %s into a normal file\n'
462 usermsg = _('remote turned local largefile %s into a normal file\n'
460 'keep (l)argefile or use (n)ormal file?'
463 'keep (l)argefile or use (n)ormal file?'
461 '$$ &Largefile $$ &Normal file') % lfile
464 '$$ &Largefile $$ &Normal file') % lfile
462 if repo.ui.promptchoice(usermsg, 0) == 0: # keep local largefile
465 if repo.ui.promptchoice(usermsg, 0) == 0: # keep local largefile
463 if branchmerge:
466 if branchmerge:
464 # largefile can be restored from standin safely
467 # largefile can be restored from standin safely
465 actions['k'].append((lfile, None, 'replaced by standin'))
468 actionbyfile[lfile] = ('k', None, 'replaced by standin')
466 else:
469 else:
467 # "lfile" should be marked as "removed" without
470 # "lfile" should be marked as "removed" without
468 # removal of itself
471 # removal of itself
469 lfmr.append((lfile, None, 'forget non-standin largefile'))
472 actionbyfile[lfile] = ('lfmr', None,
473 'forget non-standin largefile')
470
474
471 # linear-merge should treat this largefile as 're-added'
475 # linear-merge should treat this largefile as 're-added'
472 actions['a'].append((standin, None, 'keep standin'))
476 actionbyfile[standin] = ('a', None, 'keep standin')
473 else: # pick remote normal file
477 else: # pick remote normal file
474 actions['r'].append((standin, None, 'replaced by non-standin'))
478 actionbyfile[standin] = ('r', None, 'replaced by non-standin')
475 newglist.append(action)
476 else:
477 newglist.append(action)
478
479
479 actions['g'] = newglist
480 # Convert back to dictionary-of-lists format
480 if lfmr:
481 for l in actions.itervalues():
481 actions['lfmr'] = lfmr
482 l[:] = []
483 actions['lfmr'] = []
484 for f, (m, args, msg) in actionbyfile.iteritems():
485 actions[m].append((f, args, msg))
482
486
483 return actions, diverge, renamedelete
487 return actions, diverge, renamedelete
484
488
485 def mergerecordupdates(orig, repo, actions, branchmerge):
489 def mergerecordupdates(orig, repo, actions, branchmerge):
486 if 'lfmr' in actions:
490 if 'lfmr' in actions:
487 # this should be executed before 'orig', to execute 'remove'
491 # this should be executed before 'orig', to execute 'remove'
488 # before all other actions
492 # before all other actions
489 for lfile, args, msg in actions['lfmr']:
493 for lfile, args, msg in actions['lfmr']:
490 repo.dirstate.remove(lfile)
494 repo.dirstate.remove(lfile)
491
495
492 return orig(repo, actions, branchmerge)
496 return orig(repo, actions, branchmerge)
493
497
494
498
495 # 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
496 # largefiles. This will handle identical edits without prompting the user.
500 # largefiles. This will handle identical edits without prompting the user.
497 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca, labels=None):
501 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca, labels=None):
498 if not lfutil.isstandin(orig):
502 if not lfutil.isstandin(orig):
499 return origfn(repo, mynode, orig, fcd, fco, fca, labels=labels)
503 return origfn(repo, mynode, orig, fcd, fco, fca, labels=labels)
500
504
501 ahash = fca.data().strip().lower()
505 ahash = fca.data().strip().lower()
502 dhash = fcd.data().strip().lower()
506 dhash = fcd.data().strip().lower()
503 ohash = fco.data().strip().lower()
507 ohash = fco.data().strip().lower()
504 if (ohash != ahash and
508 if (ohash != ahash and
505 ohash != dhash and
509 ohash != dhash and
506 (dhash == ahash or
510 (dhash == ahash or
507 repo.ui.promptchoice(
511 repo.ui.promptchoice(
508 _('largefile %s has a merge conflict\nancestor was %s\n'
512 _('largefile %s has a merge conflict\nancestor was %s\n'
509 'keep (l)ocal %s or\ntake (o)ther %s?'
513 'keep (l)ocal %s or\ntake (o)ther %s?'
510 '$$ &Local $$ &Other') %
514 '$$ &Local $$ &Other') %
511 (lfutil.splitstandin(orig), ahash, dhash, ohash),
515 (lfutil.splitstandin(orig), ahash, dhash, ohash),
512 0) == 1)):
516 0) == 1)):
513 repo.wwrite(fcd.path(), fco.data(), fco.flags())
517 repo.wwrite(fcd.path(), fco.data(), fco.flags())
514 return 0
518 return 0
515
519
516 # Copy first changes the matchers to match standins instead of
520 # Copy first changes the matchers to match standins instead of
517 # largefiles. Then it overrides util.copyfile in that function it
521 # largefiles. Then it overrides util.copyfile in that function it
518 # checks if the destination largefile already exists. It also keeps a
522 # checks if the destination largefile already exists. It also keeps a
519 # 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
520 # dirstate updated.
524 # dirstate updated.
521 def overridecopy(orig, ui, repo, pats, opts, rename=False):
525 def overridecopy(orig, ui, repo, pats, opts, rename=False):
522 # doesn't remove largefile on rename
526 # doesn't remove largefile on rename
523 if len(pats) < 2:
527 if len(pats) < 2:
524 # this isn't legal, let the original function deal with it
528 # this isn't legal, let the original function deal with it
525 return orig(ui, repo, pats, opts, rename)
529 return orig(ui, repo, pats, opts, rename)
526
530
527 def makestandin(relpath):
531 def makestandin(relpath):
528 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
532 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
529 return os.path.join(repo.wjoin(lfutil.standin(path)))
533 return os.path.join(repo.wjoin(lfutil.standin(path)))
530
534
531 fullpats = scmutil.expandpats(pats)
535 fullpats = scmutil.expandpats(pats)
532 dest = fullpats[-1]
536 dest = fullpats[-1]
533
537
534 if os.path.isdir(dest):
538 if os.path.isdir(dest):
535 if not os.path.isdir(makestandin(dest)):
539 if not os.path.isdir(makestandin(dest)):
536 os.makedirs(makestandin(dest))
540 os.makedirs(makestandin(dest))
537 # This could copy both lfiles and normal files in one command,
541 # This could copy both lfiles and normal files in one command,
538 # 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
539 # 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
540 # match largefiles and run it again.
544 # match largefiles and run it again.
541 nonormalfiles = False
545 nonormalfiles = False
542 nolfiles = False
546 nolfiles = False
543 installnormalfilesmatchfn(repo[None].manifest())
547 installnormalfilesmatchfn(repo[None].manifest())
544 try:
548 try:
545 try:
549 try:
546 result = orig(ui, repo, pats, opts, rename)
550 result = orig(ui, repo, pats, opts, rename)
547 except util.Abort, e:
551 except util.Abort, e:
548 if str(e) != _('no files to copy'):
552 if str(e) != _('no files to copy'):
549 raise e
553 raise e
550 else:
554 else:
551 nonormalfiles = True
555 nonormalfiles = True
552 result = 0
556 result = 0
553 finally:
557 finally:
554 restorematchfn()
558 restorematchfn()
555
559
556 # 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.
557 # 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.
558 try:
562 try:
559 repo.getcwd()
563 repo.getcwd()
560 except OSError:
564 except OSError:
561 return result
565 return result
562
566
563 try:
567 try:
564 try:
568 try:
565 # 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
566 # 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.
567 wlock = repo.wlock()
571 wlock = repo.wlock()
568
572
569 manifest = repo[None].manifest()
573 manifest = repo[None].manifest()
570 def overridematch(ctx, pats=[], opts={}, globbed=False,
574 def overridematch(ctx, pats=[], opts={}, globbed=False,
571 default='relpath'):
575 default='relpath'):
572 newpats = []
576 newpats = []
573 # The patterns were previously mangled to add the standin
577 # The patterns were previously mangled to add the standin
574 # directory; we need to remove that now
578 # directory; we need to remove that now
575 for pat in pats:
579 for pat in pats:
576 if match_.patkind(pat) is None and lfutil.shortname in pat:
580 if match_.patkind(pat) is None and lfutil.shortname in pat:
577 newpats.append(pat.replace(lfutil.shortname, ''))
581 newpats.append(pat.replace(lfutil.shortname, ''))
578 else:
582 else:
579 newpats.append(pat)
583 newpats.append(pat)
580 match = oldmatch(ctx, newpats, opts, globbed, default)
584 match = oldmatch(ctx, newpats, opts, globbed, default)
581 m = copy.copy(match)
585 m = copy.copy(match)
582 lfile = lambda f: lfutil.standin(f) in manifest
586 lfile = lambda f: lfutil.standin(f) in manifest
583 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)]
584 m._fmap = set(m._files)
588 m._fmap = set(m._files)
585 origmatchfn = m.matchfn
589 origmatchfn = m.matchfn
586 m.matchfn = lambda f: (lfutil.isstandin(f) and
590 m.matchfn = lambda f: (lfutil.isstandin(f) and
587 (f in manifest) and
591 (f in manifest) and
588 origmatchfn(lfutil.splitstandin(f)) or
592 origmatchfn(lfutil.splitstandin(f)) or
589 None)
593 None)
590 return m
594 return m
591 oldmatch = installmatchfn(overridematch)
595 oldmatch = installmatchfn(overridematch)
592 listpats = []
596 listpats = []
593 for pat in pats:
597 for pat in pats:
594 if match_.patkind(pat) is not None:
598 if match_.patkind(pat) is not None:
595 listpats.append(pat)
599 listpats.append(pat)
596 else:
600 else:
597 listpats.append(makestandin(pat))
601 listpats.append(makestandin(pat))
598
602
599 try:
603 try:
600 origcopyfile = util.copyfile
604 origcopyfile = util.copyfile
601 copiedfiles = []
605 copiedfiles = []
602 def overridecopyfile(src, dest):
606 def overridecopyfile(src, dest):
603 if (lfutil.shortname in src and
607 if (lfutil.shortname in src and
604 dest.startswith(repo.wjoin(lfutil.shortname))):
608 dest.startswith(repo.wjoin(lfutil.shortname))):
605 destlfile = dest.replace(lfutil.shortname, '')
609 destlfile = dest.replace(lfutil.shortname, '')
606 if not opts['force'] and os.path.exists(destlfile):
610 if not opts['force'] and os.path.exists(destlfile):
607 raise IOError('',
611 raise IOError('',
608 _('destination largefile already exists'))
612 _('destination largefile already exists'))
609 copiedfiles.append((src, dest))
613 copiedfiles.append((src, dest))
610 origcopyfile(src, dest)
614 origcopyfile(src, dest)
611
615
612 util.copyfile = overridecopyfile
616 util.copyfile = overridecopyfile
613 result += orig(ui, repo, listpats, opts, rename)
617 result += orig(ui, repo, listpats, opts, rename)
614 finally:
618 finally:
615 util.copyfile = origcopyfile
619 util.copyfile = origcopyfile
616
620
617 lfdirstate = lfutil.openlfdirstate(ui, repo)
621 lfdirstate = lfutil.openlfdirstate(ui, repo)
618 for (src, dest) in copiedfiles:
622 for (src, dest) in copiedfiles:
619 if (lfutil.shortname in src and
623 if (lfutil.shortname in src and
620 dest.startswith(repo.wjoin(lfutil.shortname))):
624 dest.startswith(repo.wjoin(lfutil.shortname))):
621 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
625 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
622 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
626 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
623 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
627 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
624 if not os.path.isdir(destlfiledir):
628 if not os.path.isdir(destlfiledir):
625 os.makedirs(destlfiledir)
629 os.makedirs(destlfiledir)
626 if rename:
630 if rename:
627 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
631 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
628
632
629 # The file is gone, but this deletes any empty parent
633 # The file is gone, but this deletes any empty parent
630 # directories as a side-effect.
634 # directories as a side-effect.
631 util.unlinkpath(repo.wjoin(srclfile), True)
635 util.unlinkpath(repo.wjoin(srclfile), True)
632 lfdirstate.remove(srclfile)
636 lfdirstate.remove(srclfile)
633 else:
637 else:
634 util.copyfile(repo.wjoin(srclfile),
638 util.copyfile(repo.wjoin(srclfile),
635 repo.wjoin(destlfile))
639 repo.wjoin(destlfile))
636
640
637 lfdirstate.add(destlfile)
641 lfdirstate.add(destlfile)
638 lfdirstate.write()
642 lfdirstate.write()
639 except util.Abort, e:
643 except util.Abort, e:
640 if str(e) != _('no files to copy'):
644 if str(e) != _('no files to copy'):
641 raise e
645 raise e
642 else:
646 else:
643 nolfiles = True
647 nolfiles = True
644 finally:
648 finally:
645 restorematchfn()
649 restorematchfn()
646 wlock.release()
650 wlock.release()
647
651
648 if nolfiles and nonormalfiles:
652 if nolfiles and nonormalfiles:
649 raise util.Abort(_('no files to copy'))
653 raise util.Abort(_('no files to copy'))
650
654
651 return result
655 return result
652
656
653 # 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
654 # changes to other largefiles accidentally. This means we have to keep
658 # changes to other largefiles accidentally. This means we have to keep
655 # 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
656 # the necessary largefiles.
660 # the necessary largefiles.
657 #
661 #
658 # Standins are only updated (to match the hash of largefiles) before
662 # Standins are only updated (to match the hash of largefiles) before
659 # commits. Update the standins then run the original revert, changing
663 # commits. Update the standins then run the original revert, changing
660 # the matcher to hit standins instead of largefiles. Based on the
664 # the matcher to hit standins instead of largefiles. Based on the
661 # resulting standins update the largefiles.
665 # resulting standins update the largefiles.
662 def overriderevert(orig, ui, repo, *pats, **opts):
666 def overriderevert(orig, ui, repo, *pats, **opts):
663 # 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)
664 # 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
665 # prevent others from changing them in their incorrect state.
669 # prevent others from changing them in their incorrect state.
666 wlock = repo.wlock()
670 wlock = repo.wlock()
667 try:
671 try:
668 lfdirstate = lfutil.openlfdirstate(ui, repo)
672 lfdirstate = lfutil.openlfdirstate(ui, repo)
669 s = lfutil.lfdirstatestatus(lfdirstate, repo)
673 s = lfutil.lfdirstatestatus(lfdirstate, repo)
670 lfdirstate.write()
674 lfdirstate.write()
671 for lfile in s.modified:
675 for lfile in s.modified:
672 lfutil.updatestandin(repo, lfutil.standin(lfile))
676 lfutil.updatestandin(repo, lfutil.standin(lfile))
673 for lfile in s.deleted:
677 for lfile in s.deleted:
674 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
678 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
675 os.unlink(repo.wjoin(lfutil.standin(lfile)))
679 os.unlink(repo.wjoin(lfutil.standin(lfile)))
676
680
677 oldstandins = lfutil.getstandinsstate(repo)
681 oldstandins = lfutil.getstandinsstate(repo)
678
682
679 def overridematch(ctx, pats=[], opts={}, globbed=False,
683 def overridematch(ctx, pats=[], opts={}, globbed=False,
680 default='relpath'):
684 default='relpath'):
681 match = oldmatch(ctx, pats, opts, globbed, default)
685 match = oldmatch(ctx, pats, opts, globbed, default)
682 m = copy.copy(match)
686 m = copy.copy(match)
683 def tostandin(f):
687 def tostandin(f):
684 if lfutil.standin(f) in ctx:
688 if lfutil.standin(f) in ctx:
685 return lfutil.standin(f)
689 return lfutil.standin(f)
686 elif lfutil.standin(f) in repo[None]:
690 elif lfutil.standin(f) in repo[None]:
687 return None
691 return None
688 return f
692 return f
689 m._files = [tostandin(f) for f in m._files]
693 m._files = [tostandin(f) for f in m._files]
690 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]
691 m._fmap = set(m._files)
695 m._fmap = set(m._files)
692 origmatchfn = m.matchfn
696 origmatchfn = m.matchfn
693 def matchfn(f):
697 def matchfn(f):
694 if lfutil.isstandin(f):
698 if lfutil.isstandin(f):
695 return (origmatchfn(lfutil.splitstandin(f)) and
699 return (origmatchfn(lfutil.splitstandin(f)) and
696 (f in repo[None] or f in ctx))
700 (f in repo[None] or f in ctx))
697 return origmatchfn(f)
701 return origmatchfn(f)
698 m.matchfn = matchfn
702 m.matchfn = matchfn
699 return m
703 return m
700 oldmatch = installmatchfn(overridematch)
704 oldmatch = installmatchfn(overridematch)
701 try:
705 try:
702 orig(ui, repo, *pats, **opts)
706 orig(ui, repo, *pats, **opts)
703 finally:
707 finally:
704 restorematchfn()
708 restorematchfn()
705
709
706 newstandins = lfutil.getstandinsstate(repo)
710 newstandins = lfutil.getstandinsstate(repo)
707 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
711 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
708 # lfdirstate should be 'normallookup'-ed for updated files,
712 # lfdirstate should be 'normallookup'-ed for updated files,
709 # because reverting doesn't touch dirstate for 'normal' files
713 # because reverting doesn't touch dirstate for 'normal' files
710 # when target revision is explicitly specified: in such case,
714 # when target revision is explicitly specified: in such case,
711 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
715 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
712 # of target (standin) file.
716 # of target (standin) file.
713 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
717 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
714 normallookup=True)
718 normallookup=True)
715
719
716 finally:
720 finally:
717 wlock.release()
721 wlock.release()
718
722
719 # 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
720 # largefiles updated remotely
724 # largefiles updated remotely
721 def overridepull(orig, ui, repo, source=None, **opts):
725 def overridepull(orig, ui, repo, source=None, **opts):
722 revsprepull = len(repo)
726 revsprepull = len(repo)
723 if not source:
727 if not source:
724 source = 'default'
728 source = 'default'
725 repo.lfpullsource = source
729 repo.lfpullsource = source
726 result = orig(ui, repo, source, **opts)
730 result = orig(ui, repo, source, **opts)
727 revspostpull = len(repo)
731 revspostpull = len(repo)
728 lfrevs = opts.get('lfrev', [])
732 lfrevs = opts.get('lfrev', [])
729 if opts.get('all_largefiles'):
733 if opts.get('all_largefiles'):
730 lfrevs.append('pulled()')
734 lfrevs.append('pulled()')
731 if lfrevs and revspostpull > revsprepull:
735 if lfrevs and revspostpull > revsprepull:
732 numcached = 0
736 numcached = 0
733 repo.firstpulled = revsprepull # for pulled() revset expression
737 repo.firstpulled = revsprepull # for pulled() revset expression
734 try:
738 try:
735 for rev in scmutil.revrange(repo, lfrevs):
739 for rev in scmutil.revrange(repo, lfrevs):
736 ui.note(_('pulling largefiles for revision %s\n') % rev)
740 ui.note(_('pulling largefiles for revision %s\n') % rev)
737 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
741 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
738 numcached += len(cached)
742 numcached += len(cached)
739 finally:
743 finally:
740 del repo.firstpulled
744 del repo.firstpulled
741 ui.status(_("%d largefiles cached\n") % numcached)
745 ui.status(_("%d largefiles cached\n") % numcached)
742 return result
746 return result
743
747
744 def pulledrevsetsymbol(repo, subset, x):
748 def pulledrevsetsymbol(repo, subset, x):
745 """``pulled()``
749 """``pulled()``
746 Changesets that just has been pulled.
750 Changesets that just has been pulled.
747
751
748 Only available with largefiles from pull --lfrev expressions.
752 Only available with largefiles from pull --lfrev expressions.
749
753
750 .. container:: verbose
754 .. container:: verbose
751
755
752 Some examples:
756 Some examples:
753
757
754 - pull largefiles for all new changesets::
758 - pull largefiles for all new changesets::
755
759
756 hg pull -lfrev "pulled()"
760 hg pull -lfrev "pulled()"
757
761
758 - pull largefiles for all new branch heads::
762 - pull largefiles for all new branch heads::
759
763
760 hg pull -lfrev "head(pulled()) and not closed()"
764 hg pull -lfrev "head(pulled()) and not closed()"
761
765
762 """
766 """
763
767
764 try:
768 try:
765 firstpulled = repo.firstpulled
769 firstpulled = repo.firstpulled
766 except AttributeError:
770 except AttributeError:
767 raise util.Abort(_("pulled() only available in --lfrev"))
771 raise util.Abort(_("pulled() only available in --lfrev"))
768 return revset.baseset([r for r in subset if r >= firstpulled])
772 return revset.baseset([r for r in subset if r >= firstpulled])
769
773
770 def overrideclone(orig, ui, source, dest=None, **opts):
774 def overrideclone(orig, ui, source, dest=None, **opts):
771 d = dest
775 d = dest
772 if d is None:
776 if d is None:
773 d = hg.defaultdest(source)
777 d = hg.defaultdest(source)
774 if opts.get('all_largefiles') and not hg.islocal(d):
778 if opts.get('all_largefiles') and not hg.islocal(d):
775 raise util.Abort(_(
779 raise util.Abort(_(
776 '--all-largefiles is incompatible with non-local destination %s') %
780 '--all-largefiles is incompatible with non-local destination %s') %
777 d)
781 d)
778
782
779 return orig(ui, source, dest, **opts)
783 return orig(ui, source, dest, **opts)
780
784
781 def hgclone(orig, ui, opts, *args, **kwargs):
785 def hgclone(orig, ui, opts, *args, **kwargs):
782 result = orig(ui, opts, *args, **kwargs)
786 result = orig(ui, opts, *args, **kwargs)
783
787
784 if result is not None:
788 if result is not None:
785 sourcerepo, destrepo = result
789 sourcerepo, destrepo = result
786 repo = destrepo.local()
790 repo = destrepo.local()
787
791
788 # 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
789 # 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
790 # 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.
791 if opts.get('all_largefiles'):
795 if opts.get('all_largefiles'):
792 success, missing = lfcommands.downloadlfiles(ui, repo, None)
796 success, missing = lfcommands.downloadlfiles(ui, repo, None)
793
797
794 if missing != 0:
798 if missing != 0:
795 return None
799 return None
796
800
797 return result
801 return result
798
802
799 def overriderebase(orig, ui, repo, **opts):
803 def overriderebase(orig, ui, repo, **opts):
800 resuming = opts.get('continue')
804 resuming = opts.get('continue')
801 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
805 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
802 repo._lfstatuswriters.append(lambda *msg, **opts: None)
806 repo._lfstatuswriters.append(lambda *msg, **opts: None)
803 try:
807 try:
804 return orig(ui, repo, **opts)
808 return orig(ui, repo, **opts)
805 finally:
809 finally:
806 repo._lfstatuswriters.pop()
810 repo._lfstatuswriters.pop()
807 repo._lfcommithooks.pop()
811 repo._lfcommithooks.pop()
808
812
809 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
813 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
810 prefix=None, mtime=None, subrepos=None):
814 prefix=None, mtime=None, subrepos=None):
811 # No need to lock because we are only reading history and
815 # No need to lock because we are only reading history and
812 # largefile caches, neither of which are modified.
816 # largefile caches, neither of which are modified.
813 lfcommands.cachelfiles(repo.ui, repo, node)
817 lfcommands.cachelfiles(repo.ui, repo, node)
814
818
815 if kind not in archival.archivers:
819 if kind not in archival.archivers:
816 raise util.Abort(_("unknown archive type '%s'") % kind)
820 raise util.Abort(_("unknown archive type '%s'") % kind)
817
821
818 ctx = repo[node]
822 ctx = repo[node]
819
823
820 if kind == 'files':
824 if kind == 'files':
821 if prefix:
825 if prefix:
822 raise util.Abort(
826 raise util.Abort(
823 _('cannot give prefix when archiving to files'))
827 _('cannot give prefix when archiving to files'))
824 else:
828 else:
825 prefix = archival.tidyprefix(dest, kind, prefix)
829 prefix = archival.tidyprefix(dest, kind, prefix)
826
830
827 def write(name, mode, islink, getdata):
831 def write(name, mode, islink, getdata):
828 if matchfn and not matchfn(name):
832 if matchfn and not matchfn(name):
829 return
833 return
830 data = getdata()
834 data = getdata()
831 if decode:
835 if decode:
832 data = repo.wwritedata(name, data)
836 data = repo.wwritedata(name, data)
833 archiver.addfile(prefix + name, mode, islink, data)
837 archiver.addfile(prefix + name, mode, islink, data)
834
838
835 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
839 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
836
840
837 if repo.ui.configbool("ui", "archivemeta", True):
841 if repo.ui.configbool("ui", "archivemeta", True):
838 def metadata():
842 def metadata():
839 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
843 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
840 hex(repo.changelog.node(0)), hex(node), ctx.branch())
844 hex(repo.changelog.node(0)), hex(node), ctx.branch())
841
845
842 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
846 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
843 if repo.tagtype(t) == 'global')
847 if repo.tagtype(t) == 'global')
844 if not tags:
848 if not tags:
845 repo.ui.pushbuffer()
849 repo.ui.pushbuffer()
846 opts = {'template': '{latesttag}\n{latesttagdistance}',
850 opts = {'template': '{latesttag}\n{latesttagdistance}',
847 'style': '', 'patch': None, 'git': None}
851 'style': '', 'patch': None, 'git': None}
848 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
852 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
849 ltags, dist = repo.ui.popbuffer().split('\n')
853 ltags, dist = repo.ui.popbuffer().split('\n')
850 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
854 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
851 tags += 'latesttagdistance: %s\n' % dist
855 tags += 'latesttagdistance: %s\n' % dist
852
856
853 return base + tags
857 return base + tags
854
858
855 write('.hg_archival.txt', 0644, False, metadata)
859 write('.hg_archival.txt', 0644, False, metadata)
856
860
857 for f in ctx:
861 for f in ctx:
858 ff = ctx.flags(f)
862 ff = ctx.flags(f)
859 getdata = ctx[f].data
863 getdata = ctx[f].data
860 if lfutil.isstandin(f):
864 if lfutil.isstandin(f):
861 path = lfutil.findfile(repo, getdata().strip())
865 path = lfutil.findfile(repo, getdata().strip())
862 if path is None:
866 if path is None:
863 raise util.Abort(
867 raise util.Abort(
864 _('largefile %s not found in repo store or system cache')
868 _('largefile %s not found in repo store or system cache')
865 % lfutil.splitstandin(f))
869 % lfutil.splitstandin(f))
866 f = lfutil.splitstandin(f)
870 f = lfutil.splitstandin(f)
867
871
868 def getdatafn():
872 def getdatafn():
869 fd = None
873 fd = None
870 try:
874 try:
871 fd = open(path, 'rb')
875 fd = open(path, 'rb')
872 return fd.read()
876 return fd.read()
873 finally:
877 finally:
874 if fd:
878 if fd:
875 fd.close()
879 fd.close()
876
880
877 getdata = getdatafn
881 getdata = getdatafn
878 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)
879
883
880 if subrepos:
884 if subrepos:
881 for subpath in sorted(ctx.substate):
885 for subpath in sorted(ctx.substate):
882 sub = ctx.sub(subpath)
886 sub = ctx.sub(subpath)
883 submatch = match_.narrowmatcher(subpath, matchfn)
887 submatch = match_.narrowmatcher(subpath, matchfn)
884 sub.archive(repo.ui, archiver, prefix, submatch)
888 sub.archive(repo.ui, archiver, prefix, submatch)
885
889
886 archiver.done()
890 archiver.done()
887
891
888 def hgsubrepoarchive(orig, repo, ui, archiver, prefix, match=None):
892 def hgsubrepoarchive(orig, repo, ui, archiver, prefix, match=None):
889 repo._get(repo._state + ('hg',))
893 repo._get(repo._state + ('hg',))
890 rev = repo._state[1]
894 rev = repo._state[1]
891 ctx = repo._repo[rev]
895 ctx = repo._repo[rev]
892
896
893 lfcommands.cachelfiles(ui, repo._repo, ctx.node())
897 lfcommands.cachelfiles(ui, repo._repo, ctx.node())
894
898
895 def write(name, mode, islink, getdata):
899 def write(name, mode, islink, getdata):
896 # 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,
897 # so the normal matcher works here without the lfutil variants.
901 # so the normal matcher works here without the lfutil variants.
898 if match and not match(f):
902 if match and not match(f):
899 return
903 return
900 data = getdata()
904 data = getdata()
901
905
902 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
906 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
903
907
904 for f in ctx:
908 for f in ctx:
905 ff = ctx.flags(f)
909 ff = ctx.flags(f)
906 getdata = ctx[f].data
910 getdata = ctx[f].data
907 if lfutil.isstandin(f):
911 if lfutil.isstandin(f):
908 path = lfutil.findfile(repo._repo, getdata().strip())
912 path = lfutil.findfile(repo._repo, getdata().strip())
909 if path is None:
913 if path is None:
910 raise util.Abort(
914 raise util.Abort(
911 _('largefile %s not found in repo store or system cache')
915 _('largefile %s not found in repo store or system cache')
912 % lfutil.splitstandin(f))
916 % lfutil.splitstandin(f))
913 f = lfutil.splitstandin(f)
917 f = lfutil.splitstandin(f)
914
918
915 def getdatafn():
919 def getdatafn():
916 fd = None
920 fd = None
917 try:
921 try:
918 fd = open(os.path.join(prefix, path), 'rb')
922 fd = open(os.path.join(prefix, path), 'rb')
919 return fd.read()
923 return fd.read()
920 finally:
924 finally:
921 if fd:
925 if fd:
922 fd.close()
926 fd.close()
923
927
924 getdata = getdatafn
928 getdata = getdatafn
925
929
926 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)
927
931
928 for subpath in sorted(ctx.substate):
932 for subpath in sorted(ctx.substate):
929 sub = ctx.sub(subpath)
933 sub = ctx.sub(subpath)
930 submatch = match_.narrowmatcher(subpath, match)
934 submatch = match_.narrowmatcher(subpath, match)
931 sub.archive(ui, archiver, os.path.join(prefix, repo._path) + '/',
935 sub.archive(ui, archiver, os.path.join(prefix, repo._path) + '/',
932 submatch)
936 submatch)
933
937
934 # 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
935 # standin until a commit. cmdutil.bailifchanged() raises an exception
939 # standin until a commit. cmdutil.bailifchanged() raises an exception
936 # 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
937 # largefiles were changed. This is used by bisect, backout and fetch.
941 # largefiles were changed. This is used by bisect, backout and fetch.
938 def overridebailifchanged(orig, repo):
942 def overridebailifchanged(orig, repo):
939 orig(repo)
943 orig(repo)
940 repo.lfstatus = True
944 repo.lfstatus = True
941 s = repo.status()
945 s = repo.status()
942 repo.lfstatus = False
946 repo.lfstatus = False
943 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:
944 raise util.Abort(_('uncommitted changes'))
948 raise util.Abort(_('uncommitted changes'))
945
949
946 def overrideforget(orig, ui, repo, *pats, **opts):
950 def overrideforget(orig, ui, repo, *pats, **opts):
947 installnormalfilesmatchfn(repo[None].manifest())
951 installnormalfilesmatchfn(repo[None].manifest())
948 result = orig(ui, repo, *pats, **opts)
952 result = orig(ui, repo, *pats, **opts)
949 restorematchfn()
953 restorematchfn()
950 m = scmutil.match(repo[None], pats, opts)
954 m = scmutil.match(repo[None], pats, opts)
951
955
952 try:
956 try:
953 repo.lfstatus = True
957 repo.lfstatus = True
954 s = repo.status(match=m, clean=True)
958 s = repo.status(match=m, clean=True)
955 finally:
959 finally:
956 repo.lfstatus = False
960 repo.lfstatus = False
957 forget = sorted(s.modified + s.added + s.deleted + s.clean)
961 forget = sorted(s.modified + s.added + s.deleted + s.clean)
958 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()]
959
963
960 for f in forget:
964 for f in forget:
961 if lfutil.standin(f) not in repo.dirstate and not \
965 if lfutil.standin(f) not in repo.dirstate and not \
962 os.path.isdir(m.rel(lfutil.standin(f))):
966 os.path.isdir(m.rel(lfutil.standin(f))):
963 ui.warn(_('not removing %s: file is already untracked\n')
967 ui.warn(_('not removing %s: file is already untracked\n')
964 % m.rel(f))
968 % m.rel(f))
965 result = 1
969 result = 1
966
970
967 for f in forget:
971 for f in forget:
968 if ui.verbose or not m.exact(f):
972 if ui.verbose or not m.exact(f):
969 ui.status(_('removing %s\n') % m.rel(f))
973 ui.status(_('removing %s\n') % m.rel(f))
970
974
971 # 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
972 # repository and we could race in-between.
976 # repository and we could race in-between.
973 wlock = repo.wlock()
977 wlock = repo.wlock()
974 try:
978 try:
975 lfdirstate = lfutil.openlfdirstate(ui, repo)
979 lfdirstate = lfutil.openlfdirstate(ui, repo)
976 for f in forget:
980 for f in forget:
977 if lfdirstate[f] == 'a':
981 if lfdirstate[f] == 'a':
978 lfdirstate.drop(f)
982 lfdirstate.drop(f)
979 else:
983 else:
980 lfdirstate.remove(f)
984 lfdirstate.remove(f)
981 lfdirstate.write()
985 lfdirstate.write()
982 standins = [lfutil.standin(f) for f in forget]
986 standins = [lfutil.standin(f) for f in forget]
983 for f in standins:
987 for f in standins:
984 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
988 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
985 repo[None].forget(standins)
989 repo[None].forget(standins)
986 finally:
990 finally:
987 wlock.release()
991 wlock.release()
988
992
989 return result
993 return result
990
994
991 def _getoutgoings(repo, other, missing, addfunc):
995 def _getoutgoings(repo, other, missing, addfunc):
992 """get pairs of filename and largefile hash in outgoing revisions
996 """get pairs of filename and largefile hash in outgoing revisions
993 in 'missing'.
997 in 'missing'.
994
998
995 largefiles already existing on 'other' repository are ignored.
999 largefiles already existing on 'other' repository are ignored.
996
1000
997 'addfunc' is invoked with each unique pairs of filename and
1001 'addfunc' is invoked with each unique pairs of filename and
998 largefile hash value.
1002 largefile hash value.
999 """
1003 """
1000 knowns = set()
1004 knowns = set()
1001 lfhashes = set()
1005 lfhashes = set()
1002 def dedup(fn, lfhash):
1006 def dedup(fn, lfhash):
1003 k = (fn, lfhash)
1007 k = (fn, lfhash)
1004 if k not in knowns:
1008 if k not in knowns:
1005 knowns.add(k)
1009 knowns.add(k)
1006 lfhashes.add(lfhash)
1010 lfhashes.add(lfhash)
1007 lfutil.getlfilestoupload(repo, missing, dedup)
1011 lfutil.getlfilestoupload(repo, missing, dedup)
1008 if lfhashes:
1012 if lfhashes:
1009 lfexists = basestore._openstore(repo, other).exists(lfhashes)
1013 lfexists = basestore._openstore(repo, other).exists(lfhashes)
1010 for fn, lfhash in knowns:
1014 for fn, lfhash in knowns:
1011 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1015 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1012 addfunc(fn, lfhash)
1016 addfunc(fn, lfhash)
1013
1017
1014 def outgoinghook(ui, repo, other, opts, missing):
1018 def outgoinghook(ui, repo, other, opts, missing):
1015 if opts.pop('large', None):
1019 if opts.pop('large', None):
1016 lfhashes = set()
1020 lfhashes = set()
1017 if ui.debugflag:
1021 if ui.debugflag:
1018 toupload = {}
1022 toupload = {}
1019 def addfunc(fn, lfhash):
1023 def addfunc(fn, lfhash):
1020 if fn not in toupload:
1024 if fn not in toupload:
1021 toupload[fn] = []
1025 toupload[fn] = []
1022 toupload[fn].append(lfhash)
1026 toupload[fn].append(lfhash)
1023 lfhashes.add(lfhash)
1027 lfhashes.add(lfhash)
1024 def showhashes(fn):
1028 def showhashes(fn):
1025 for lfhash in sorted(toupload[fn]):
1029 for lfhash in sorted(toupload[fn]):
1026 ui.debug(' %s\n' % (lfhash))
1030 ui.debug(' %s\n' % (lfhash))
1027 else:
1031 else:
1028 toupload = set()
1032 toupload = set()
1029 def addfunc(fn, lfhash):
1033 def addfunc(fn, lfhash):
1030 toupload.add(fn)
1034 toupload.add(fn)
1031 lfhashes.add(lfhash)
1035 lfhashes.add(lfhash)
1032 def showhashes(fn):
1036 def showhashes(fn):
1033 pass
1037 pass
1034 _getoutgoings(repo, other, missing, addfunc)
1038 _getoutgoings(repo, other, missing, addfunc)
1035
1039
1036 if not toupload:
1040 if not toupload:
1037 ui.status(_('largefiles: no files to upload\n'))
1041 ui.status(_('largefiles: no files to upload\n'))
1038 else:
1042 else:
1039 ui.status(_('largefiles to upload (%d entities):\n')
1043 ui.status(_('largefiles to upload (%d entities):\n')
1040 % (len(lfhashes)))
1044 % (len(lfhashes)))
1041 for file in sorted(toupload):
1045 for file in sorted(toupload):
1042 ui.status(lfutil.splitstandin(file) + '\n')
1046 ui.status(lfutil.splitstandin(file) + '\n')
1043 showhashes(file)
1047 showhashes(file)
1044 ui.status('\n')
1048 ui.status('\n')
1045
1049
1046 def summaryremotehook(ui, repo, opts, changes):
1050 def summaryremotehook(ui, repo, opts, changes):
1047 largeopt = opts.get('large', False)
1051 largeopt = opts.get('large', False)
1048 if changes is None:
1052 if changes is None:
1049 if largeopt:
1053 if largeopt:
1050 return (False, True) # only outgoing check is needed
1054 return (False, True) # only outgoing check is needed
1051 else:
1055 else:
1052 return (False, False)
1056 return (False, False)
1053 elif largeopt:
1057 elif largeopt:
1054 url, branch, peer, outgoing = changes[1]
1058 url, branch, peer, outgoing = changes[1]
1055 if peer is None:
1059 if peer is None:
1056 # i18n: column positioning for "hg summary"
1060 # i18n: column positioning for "hg summary"
1057 ui.status(_('largefiles: (no remote repo)\n'))
1061 ui.status(_('largefiles: (no remote repo)\n'))
1058 return
1062 return
1059
1063
1060 toupload = set()
1064 toupload = set()
1061 lfhashes = set()
1065 lfhashes = set()
1062 def addfunc(fn, lfhash):
1066 def addfunc(fn, lfhash):
1063 toupload.add(fn)
1067 toupload.add(fn)
1064 lfhashes.add(lfhash)
1068 lfhashes.add(lfhash)
1065 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1069 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1066
1070
1067 if not toupload:
1071 if not toupload:
1068 # i18n: column positioning for "hg summary"
1072 # i18n: column positioning for "hg summary"
1069 ui.status(_('largefiles: (no files to upload)\n'))
1073 ui.status(_('largefiles: (no files to upload)\n'))
1070 else:
1074 else:
1071 # i18n: column positioning for "hg summary"
1075 # i18n: column positioning for "hg summary"
1072 ui.status(_('largefiles: %d entities for %d files to upload\n')
1076 ui.status(_('largefiles: %d entities for %d files to upload\n')
1073 % (len(lfhashes), len(toupload)))
1077 % (len(lfhashes), len(toupload)))
1074
1078
1075 def overridesummary(orig, ui, repo, *pats, **opts):
1079 def overridesummary(orig, ui, repo, *pats, **opts):
1076 try:
1080 try:
1077 repo.lfstatus = True
1081 repo.lfstatus = True
1078 orig(ui, repo, *pats, **opts)
1082 orig(ui, repo, *pats, **opts)
1079 finally:
1083 finally:
1080 repo.lfstatus = False
1084 repo.lfstatus = False
1081
1085
1082 def scmutiladdremove(orig, repo, pats=[], opts={}, dry_run=None,
1086 def scmutiladdremove(orig, repo, pats=[], opts={}, dry_run=None,
1083 similarity=None):
1087 similarity=None):
1084 if not lfutil.islfilesrepo(repo):
1088 if not lfutil.islfilesrepo(repo):
1085 return orig(repo, pats, opts, dry_run, similarity)
1089 return orig(repo, pats, opts, dry_run, similarity)
1086 # Get the list of missing largefiles so we can remove them
1090 # Get the list of missing largefiles so we can remove them
1087 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1091 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1088 unsure, s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [],
1092 unsure, s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [],
1089 False, False, False)
1093 False, False, False)
1090
1094
1091 # 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
1092 # to have handled by original addremove. Monkey patching here makes sure
1096 # to have handled by original addremove. Monkey patching here makes sure
1093 # 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
1094 # confused state later.
1098 # confused state later.
1095 if s.deleted:
1099 if s.deleted:
1096 m = [repo.wjoin(f) for f in s.deleted]
1100 m = [repo.wjoin(f) for f in s.deleted]
1097 removelargefiles(repo.ui, repo, True, *m, **opts)
1101 removelargefiles(repo.ui, repo, True, *m, **opts)
1098 # 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
1099 # largefiles will be
1103 # largefiles will be
1100 addlargefiles(repo.ui, repo, *pats, **opts)
1104 addlargefiles(repo.ui, repo, *pats, **opts)
1101 # 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
1102 # 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
1103 # largefiles by installing a matcher that will ignore them.
1107 # largefiles by installing a matcher that will ignore them.
1104 installnormalfilesmatchfn(repo[None].manifest())
1108 installnormalfilesmatchfn(repo[None].manifest())
1105 result = orig(repo, pats, opts, dry_run, similarity)
1109 result = orig(repo, pats, opts, dry_run, similarity)
1106 restorematchfn()
1110 restorematchfn()
1107 return result
1111 return result
1108
1112
1109 # Calling purge with --all will cause the largefiles to be deleted.
1113 # Calling purge with --all will cause the largefiles to be deleted.
1110 # Override repo.status to prevent this from happening.
1114 # Override repo.status to prevent this from happening.
1111 def overridepurge(orig, ui, repo, *dirs, **opts):
1115 def overridepurge(orig, ui, repo, *dirs, **opts):
1112 oldstatus = repo.status
1116 oldstatus = repo.status
1113 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1117 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1114 clean=False, unknown=False, listsubrepos=False):
1118 clean=False, unknown=False, listsubrepos=False):
1115 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1119 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1116 listsubrepos)
1120 listsubrepos)
1117 lfdirstate = lfutil.openlfdirstate(ui, repo)
1121 lfdirstate = lfutil.openlfdirstate(ui, repo)
1118 unknown = [f for f in r.unknown if lfdirstate[f] == '?']
1122 unknown = [f for f in r.unknown if lfdirstate[f] == '?']
1119 ignored = [f for f in r.ignored if lfdirstate[f] == '?']
1123 ignored = [f for f in r.ignored if lfdirstate[f] == '?']
1120 return scmutil.status(r.modified, r.added, r.removed, r.deleted,
1124 return scmutil.status(r.modified, r.added, r.removed, r.deleted,
1121 unknown, ignored, r.clean)
1125 unknown, ignored, r.clean)
1122 repo.status = overridestatus
1126 repo.status = overridestatus
1123 orig(ui, repo, *dirs, **opts)
1127 orig(ui, repo, *dirs, **opts)
1124 repo.status = oldstatus
1128 repo.status = oldstatus
1125 def overriderollback(orig, ui, repo, **opts):
1129 def overriderollback(orig, ui, repo, **opts):
1126 wlock = repo.wlock()
1130 wlock = repo.wlock()
1127 try:
1131 try:
1128 before = repo.dirstate.parents()
1132 before = repo.dirstate.parents()
1129 orphans = set(f for f in repo.dirstate
1133 orphans = set(f for f in repo.dirstate
1130 if lfutil.isstandin(f) and repo.dirstate[f] != 'r')
1134 if lfutil.isstandin(f) and repo.dirstate[f] != 'r')
1131 result = orig(ui, repo, **opts)
1135 result = orig(ui, repo, **opts)
1132 after = repo.dirstate.parents()
1136 after = repo.dirstate.parents()
1133 if before == after:
1137 if before == after:
1134 return result # no need to restore standins
1138 return result # no need to restore standins
1135
1139
1136 pctx = repo['.']
1140 pctx = repo['.']
1137 for f in repo.dirstate:
1141 for f in repo.dirstate:
1138 if lfutil.isstandin(f):
1142 if lfutil.isstandin(f):
1139 orphans.discard(f)
1143 orphans.discard(f)
1140 if repo.dirstate[f] == 'r':
1144 if repo.dirstate[f] == 'r':
1141 repo.wvfs.unlinkpath(f, ignoremissing=True)
1145 repo.wvfs.unlinkpath(f, ignoremissing=True)
1142 elif f in pctx:
1146 elif f in pctx:
1143 fctx = pctx[f]
1147 fctx = pctx[f]
1144 repo.wwrite(f, fctx.data(), fctx.flags())
1148 repo.wwrite(f, fctx.data(), fctx.flags())
1145 else:
1149 else:
1146 # content of standin is not so important in 'a',
1150 # content of standin is not so important in 'a',
1147 # 'm' or 'n' (coming from the 2nd parent) cases
1151 # 'm' or 'n' (coming from the 2nd parent) cases
1148 lfutil.writestandin(repo, f, '', False)
1152 lfutil.writestandin(repo, f, '', False)
1149 for standin in orphans:
1153 for standin in orphans:
1150 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1154 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1151
1155
1152 lfdirstate = lfutil.openlfdirstate(ui, repo)
1156 lfdirstate = lfutil.openlfdirstate(ui, repo)
1153 orphans = set(lfdirstate)
1157 orphans = set(lfdirstate)
1154 lfiles = lfutil.listlfiles(repo)
1158 lfiles = lfutil.listlfiles(repo)
1155 for file in lfiles:
1159 for file in lfiles:
1156 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1160 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1157 orphans.discard(file)
1161 orphans.discard(file)
1158 for lfile in orphans:
1162 for lfile in orphans:
1159 lfdirstate.drop(lfile)
1163 lfdirstate.drop(lfile)
1160 lfdirstate.write()
1164 lfdirstate.write()
1161 finally:
1165 finally:
1162 wlock.release()
1166 wlock.release()
1163 return result
1167 return result
1164
1168
1165 def overridetransplant(orig, ui, repo, *revs, **opts):
1169 def overridetransplant(orig, ui, repo, *revs, **opts):
1166 resuming = opts.get('continue')
1170 resuming = opts.get('continue')
1167 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1171 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1168 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1172 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1169 try:
1173 try:
1170 result = orig(ui, repo, *revs, **opts)
1174 result = orig(ui, repo, *revs, **opts)
1171 finally:
1175 finally:
1172 repo._lfstatuswriters.pop()
1176 repo._lfstatuswriters.pop()
1173 repo._lfcommithooks.pop()
1177 repo._lfcommithooks.pop()
1174 return result
1178 return result
1175
1179
1176 def overridecat(orig, ui, repo, file1, *pats, **opts):
1180 def overridecat(orig, ui, repo, file1, *pats, **opts):
1177 ctx = scmutil.revsingle(repo, opts.get('rev'))
1181 ctx = scmutil.revsingle(repo, opts.get('rev'))
1178 err = 1
1182 err = 1
1179 notbad = set()
1183 notbad = set()
1180 m = scmutil.match(ctx, (file1,) + pats, opts)
1184 m = scmutil.match(ctx, (file1,) + pats, opts)
1181 origmatchfn = m.matchfn
1185 origmatchfn = m.matchfn
1182 def lfmatchfn(f):
1186 def lfmatchfn(f):
1183 if origmatchfn(f):
1187 if origmatchfn(f):
1184 return True
1188 return True
1185 lf = lfutil.splitstandin(f)
1189 lf = lfutil.splitstandin(f)
1186 if lf is None:
1190 if lf is None:
1187 return False
1191 return False
1188 notbad.add(lf)
1192 notbad.add(lf)
1189 return origmatchfn(lf)
1193 return origmatchfn(lf)
1190 m.matchfn = lfmatchfn
1194 m.matchfn = lfmatchfn
1191 origbadfn = m.bad
1195 origbadfn = m.bad
1192 def lfbadfn(f, msg):
1196 def lfbadfn(f, msg):
1193 if not f in notbad:
1197 if not f in notbad:
1194 origbadfn(f, msg)
1198 origbadfn(f, msg)
1195 m.bad = lfbadfn
1199 m.bad = lfbadfn
1196 for f in ctx.walk(m):
1200 for f in ctx.walk(m):
1197 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1201 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1198 pathname=f)
1202 pathname=f)
1199 lf = lfutil.splitstandin(f)
1203 lf = lfutil.splitstandin(f)
1200 if lf is None or origmatchfn(f):
1204 if lf is None or origmatchfn(f):
1201 # duplicating unreachable code from commands.cat
1205 # duplicating unreachable code from commands.cat
1202 data = ctx[f].data()
1206 data = ctx[f].data()
1203 if opts.get('decode'):
1207 if opts.get('decode'):
1204 data = repo.wwritedata(f, data)
1208 data = repo.wwritedata(f, data)
1205 fp.write(data)
1209 fp.write(data)
1206 else:
1210 else:
1207 hash = lfutil.readstandin(repo, lf, ctx.rev())
1211 hash = lfutil.readstandin(repo, lf, ctx.rev())
1208 if not lfutil.inusercache(repo.ui, hash):
1212 if not lfutil.inusercache(repo.ui, hash):
1209 store = basestore._openstore(repo)
1213 store = basestore._openstore(repo)
1210 success, missing = store.get([(lf, hash)])
1214 success, missing = store.get([(lf, hash)])
1211 if len(success) != 1:
1215 if len(success) != 1:
1212 raise util.Abort(
1216 raise util.Abort(
1213 _('largefile %s is not in cache and could not be '
1217 _('largefile %s is not in cache and could not be '
1214 'downloaded') % lf)
1218 'downloaded') % lf)
1215 path = lfutil.usercachepath(repo.ui, hash)
1219 path = lfutil.usercachepath(repo.ui, hash)
1216 fpin = open(path, "rb")
1220 fpin = open(path, "rb")
1217 for chunk in util.filechunkiter(fpin, 128 * 1024):
1221 for chunk in util.filechunkiter(fpin, 128 * 1024):
1218 fp.write(chunk)
1222 fp.write(chunk)
1219 fpin.close()
1223 fpin.close()
1220 fp.close()
1224 fp.close()
1221 err = 0
1225 err = 0
1222 return err
1226 return err
1223
1227
1224 def mergeupdate(orig, repo, node, branchmerge, force, partial,
1228 def mergeupdate(orig, repo, node, branchmerge, force, partial,
1225 *args, **kwargs):
1229 *args, **kwargs):
1226 wlock = repo.wlock()
1230 wlock = repo.wlock()
1227 try:
1231 try:
1228 # branch | | |
1232 # branch | | |
1229 # merge | force | partial | action
1233 # merge | force | partial | action
1230 # -------+-------+---------+--------------
1234 # -------+-------+---------+--------------
1231 # x | x | x | linear-merge
1235 # x | x | x | linear-merge
1232 # o | x | x | branch-merge
1236 # o | x | x | branch-merge
1233 # x | o | x | overwrite (as clean update)
1237 # x | o | x | overwrite (as clean update)
1234 # o | o | x | force-branch-merge (*1)
1238 # o | o | x | force-branch-merge (*1)
1235 # x | x | o | (*)
1239 # x | x | o | (*)
1236 # o | x | o | (*)
1240 # o | x | o | (*)
1237 # x | o | o | overwrite (as revert)
1241 # x | o | o | overwrite (as revert)
1238 # o | o | o | (*)
1242 # o | o | o | (*)
1239 #
1243 #
1240 # (*) don't care
1244 # (*) don't care
1241 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1245 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1242
1246
1243 linearmerge = not branchmerge and not force and not partial
1247 linearmerge = not branchmerge and not force and not partial
1244
1248
1245 if linearmerge or (branchmerge and force and not partial):
1249 if linearmerge or (branchmerge and force and not partial):
1246 # update standins for linear-merge or force-branch-merge,
1250 # update standins for linear-merge or force-branch-merge,
1247 # because largefiles in the working directory may be modified
1251 # because largefiles in the working directory may be modified
1248 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1252 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1249 unsure, s = lfdirstate.status(match_.always(repo.root,
1253 unsure, s = lfdirstate.status(match_.always(repo.root,
1250 repo.getcwd()),
1254 repo.getcwd()),
1251 [], False, False, False)
1255 [], False, False, False)
1252 for lfile in unsure + s.modified + s.added:
1256 for lfile in unsure + s.modified + s.added:
1253 lfutil.updatestandin(repo, lfutil.standin(lfile))
1257 lfutil.updatestandin(repo, lfutil.standin(lfile))
1254
1258
1255 if linearmerge:
1259 if linearmerge:
1256 # Only call updatelfiles on the standins that have changed
1260 # Only call updatelfiles on the standins that have changed
1257 # to save time
1261 # to save time
1258 oldstandins = lfutil.getstandinsstate(repo)
1262 oldstandins = lfutil.getstandinsstate(repo)
1259
1263
1260 result = orig(repo, node, branchmerge, force, partial, *args, **kwargs)
1264 result = orig(repo, node, branchmerge, force, partial, *args, **kwargs)
1261
1265
1262 filelist = None
1266 filelist = None
1263 if linearmerge:
1267 if linearmerge:
1264 newstandins = lfutil.getstandinsstate(repo)
1268 newstandins = lfutil.getstandinsstate(repo)
1265 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1269 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1266
1270
1267 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1271 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1268 normallookup=partial)
1272 normallookup=partial)
1269
1273
1270 return result
1274 return result
1271 finally:
1275 finally:
1272 wlock.release()
1276 wlock.release()
1273
1277
1274 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1278 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1275 result = orig(repo, files, *args, **kwargs)
1279 result = orig(repo, files, *args, **kwargs)
1276
1280
1277 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)]
1278 if filelist:
1282 if filelist:
1279 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1283 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1280 printmessage=False, normallookup=True)
1284 printmessage=False, normallookup=True)
1281
1285
1282 return result
1286 return result
General Comments 0
You need to be logged in to leave comments. Login now