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