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