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