##// END OF EJS Templates
largefiles: use revisions as a ui.progress unit...
av6 -
r28464:6e346902 default
parent child Browse files
Show More
@@ -1,544 +1,544 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 '''High-level command function for lfconvert, plus the cmdtable.'''
9 '''High-level command function for lfconvert, plus the cmdtable.'''
10
10
11 import os, errno
11 import os, errno
12 import shutil
12 import shutil
13
13
14 from mercurial import util, match as match_, hg, node, context, error, \
14 from mercurial import util, match as match_, hg, node, context, error, \
15 cmdutil, scmutil, commands
15 cmdutil, scmutil, commands
16 from mercurial.i18n import _
16 from mercurial.i18n import _
17 from mercurial.lock import release
17 from mercurial.lock import release
18
18
19 from hgext.convert import convcmd
19 from hgext.convert import convcmd
20 from hgext.convert import filemap
20 from hgext.convert import filemap
21
21
22 import lfutil
22 import lfutil
23 import basestore
23 import basestore
24
24
25 # -- Commands ----------------------------------------------------------
25 # -- Commands ----------------------------------------------------------
26
26
27 cmdtable = {}
27 cmdtable = {}
28 command = cmdutil.command(cmdtable)
28 command = cmdutil.command(cmdtable)
29
29
30 @command('lfconvert',
30 @command('lfconvert',
31 [('s', 'size', '',
31 [('s', 'size', '',
32 _('minimum size (MB) for files to be converted as largefiles'), 'SIZE'),
32 _('minimum size (MB) for files to be converted as largefiles'), 'SIZE'),
33 ('', 'to-normal', False,
33 ('', 'to-normal', False,
34 _('convert from a largefiles repo to a normal repo')),
34 _('convert from a largefiles repo to a normal repo')),
35 ],
35 ],
36 _('hg lfconvert SOURCE DEST [FILE ...]'),
36 _('hg lfconvert SOURCE DEST [FILE ...]'),
37 norepo=True,
37 norepo=True,
38 inferrepo=True)
38 inferrepo=True)
39 def lfconvert(ui, src, dest, *pats, **opts):
39 def lfconvert(ui, src, dest, *pats, **opts):
40 '''convert a normal repository to a largefiles repository
40 '''convert a normal repository to a largefiles repository
41
41
42 Convert repository SOURCE to a new repository DEST, identical to
42 Convert repository SOURCE to a new repository DEST, identical to
43 SOURCE except that certain files will be converted as largefiles:
43 SOURCE except that certain files will be converted as largefiles:
44 specifically, any file that matches any PATTERN *or* whose size is
44 specifically, any file that matches any PATTERN *or* whose size is
45 above the minimum size threshold is converted as a largefile. The
45 above the minimum size threshold is converted as a largefile. The
46 size used to determine whether or not to track a file as a
46 size used to determine whether or not to track a file as a
47 largefile is the size of the first version of the file. The
47 largefile is the size of the first version of the file. The
48 minimum size can be specified either with --size or in
48 minimum size can be specified either with --size or in
49 configuration as ``largefiles.size``.
49 configuration as ``largefiles.size``.
50
50
51 After running this command you will need to make sure that
51 After running this command you will need to make sure that
52 largefiles is enabled anywhere you intend to push the new
52 largefiles is enabled anywhere you intend to push the new
53 repository.
53 repository.
54
54
55 Use --to-normal to convert largefiles back to normal files; after
55 Use --to-normal to convert largefiles back to normal files; after
56 this, the DEST repository can be used without largefiles at all.'''
56 this, the DEST repository can be used without largefiles at all.'''
57
57
58 if opts['to_normal']:
58 if opts['to_normal']:
59 tolfile = False
59 tolfile = False
60 else:
60 else:
61 tolfile = True
61 tolfile = True
62 size = lfutil.getminsize(ui, True, opts.get('size'), default=None)
62 size = lfutil.getminsize(ui, True, opts.get('size'), default=None)
63
63
64 if not hg.islocal(src):
64 if not hg.islocal(src):
65 raise error.Abort(_('%s is not a local Mercurial repo') % src)
65 raise error.Abort(_('%s is not a local Mercurial repo') % src)
66 if not hg.islocal(dest):
66 if not hg.islocal(dest):
67 raise error.Abort(_('%s is not a local Mercurial repo') % dest)
67 raise error.Abort(_('%s is not a local Mercurial repo') % dest)
68
68
69 rsrc = hg.repository(ui, src)
69 rsrc = hg.repository(ui, src)
70 ui.status(_('initializing destination %s\n') % dest)
70 ui.status(_('initializing destination %s\n') % dest)
71 rdst = hg.repository(ui, dest, create=True)
71 rdst = hg.repository(ui, dest, create=True)
72
72
73 success = False
73 success = False
74 dstwlock = dstlock = None
74 dstwlock = dstlock = None
75 try:
75 try:
76 # Get a list of all changesets in the source. The easy way to do this
76 # Get a list of all changesets in the source. The easy way to do this
77 # is to simply walk the changelog, using changelog.nodesbetween().
77 # is to simply walk the changelog, using changelog.nodesbetween().
78 # Take a look at mercurial/revlog.py:639 for more details.
78 # Take a look at mercurial/revlog.py:639 for more details.
79 # Use a generator instead of a list to decrease memory usage
79 # Use a generator instead of a list to decrease memory usage
80 ctxs = (rsrc[ctx] for ctx in rsrc.changelog.nodesbetween(None,
80 ctxs = (rsrc[ctx] for ctx in rsrc.changelog.nodesbetween(None,
81 rsrc.heads())[0])
81 rsrc.heads())[0])
82 revmap = {node.nullid: node.nullid}
82 revmap = {node.nullid: node.nullid}
83 if tolfile:
83 if tolfile:
84 # Lock destination to prevent modification while it is converted to.
84 # Lock destination to prevent modification while it is converted to.
85 # Don't need to lock src because we are just reading from its
85 # Don't need to lock src because we are just reading from its
86 # history which can't change.
86 # history which can't change.
87 dstwlock = rdst.wlock()
87 dstwlock = rdst.wlock()
88 dstlock = rdst.lock()
88 dstlock = rdst.lock()
89
89
90 lfiles = set()
90 lfiles = set()
91 normalfiles = set()
91 normalfiles = set()
92 if not pats:
92 if not pats:
93 pats = ui.configlist(lfutil.longname, 'patterns', default=[])
93 pats = ui.configlist(lfutil.longname, 'patterns', default=[])
94 if pats:
94 if pats:
95 matcher = match_.match(rsrc.root, '', list(pats))
95 matcher = match_.match(rsrc.root, '', list(pats))
96 else:
96 else:
97 matcher = None
97 matcher = None
98
98
99 lfiletohash = {}
99 lfiletohash = {}
100 for ctx in ctxs:
100 for ctx in ctxs:
101 ui.progress(_('converting revisions'), ctx.rev(),
101 ui.progress(_('converting revisions'), ctx.rev(),
102 unit=_('revision'), total=rsrc['tip'].rev())
102 unit=_('revisions'), total=rsrc['tip'].rev())
103 _lfconvert_addchangeset(rsrc, rdst, ctx, revmap,
103 _lfconvert_addchangeset(rsrc, rdst, ctx, revmap,
104 lfiles, normalfiles, matcher, size, lfiletohash)
104 lfiles, normalfiles, matcher, size, lfiletohash)
105 ui.progress(_('converting revisions'), None)
105 ui.progress(_('converting revisions'), None)
106
106
107 if os.path.exists(rdst.wjoin(lfutil.shortname)):
107 if os.path.exists(rdst.wjoin(lfutil.shortname)):
108 shutil.rmtree(rdst.wjoin(lfutil.shortname))
108 shutil.rmtree(rdst.wjoin(lfutil.shortname))
109
109
110 for f in lfiletohash.keys():
110 for f in lfiletohash.keys():
111 if os.path.isfile(rdst.wjoin(f)):
111 if os.path.isfile(rdst.wjoin(f)):
112 os.unlink(rdst.wjoin(f))
112 os.unlink(rdst.wjoin(f))
113 try:
113 try:
114 os.removedirs(os.path.dirname(rdst.wjoin(f)))
114 os.removedirs(os.path.dirname(rdst.wjoin(f)))
115 except OSError:
115 except OSError:
116 pass
116 pass
117
117
118 # If there were any files converted to largefiles, add largefiles
118 # If there were any files converted to largefiles, add largefiles
119 # to the destination repository's requirements.
119 # to the destination repository's requirements.
120 if lfiles:
120 if lfiles:
121 rdst.requirements.add('largefiles')
121 rdst.requirements.add('largefiles')
122 rdst._writerequirements()
122 rdst._writerequirements()
123 else:
123 else:
124 class lfsource(filemap.filemap_source):
124 class lfsource(filemap.filemap_source):
125 def __init__(self, ui, source):
125 def __init__(self, ui, source):
126 super(lfsource, self).__init__(ui, source, None)
126 super(lfsource, self).__init__(ui, source, None)
127 self.filemapper.rename[lfutil.shortname] = '.'
127 self.filemapper.rename[lfutil.shortname] = '.'
128
128
129 def getfile(self, name, rev):
129 def getfile(self, name, rev):
130 realname, realrev = rev
130 realname, realrev = rev
131 f = super(lfsource, self).getfile(name, rev)
131 f = super(lfsource, self).getfile(name, rev)
132
132
133 if (not realname.startswith(lfutil.shortnameslash)
133 if (not realname.startswith(lfutil.shortnameslash)
134 or f[0] is None):
134 or f[0] is None):
135 return f
135 return f
136
136
137 # Substitute in the largefile data for the hash
137 # Substitute in the largefile data for the hash
138 hash = f[0].strip()
138 hash = f[0].strip()
139 path = lfutil.findfile(rsrc, hash)
139 path = lfutil.findfile(rsrc, hash)
140
140
141 if path is None:
141 if path is None:
142 raise error.Abort(_("missing largefile for '%s' in %s")
142 raise error.Abort(_("missing largefile for '%s' in %s")
143 % (realname, realrev))
143 % (realname, realrev))
144 return util.readfile(path), f[1]
144 return util.readfile(path), f[1]
145
145
146 class converter(convcmd.converter):
146 class converter(convcmd.converter):
147 def __init__(self, ui, source, dest, revmapfile, opts):
147 def __init__(self, ui, source, dest, revmapfile, opts):
148 src = lfsource(ui, source)
148 src = lfsource(ui, source)
149
149
150 super(converter, self).__init__(ui, src, dest, revmapfile,
150 super(converter, self).__init__(ui, src, dest, revmapfile,
151 opts)
151 opts)
152
152
153 found, missing = downloadlfiles(ui, rsrc)
153 found, missing = downloadlfiles(ui, rsrc)
154 if missing != 0:
154 if missing != 0:
155 raise error.Abort(_("all largefiles must be present locally"))
155 raise error.Abort(_("all largefiles must be present locally"))
156
156
157 orig = convcmd.converter
157 orig = convcmd.converter
158 convcmd.converter = converter
158 convcmd.converter = converter
159
159
160 try:
160 try:
161 convcmd.convert(ui, src, dest)
161 convcmd.convert(ui, src, dest)
162 finally:
162 finally:
163 convcmd.converter = orig
163 convcmd.converter = orig
164 success = True
164 success = True
165 finally:
165 finally:
166 if tolfile:
166 if tolfile:
167 rdst.dirstate.clear()
167 rdst.dirstate.clear()
168 release(dstlock, dstwlock)
168 release(dstlock, dstwlock)
169 if not success:
169 if not success:
170 # we failed, remove the new directory
170 # we failed, remove the new directory
171 shutil.rmtree(rdst.root)
171 shutil.rmtree(rdst.root)
172
172
173 def _lfconvert_addchangeset(rsrc, rdst, ctx, revmap, lfiles, normalfiles,
173 def _lfconvert_addchangeset(rsrc, rdst, ctx, revmap, lfiles, normalfiles,
174 matcher, size, lfiletohash):
174 matcher, size, lfiletohash):
175 # Convert src parents to dst parents
175 # Convert src parents to dst parents
176 parents = _convertparents(ctx, revmap)
176 parents = _convertparents(ctx, revmap)
177
177
178 # Generate list of changed files
178 # Generate list of changed files
179 files = _getchangedfiles(ctx, parents)
179 files = _getchangedfiles(ctx, parents)
180
180
181 dstfiles = []
181 dstfiles = []
182 for f in files:
182 for f in files:
183 if f not in lfiles and f not in normalfiles:
183 if f not in lfiles and f not in normalfiles:
184 islfile = _islfile(f, ctx, matcher, size)
184 islfile = _islfile(f, ctx, matcher, size)
185 # If this file was renamed or copied then copy
185 # If this file was renamed or copied then copy
186 # the largefile-ness of its predecessor
186 # the largefile-ness of its predecessor
187 if f in ctx.manifest():
187 if f in ctx.manifest():
188 fctx = ctx.filectx(f)
188 fctx = ctx.filectx(f)
189 renamed = fctx.renamed()
189 renamed = fctx.renamed()
190 renamedlfile = renamed and renamed[0] in lfiles
190 renamedlfile = renamed and renamed[0] in lfiles
191 islfile |= renamedlfile
191 islfile |= renamedlfile
192 if 'l' in fctx.flags():
192 if 'l' in fctx.flags():
193 if renamedlfile:
193 if renamedlfile:
194 raise error.Abort(
194 raise error.Abort(
195 _('renamed/copied largefile %s becomes symlink')
195 _('renamed/copied largefile %s becomes symlink')
196 % f)
196 % f)
197 islfile = False
197 islfile = False
198 if islfile:
198 if islfile:
199 lfiles.add(f)
199 lfiles.add(f)
200 else:
200 else:
201 normalfiles.add(f)
201 normalfiles.add(f)
202
202
203 if f in lfiles:
203 if f in lfiles:
204 dstfiles.append(lfutil.standin(f))
204 dstfiles.append(lfutil.standin(f))
205 # largefile in manifest if it has not been removed/renamed
205 # largefile in manifest if it has not been removed/renamed
206 if f in ctx.manifest():
206 if f in ctx.manifest():
207 fctx = ctx.filectx(f)
207 fctx = ctx.filectx(f)
208 if 'l' in fctx.flags():
208 if 'l' in fctx.flags():
209 renamed = fctx.renamed()
209 renamed = fctx.renamed()
210 if renamed and renamed[0] in lfiles:
210 if renamed and renamed[0] in lfiles:
211 raise error.Abort(_('largefile %s becomes symlink') % f)
211 raise error.Abort(_('largefile %s becomes symlink') % f)
212
212
213 # largefile was modified, update standins
213 # largefile was modified, update standins
214 m = util.sha1('')
214 m = util.sha1('')
215 m.update(ctx[f].data())
215 m.update(ctx[f].data())
216 hash = m.hexdigest()
216 hash = m.hexdigest()
217 if f not in lfiletohash or lfiletohash[f] != hash:
217 if f not in lfiletohash or lfiletohash[f] != hash:
218 rdst.wwrite(f, ctx[f].data(), ctx[f].flags())
218 rdst.wwrite(f, ctx[f].data(), ctx[f].flags())
219 executable = 'x' in ctx[f].flags()
219 executable = 'x' in ctx[f].flags()
220 lfutil.writestandin(rdst, lfutil.standin(f), hash,
220 lfutil.writestandin(rdst, lfutil.standin(f), hash,
221 executable)
221 executable)
222 lfiletohash[f] = hash
222 lfiletohash[f] = hash
223 else:
223 else:
224 # normal file
224 # normal file
225 dstfiles.append(f)
225 dstfiles.append(f)
226
226
227 def getfilectx(repo, memctx, f):
227 def getfilectx(repo, memctx, f):
228 if lfutil.isstandin(f):
228 if lfutil.isstandin(f):
229 # if the file isn't in the manifest then it was removed
229 # if the file isn't in the manifest then it was removed
230 # or renamed, raise IOError to indicate this
230 # or renamed, raise IOError to indicate this
231 srcfname = lfutil.splitstandin(f)
231 srcfname = lfutil.splitstandin(f)
232 try:
232 try:
233 fctx = ctx.filectx(srcfname)
233 fctx = ctx.filectx(srcfname)
234 except error.LookupError:
234 except error.LookupError:
235 return None
235 return None
236 renamed = fctx.renamed()
236 renamed = fctx.renamed()
237 if renamed:
237 if renamed:
238 # standin is always a largefile because largefile-ness
238 # standin is always a largefile because largefile-ness
239 # doesn't change after rename or copy
239 # doesn't change after rename or copy
240 renamed = lfutil.standin(renamed[0])
240 renamed = lfutil.standin(renamed[0])
241
241
242 return context.memfilectx(repo, f, lfiletohash[srcfname] + '\n',
242 return context.memfilectx(repo, f, lfiletohash[srcfname] + '\n',
243 'l' in fctx.flags(), 'x' in fctx.flags(),
243 'l' in fctx.flags(), 'x' in fctx.flags(),
244 renamed)
244 renamed)
245 else:
245 else:
246 return _getnormalcontext(repo, ctx, f, revmap)
246 return _getnormalcontext(repo, ctx, f, revmap)
247
247
248 # Commit
248 # Commit
249 _commitcontext(rdst, parents, ctx, dstfiles, getfilectx, revmap)
249 _commitcontext(rdst, parents, ctx, dstfiles, getfilectx, revmap)
250
250
251 def _commitcontext(rdst, parents, ctx, dstfiles, getfilectx, revmap):
251 def _commitcontext(rdst, parents, ctx, dstfiles, getfilectx, revmap):
252 mctx = context.memctx(rdst, parents, ctx.description(), dstfiles,
252 mctx = context.memctx(rdst, parents, ctx.description(), dstfiles,
253 getfilectx, ctx.user(), ctx.date(), ctx.extra())
253 getfilectx, ctx.user(), ctx.date(), ctx.extra())
254 ret = rdst.commitctx(mctx)
254 ret = rdst.commitctx(mctx)
255 lfutil.copyalltostore(rdst, ret)
255 lfutil.copyalltostore(rdst, ret)
256 rdst.setparents(ret)
256 rdst.setparents(ret)
257 revmap[ctx.node()] = rdst.changelog.tip()
257 revmap[ctx.node()] = rdst.changelog.tip()
258
258
259 # Generate list of changed files
259 # Generate list of changed files
260 def _getchangedfiles(ctx, parents):
260 def _getchangedfiles(ctx, parents):
261 files = set(ctx.files())
261 files = set(ctx.files())
262 if node.nullid not in parents:
262 if node.nullid not in parents:
263 mc = ctx.manifest()
263 mc = ctx.manifest()
264 mp1 = ctx.parents()[0].manifest()
264 mp1 = ctx.parents()[0].manifest()
265 mp2 = ctx.parents()[1].manifest()
265 mp2 = ctx.parents()[1].manifest()
266 files |= (set(mp1) | set(mp2)) - set(mc)
266 files |= (set(mp1) | set(mp2)) - set(mc)
267 for f in mc:
267 for f in mc:
268 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
268 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
269 files.add(f)
269 files.add(f)
270 return files
270 return files
271
271
272 # Convert src parents to dst parents
272 # Convert src parents to dst parents
273 def _convertparents(ctx, revmap):
273 def _convertparents(ctx, revmap):
274 parents = []
274 parents = []
275 for p in ctx.parents():
275 for p in ctx.parents():
276 parents.append(revmap[p.node()])
276 parents.append(revmap[p.node()])
277 while len(parents) < 2:
277 while len(parents) < 2:
278 parents.append(node.nullid)
278 parents.append(node.nullid)
279 return parents
279 return parents
280
280
281 # Get memfilectx for a normal file
281 # Get memfilectx for a normal file
282 def _getnormalcontext(repo, ctx, f, revmap):
282 def _getnormalcontext(repo, ctx, f, revmap):
283 try:
283 try:
284 fctx = ctx.filectx(f)
284 fctx = ctx.filectx(f)
285 except error.LookupError:
285 except error.LookupError:
286 return None
286 return None
287 renamed = fctx.renamed()
287 renamed = fctx.renamed()
288 if renamed:
288 if renamed:
289 renamed = renamed[0]
289 renamed = renamed[0]
290
290
291 data = fctx.data()
291 data = fctx.data()
292 if f == '.hgtags':
292 if f == '.hgtags':
293 data = _converttags (repo.ui, revmap, data)
293 data = _converttags (repo.ui, revmap, data)
294 return context.memfilectx(repo, f, data, 'l' in fctx.flags(),
294 return context.memfilectx(repo, f, data, 'l' in fctx.flags(),
295 'x' in fctx.flags(), renamed)
295 'x' in fctx.flags(), renamed)
296
296
297 # Remap tag data using a revision map
297 # Remap tag data using a revision map
298 def _converttags(ui, revmap, data):
298 def _converttags(ui, revmap, data):
299 newdata = []
299 newdata = []
300 for line in data.splitlines():
300 for line in data.splitlines():
301 try:
301 try:
302 id, name = line.split(' ', 1)
302 id, name = line.split(' ', 1)
303 except ValueError:
303 except ValueError:
304 ui.warn(_('skipping incorrectly formatted tag %s\n')
304 ui.warn(_('skipping incorrectly formatted tag %s\n')
305 % line)
305 % line)
306 continue
306 continue
307 try:
307 try:
308 newid = node.bin(id)
308 newid = node.bin(id)
309 except TypeError:
309 except TypeError:
310 ui.warn(_('skipping incorrectly formatted id %s\n')
310 ui.warn(_('skipping incorrectly formatted id %s\n')
311 % id)
311 % id)
312 continue
312 continue
313 try:
313 try:
314 newdata.append('%s %s\n' % (node.hex(revmap[newid]),
314 newdata.append('%s %s\n' % (node.hex(revmap[newid]),
315 name))
315 name))
316 except KeyError:
316 except KeyError:
317 ui.warn(_('no mapping for id %s\n') % id)
317 ui.warn(_('no mapping for id %s\n') % id)
318 continue
318 continue
319 return ''.join(newdata)
319 return ''.join(newdata)
320
320
321 def _islfile(file, ctx, matcher, size):
321 def _islfile(file, ctx, matcher, size):
322 '''Return true if file should be considered a largefile, i.e.
322 '''Return true if file should be considered a largefile, i.e.
323 matcher matches it or it is larger than size.'''
323 matcher matches it or it is larger than size.'''
324 # never store special .hg* files as largefiles
324 # never store special .hg* files as largefiles
325 if file == '.hgtags' or file == '.hgignore' or file == '.hgsigs':
325 if file == '.hgtags' or file == '.hgignore' or file == '.hgsigs':
326 return False
326 return False
327 if matcher and matcher(file):
327 if matcher and matcher(file):
328 return True
328 return True
329 try:
329 try:
330 return ctx.filectx(file).size() >= size * 1024 * 1024
330 return ctx.filectx(file).size() >= size * 1024 * 1024
331 except error.LookupError:
331 except error.LookupError:
332 return False
332 return False
333
333
334 def uploadlfiles(ui, rsrc, rdst, files):
334 def uploadlfiles(ui, rsrc, rdst, files):
335 '''upload largefiles to the central store'''
335 '''upload largefiles to the central store'''
336
336
337 if not files:
337 if not files:
338 return
338 return
339
339
340 store = basestore._openstore(rsrc, rdst, put=True)
340 store = basestore._openstore(rsrc, rdst, put=True)
341
341
342 at = 0
342 at = 0
343 ui.debug("sending statlfile command for %d largefiles\n" % len(files))
343 ui.debug("sending statlfile command for %d largefiles\n" % len(files))
344 retval = store.exists(files)
344 retval = store.exists(files)
345 files = filter(lambda h: not retval[h], files)
345 files = filter(lambda h: not retval[h], files)
346 ui.debug("%d largefiles need to be uploaded\n" % len(files))
346 ui.debug("%d largefiles need to be uploaded\n" % len(files))
347
347
348 for hash in files:
348 for hash in files:
349 ui.progress(_('uploading largefiles'), at, unit=_('files'),
349 ui.progress(_('uploading largefiles'), at, unit=_('files'),
350 total=len(files))
350 total=len(files))
351 source = lfutil.findfile(rsrc, hash)
351 source = lfutil.findfile(rsrc, hash)
352 if not source:
352 if not source:
353 raise error.Abort(_('largefile %s missing from store'
353 raise error.Abort(_('largefile %s missing from store'
354 ' (needs to be uploaded)') % hash)
354 ' (needs to be uploaded)') % hash)
355 # XXX check for errors here
355 # XXX check for errors here
356 store.put(source, hash)
356 store.put(source, hash)
357 at += 1
357 at += 1
358 ui.progress(_('uploading largefiles'), None)
358 ui.progress(_('uploading largefiles'), None)
359
359
360 def verifylfiles(ui, repo, all=False, contents=False):
360 def verifylfiles(ui, repo, all=False, contents=False):
361 '''Verify that every largefile revision in the current changeset
361 '''Verify that every largefile revision in the current changeset
362 exists in the central store. With --contents, also verify that
362 exists in the central store. With --contents, also verify that
363 the contents of each local largefile file revision are correct (SHA-1 hash
363 the contents of each local largefile file revision are correct (SHA-1 hash
364 matches the revision ID). With --all, check every changeset in
364 matches the revision ID). With --all, check every changeset in
365 this repository.'''
365 this repository.'''
366 if all:
366 if all:
367 revs = repo.revs('all()')
367 revs = repo.revs('all()')
368 else:
368 else:
369 revs = ['.']
369 revs = ['.']
370
370
371 store = basestore._openstore(repo)
371 store = basestore._openstore(repo)
372 return store.verify(revs, contents=contents)
372 return store.verify(revs, contents=contents)
373
373
374 def cachelfiles(ui, repo, node, filelist=None):
374 def cachelfiles(ui, repo, node, filelist=None):
375 '''cachelfiles ensures that all largefiles needed by the specified revision
375 '''cachelfiles ensures that all largefiles needed by the specified revision
376 are present in the repository's largefile cache.
376 are present in the repository's largefile cache.
377
377
378 returns a tuple (cached, missing). cached is the list of files downloaded
378 returns a tuple (cached, missing). cached is the list of files downloaded
379 by this operation; missing is the list of files that were needed but could
379 by this operation; missing is the list of files that were needed but could
380 not be found.'''
380 not be found.'''
381 lfiles = lfutil.listlfiles(repo, node)
381 lfiles = lfutil.listlfiles(repo, node)
382 if filelist:
382 if filelist:
383 lfiles = set(lfiles) & set(filelist)
383 lfiles = set(lfiles) & set(filelist)
384 toget = []
384 toget = []
385
385
386 for lfile in lfiles:
386 for lfile in lfiles:
387 try:
387 try:
388 expectedhash = repo[node][lfutil.standin(lfile)].data().strip()
388 expectedhash = repo[node][lfutil.standin(lfile)].data().strip()
389 except IOError as err:
389 except IOError as err:
390 if err.errno == errno.ENOENT:
390 if err.errno == errno.ENOENT:
391 continue # node must be None and standin wasn't found in wctx
391 continue # node must be None and standin wasn't found in wctx
392 raise
392 raise
393 if not lfutil.findfile(repo, expectedhash):
393 if not lfutil.findfile(repo, expectedhash):
394 toget.append((lfile, expectedhash))
394 toget.append((lfile, expectedhash))
395
395
396 if toget:
396 if toget:
397 store = basestore._openstore(repo)
397 store = basestore._openstore(repo)
398 ret = store.get(toget)
398 ret = store.get(toget)
399 return ret
399 return ret
400
400
401 return ([], [])
401 return ([], [])
402
402
403 def downloadlfiles(ui, repo, rev=None):
403 def downloadlfiles(ui, repo, rev=None):
404 matchfn = scmutil.match(repo[None],
404 matchfn = scmutil.match(repo[None],
405 [repo.wjoin(lfutil.shortname)], {})
405 [repo.wjoin(lfutil.shortname)], {})
406 def prepare(ctx, fns):
406 def prepare(ctx, fns):
407 pass
407 pass
408 totalsuccess = 0
408 totalsuccess = 0
409 totalmissing = 0
409 totalmissing = 0
410 if rev != []: # walkchangerevs on empty list would return all revs
410 if rev != []: # walkchangerevs on empty list would return all revs
411 for ctx in cmdutil.walkchangerevs(repo, matchfn, {'rev' : rev},
411 for ctx in cmdutil.walkchangerevs(repo, matchfn, {'rev' : rev},
412 prepare):
412 prepare):
413 success, missing = cachelfiles(ui, repo, ctx.node())
413 success, missing = cachelfiles(ui, repo, ctx.node())
414 totalsuccess += len(success)
414 totalsuccess += len(success)
415 totalmissing += len(missing)
415 totalmissing += len(missing)
416 ui.status(_("%d additional largefiles cached\n") % totalsuccess)
416 ui.status(_("%d additional largefiles cached\n") % totalsuccess)
417 if totalmissing > 0:
417 if totalmissing > 0:
418 ui.status(_("%d largefiles failed to download\n") % totalmissing)
418 ui.status(_("%d largefiles failed to download\n") % totalmissing)
419 return totalsuccess, totalmissing
419 return totalsuccess, totalmissing
420
420
421 def updatelfiles(ui, repo, filelist=None, printmessage=None,
421 def updatelfiles(ui, repo, filelist=None, printmessage=None,
422 normallookup=False):
422 normallookup=False):
423 '''Update largefiles according to standins in the working directory
423 '''Update largefiles according to standins in the working directory
424
424
425 If ``printmessage`` is other than ``None``, it means "print (or
425 If ``printmessage`` is other than ``None``, it means "print (or
426 ignore, for false) message forcibly".
426 ignore, for false) message forcibly".
427 '''
427 '''
428 statuswriter = lfutil.getstatuswriter(ui, repo, printmessage)
428 statuswriter = lfutil.getstatuswriter(ui, repo, printmessage)
429 with repo.wlock():
429 with repo.wlock():
430 lfdirstate = lfutil.openlfdirstate(ui, repo)
430 lfdirstate = lfutil.openlfdirstate(ui, repo)
431 lfiles = set(lfutil.listlfiles(repo)) | set(lfdirstate)
431 lfiles = set(lfutil.listlfiles(repo)) | set(lfdirstate)
432
432
433 if filelist is not None:
433 if filelist is not None:
434 filelist = set(filelist)
434 filelist = set(filelist)
435 lfiles = [f for f in lfiles if f in filelist]
435 lfiles = [f for f in lfiles if f in filelist]
436
436
437 update = {}
437 update = {}
438 updated, removed = 0, 0
438 updated, removed = 0, 0
439 for lfile in lfiles:
439 for lfile in lfiles:
440 abslfile = repo.wjoin(lfile)
440 abslfile = repo.wjoin(lfile)
441 abslfileorig = scmutil.origpath(ui, repo, abslfile)
441 abslfileorig = scmutil.origpath(ui, repo, abslfile)
442 absstandin = repo.wjoin(lfutil.standin(lfile))
442 absstandin = repo.wjoin(lfutil.standin(lfile))
443 absstandinorig = scmutil.origpath(ui, repo, absstandin)
443 absstandinorig = scmutil.origpath(ui, repo, absstandin)
444 if os.path.exists(absstandin):
444 if os.path.exists(absstandin):
445 if (os.path.exists(absstandinorig) and
445 if (os.path.exists(absstandinorig) and
446 os.path.exists(abslfile)):
446 os.path.exists(abslfile)):
447 shutil.copyfile(abslfile, abslfileorig)
447 shutil.copyfile(abslfile, abslfileorig)
448 util.unlinkpath(absstandinorig)
448 util.unlinkpath(absstandinorig)
449 expecthash = lfutil.readstandin(repo, lfile)
449 expecthash = lfutil.readstandin(repo, lfile)
450 if expecthash != '':
450 if expecthash != '':
451 if lfile not in repo[None]: # not switched to normal file
451 if lfile not in repo[None]: # not switched to normal file
452 util.unlinkpath(abslfile, ignoremissing=True)
452 util.unlinkpath(abslfile, ignoremissing=True)
453 # use normallookup() to allocate an entry in largefiles
453 # use normallookup() to allocate an entry in largefiles
454 # dirstate to prevent lfilesrepo.status() from reporting
454 # dirstate to prevent lfilesrepo.status() from reporting
455 # missing files as removed.
455 # missing files as removed.
456 lfdirstate.normallookup(lfile)
456 lfdirstate.normallookup(lfile)
457 update[lfile] = expecthash
457 update[lfile] = expecthash
458 else:
458 else:
459 # Remove lfiles for which the standin is deleted, unless the
459 # Remove lfiles for which the standin is deleted, unless the
460 # lfile is added to the repository again. This happens when a
460 # lfile is added to the repository again. This happens when a
461 # largefile is converted back to a normal file: the standin
461 # largefile is converted back to a normal file: the standin
462 # disappears, but a new (normal) file appears as the lfile.
462 # disappears, but a new (normal) file appears as the lfile.
463 if (os.path.exists(abslfile) and
463 if (os.path.exists(abslfile) and
464 repo.dirstate.normalize(lfile) not in repo[None]):
464 repo.dirstate.normalize(lfile) not in repo[None]):
465 util.unlinkpath(abslfile)
465 util.unlinkpath(abslfile)
466 removed += 1
466 removed += 1
467
467
468 # largefile processing might be slow and be interrupted - be prepared
468 # largefile processing might be slow and be interrupted - be prepared
469 lfdirstate.write()
469 lfdirstate.write()
470
470
471 if lfiles:
471 if lfiles:
472 statuswriter(_('getting changed largefiles\n'))
472 statuswriter(_('getting changed largefiles\n'))
473 cachelfiles(ui, repo, None, lfiles)
473 cachelfiles(ui, repo, None, lfiles)
474
474
475 for lfile in lfiles:
475 for lfile in lfiles:
476 update1 = 0
476 update1 = 0
477
477
478 expecthash = update.get(lfile)
478 expecthash = update.get(lfile)
479 if expecthash:
479 if expecthash:
480 if not lfutil.copyfromcache(repo, expecthash, lfile):
480 if not lfutil.copyfromcache(repo, expecthash, lfile):
481 # failed ... but already removed and set to normallookup
481 # failed ... but already removed and set to normallookup
482 continue
482 continue
483 # Synchronize largefile dirstate to the last modified
483 # Synchronize largefile dirstate to the last modified
484 # time of the file
484 # time of the file
485 lfdirstate.normal(lfile)
485 lfdirstate.normal(lfile)
486 update1 = 1
486 update1 = 1
487
487
488 # copy the state of largefile standin from the repository's
488 # copy the state of largefile standin from the repository's
489 # dirstate to its state in the lfdirstate.
489 # dirstate to its state in the lfdirstate.
490 abslfile = repo.wjoin(lfile)
490 abslfile = repo.wjoin(lfile)
491 absstandin = repo.wjoin(lfutil.standin(lfile))
491 absstandin = repo.wjoin(lfutil.standin(lfile))
492 if os.path.exists(absstandin):
492 if os.path.exists(absstandin):
493 mode = os.stat(absstandin).st_mode
493 mode = os.stat(absstandin).st_mode
494 if mode != os.stat(abslfile).st_mode:
494 if mode != os.stat(abslfile).st_mode:
495 os.chmod(abslfile, mode)
495 os.chmod(abslfile, mode)
496 update1 = 1
496 update1 = 1
497
497
498 updated += update1
498 updated += update1
499
499
500 lfutil.synclfdirstate(repo, lfdirstate, lfile, normallookup)
500 lfutil.synclfdirstate(repo, lfdirstate, lfile, normallookup)
501
501
502 lfdirstate.write()
502 lfdirstate.write()
503 if lfiles:
503 if lfiles:
504 statuswriter(_('%d largefiles updated, %d removed\n') % (updated,
504 statuswriter(_('%d largefiles updated, %d removed\n') % (updated,
505 removed))
505 removed))
506
506
507 @command('lfpull',
507 @command('lfpull',
508 [('r', 'rev', [], _('pull largefiles for these revisions'))
508 [('r', 'rev', [], _('pull largefiles for these revisions'))
509 ] + commands.remoteopts,
509 ] + commands.remoteopts,
510 _('-r REV... [-e CMD] [--remotecmd CMD] [SOURCE]'))
510 _('-r REV... [-e CMD] [--remotecmd CMD] [SOURCE]'))
511 def lfpull(ui, repo, source="default", **opts):
511 def lfpull(ui, repo, source="default", **opts):
512 """pull largefiles for the specified revisions from the specified source
512 """pull largefiles for the specified revisions from the specified source
513
513
514 Pull largefiles that are referenced from local changesets but missing
514 Pull largefiles that are referenced from local changesets but missing
515 locally, pulling from a remote repository to the local cache.
515 locally, pulling from a remote repository to the local cache.
516
516
517 If SOURCE is omitted, the 'default' path will be used.
517 If SOURCE is omitted, the 'default' path will be used.
518 See :hg:`help urls` for more information.
518 See :hg:`help urls` for more information.
519
519
520 .. container:: verbose
520 .. container:: verbose
521
521
522 Some examples:
522 Some examples:
523
523
524 - pull largefiles for all branch heads::
524 - pull largefiles for all branch heads::
525
525
526 hg lfpull -r "head() and not closed()"
526 hg lfpull -r "head() and not closed()"
527
527
528 - pull largefiles on the default branch::
528 - pull largefiles on the default branch::
529
529
530 hg lfpull -r "branch(default)"
530 hg lfpull -r "branch(default)"
531 """
531 """
532 repo.lfpullsource = source
532 repo.lfpullsource = source
533
533
534 revs = opts.get('rev', [])
534 revs = opts.get('rev', [])
535 if not revs:
535 if not revs:
536 raise error.Abort(_('no revisions specified'))
536 raise error.Abort(_('no revisions specified'))
537 revs = scmutil.revrange(repo, revs)
537 revs = scmutil.revrange(repo, revs)
538
538
539 numcached = 0
539 numcached = 0
540 for rev in revs:
540 for rev in revs:
541 ui.note(_('pulling largefiles for revision %s\n') % rev)
541 ui.note(_('pulling largefiles for revision %s\n') % rev)
542 (cached, missing) = cachelfiles(ui, repo, rev)
542 (cached, missing) = cachelfiles(ui, repo, rev)
543 numcached += len(cached)
543 numcached += len(cached)
544 ui.status(_("%d largefiles cached\n") % numcached)
544 ui.status(_("%d largefiles cached\n") % numcached)
@@ -1,639 +1,639 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 '''largefiles utility code: must not import other modules in this package.'''
9 '''largefiles utility code: must not import other modules in this package.'''
10
10
11 import os
11 import os
12 import platform
12 import platform
13 import stat
13 import stat
14 import copy
14 import copy
15
15
16 from mercurial import dirstate, httpconnection, match as match_, util, scmutil
16 from mercurial import dirstate, httpconnection, match as match_, util, scmutil
17 from mercurial.i18n import _
17 from mercurial.i18n import _
18 from mercurial import node, error
18 from mercurial import node, error
19
19
20 shortname = '.hglf'
20 shortname = '.hglf'
21 shortnameslash = shortname + '/'
21 shortnameslash = shortname + '/'
22 longname = 'largefiles'
22 longname = 'largefiles'
23
23
24
24
25 # -- Private worker functions ------------------------------------------
25 # -- Private worker functions ------------------------------------------
26
26
27 def getminsize(ui, assumelfiles, opt, default=10):
27 def getminsize(ui, assumelfiles, opt, default=10):
28 lfsize = opt
28 lfsize = opt
29 if not lfsize and assumelfiles:
29 if not lfsize and assumelfiles:
30 lfsize = ui.config(longname, 'minsize', default=default)
30 lfsize = ui.config(longname, 'minsize', default=default)
31 if lfsize:
31 if lfsize:
32 try:
32 try:
33 lfsize = float(lfsize)
33 lfsize = float(lfsize)
34 except ValueError:
34 except ValueError:
35 raise error.Abort(_('largefiles: size must be number (not %s)\n')
35 raise error.Abort(_('largefiles: size must be number (not %s)\n')
36 % lfsize)
36 % lfsize)
37 if lfsize is None:
37 if lfsize is None:
38 raise error.Abort(_('minimum size for largefiles must be specified'))
38 raise error.Abort(_('minimum size for largefiles must be specified'))
39 return lfsize
39 return lfsize
40
40
41 def link(src, dest):
41 def link(src, dest):
42 util.makedirs(os.path.dirname(dest))
42 util.makedirs(os.path.dirname(dest))
43 try:
43 try:
44 util.oslink(src, dest)
44 util.oslink(src, dest)
45 except OSError:
45 except OSError:
46 # if hardlinks fail, fallback on atomic copy
46 # if hardlinks fail, fallback on atomic copy
47 dst = util.atomictempfile(dest)
47 dst = util.atomictempfile(dest)
48 for chunk in util.filechunkiter(open(src, 'rb')):
48 for chunk in util.filechunkiter(open(src, 'rb')):
49 dst.write(chunk)
49 dst.write(chunk)
50 dst.close()
50 dst.close()
51 os.chmod(dest, os.stat(src).st_mode)
51 os.chmod(dest, os.stat(src).st_mode)
52
52
53 def usercachepath(ui, hash):
53 def usercachepath(ui, hash):
54 path = ui.configpath(longname, 'usercache', None)
54 path = ui.configpath(longname, 'usercache', None)
55 if path:
55 if path:
56 path = os.path.join(path, hash)
56 path = os.path.join(path, hash)
57 else:
57 else:
58 if os.name == 'nt':
58 if os.name == 'nt':
59 appdata = os.getenv('LOCALAPPDATA', os.getenv('APPDATA'))
59 appdata = os.getenv('LOCALAPPDATA', os.getenv('APPDATA'))
60 if appdata:
60 if appdata:
61 path = os.path.join(appdata, longname, hash)
61 path = os.path.join(appdata, longname, hash)
62 elif platform.system() == 'Darwin':
62 elif platform.system() == 'Darwin':
63 home = os.getenv('HOME')
63 home = os.getenv('HOME')
64 if home:
64 if home:
65 path = os.path.join(home, 'Library', 'Caches',
65 path = os.path.join(home, 'Library', 'Caches',
66 longname, hash)
66 longname, hash)
67 elif os.name == 'posix':
67 elif os.name == 'posix':
68 path = os.getenv('XDG_CACHE_HOME')
68 path = os.getenv('XDG_CACHE_HOME')
69 if path:
69 if path:
70 path = os.path.join(path, longname, hash)
70 path = os.path.join(path, longname, hash)
71 else:
71 else:
72 home = os.getenv('HOME')
72 home = os.getenv('HOME')
73 if home:
73 if home:
74 path = os.path.join(home, '.cache', longname, hash)
74 path = os.path.join(home, '.cache', longname, hash)
75 else:
75 else:
76 raise error.Abort(_('unknown operating system: %s\n') % os.name)
76 raise error.Abort(_('unknown operating system: %s\n') % os.name)
77 return path
77 return path
78
78
79 def inusercache(ui, hash):
79 def inusercache(ui, hash):
80 path = usercachepath(ui, hash)
80 path = usercachepath(ui, hash)
81 return path and os.path.exists(path)
81 return path and os.path.exists(path)
82
82
83 def findfile(repo, hash):
83 def findfile(repo, hash):
84 path, exists = findstorepath(repo, hash)
84 path, exists = findstorepath(repo, hash)
85 if exists:
85 if exists:
86 repo.ui.note(_('found %s in store\n') % hash)
86 repo.ui.note(_('found %s in store\n') % hash)
87 return path
87 return path
88 elif inusercache(repo.ui, hash):
88 elif inusercache(repo.ui, hash):
89 repo.ui.note(_('found %s in system cache\n') % hash)
89 repo.ui.note(_('found %s in system cache\n') % hash)
90 path = storepath(repo, hash)
90 path = storepath(repo, hash)
91 link(usercachepath(repo.ui, hash), path)
91 link(usercachepath(repo.ui, hash), path)
92 return path
92 return path
93 return None
93 return None
94
94
95 class largefilesdirstate(dirstate.dirstate):
95 class largefilesdirstate(dirstate.dirstate):
96 def __getitem__(self, key):
96 def __getitem__(self, key):
97 return super(largefilesdirstate, self).__getitem__(unixpath(key))
97 return super(largefilesdirstate, self).__getitem__(unixpath(key))
98 def normal(self, f):
98 def normal(self, f):
99 return super(largefilesdirstate, self).normal(unixpath(f))
99 return super(largefilesdirstate, self).normal(unixpath(f))
100 def remove(self, f):
100 def remove(self, f):
101 return super(largefilesdirstate, self).remove(unixpath(f))
101 return super(largefilesdirstate, self).remove(unixpath(f))
102 def add(self, f):
102 def add(self, f):
103 return super(largefilesdirstate, self).add(unixpath(f))
103 return super(largefilesdirstate, self).add(unixpath(f))
104 def drop(self, f):
104 def drop(self, f):
105 return super(largefilesdirstate, self).drop(unixpath(f))
105 return super(largefilesdirstate, self).drop(unixpath(f))
106 def forget(self, f):
106 def forget(self, f):
107 return super(largefilesdirstate, self).forget(unixpath(f))
107 return super(largefilesdirstate, self).forget(unixpath(f))
108 def normallookup(self, f):
108 def normallookup(self, f):
109 return super(largefilesdirstate, self).normallookup(unixpath(f))
109 return super(largefilesdirstate, self).normallookup(unixpath(f))
110 def _ignore(self, f):
110 def _ignore(self, f):
111 return False
111 return False
112 def write(self, tr=False):
112 def write(self, tr=False):
113 # (1) disable PENDING mode always
113 # (1) disable PENDING mode always
114 # (lfdirstate isn't yet managed as a part of the transaction)
114 # (lfdirstate isn't yet managed as a part of the transaction)
115 # (2) avoid develwarn 'use dirstate.write with ....'
115 # (2) avoid develwarn 'use dirstate.write with ....'
116 super(largefilesdirstate, self).write(None)
116 super(largefilesdirstate, self).write(None)
117
117
118 def openlfdirstate(ui, repo, create=True):
118 def openlfdirstate(ui, repo, create=True):
119 '''
119 '''
120 Return a dirstate object that tracks largefiles: i.e. its root is
120 Return a dirstate object that tracks largefiles: i.e. its root is
121 the repo root, but it is saved in .hg/largefiles/dirstate.
121 the repo root, but it is saved in .hg/largefiles/dirstate.
122 '''
122 '''
123 lfstoredir = repo.join(longname)
123 lfstoredir = repo.join(longname)
124 opener = scmutil.opener(lfstoredir)
124 opener = scmutil.opener(lfstoredir)
125 lfdirstate = largefilesdirstate(opener, ui, repo.root,
125 lfdirstate = largefilesdirstate(opener, ui, repo.root,
126 repo.dirstate._validate)
126 repo.dirstate._validate)
127
127
128 # If the largefiles dirstate does not exist, populate and create
128 # If the largefiles dirstate does not exist, populate and create
129 # it. This ensures that we create it on the first meaningful
129 # it. This ensures that we create it on the first meaningful
130 # largefiles operation in a new clone.
130 # largefiles operation in a new clone.
131 if create and not os.path.exists(os.path.join(lfstoredir, 'dirstate')):
131 if create and not os.path.exists(os.path.join(lfstoredir, 'dirstate')):
132 matcher = getstandinmatcher(repo)
132 matcher = getstandinmatcher(repo)
133 standins = repo.dirstate.walk(matcher, [], False, False)
133 standins = repo.dirstate.walk(matcher, [], False, False)
134
134
135 if len(standins) > 0:
135 if len(standins) > 0:
136 util.makedirs(lfstoredir)
136 util.makedirs(lfstoredir)
137
137
138 for standin in standins:
138 for standin in standins:
139 lfile = splitstandin(standin)
139 lfile = splitstandin(standin)
140 lfdirstate.normallookup(lfile)
140 lfdirstate.normallookup(lfile)
141 return lfdirstate
141 return lfdirstate
142
142
143 def lfdirstatestatus(lfdirstate, repo):
143 def lfdirstatestatus(lfdirstate, repo):
144 wctx = repo['.']
144 wctx = repo['.']
145 match = match_.always(repo.root, repo.getcwd())
145 match = match_.always(repo.root, repo.getcwd())
146 unsure, s = lfdirstate.status(match, [], False, False, False)
146 unsure, s = lfdirstate.status(match, [], False, False, False)
147 modified, clean = s.modified, s.clean
147 modified, clean = s.modified, s.clean
148 for lfile in unsure:
148 for lfile in unsure:
149 try:
149 try:
150 fctx = wctx[standin(lfile)]
150 fctx = wctx[standin(lfile)]
151 except LookupError:
151 except LookupError:
152 fctx = None
152 fctx = None
153 if not fctx or fctx.data().strip() != hashfile(repo.wjoin(lfile)):
153 if not fctx or fctx.data().strip() != hashfile(repo.wjoin(lfile)):
154 modified.append(lfile)
154 modified.append(lfile)
155 else:
155 else:
156 clean.append(lfile)
156 clean.append(lfile)
157 lfdirstate.normal(lfile)
157 lfdirstate.normal(lfile)
158 return s
158 return s
159
159
160 def listlfiles(repo, rev=None, matcher=None):
160 def listlfiles(repo, rev=None, matcher=None):
161 '''return a list of largefiles in the working copy or the
161 '''return a list of largefiles in the working copy or the
162 specified changeset'''
162 specified changeset'''
163
163
164 if matcher is None:
164 if matcher is None:
165 matcher = getstandinmatcher(repo)
165 matcher = getstandinmatcher(repo)
166
166
167 # ignore unknown files in working directory
167 # ignore unknown files in working directory
168 return [splitstandin(f)
168 return [splitstandin(f)
169 for f in repo[rev].walk(matcher)
169 for f in repo[rev].walk(matcher)
170 if rev is not None or repo.dirstate[f] != '?']
170 if rev is not None or repo.dirstate[f] != '?']
171
171
172 def instore(repo, hash, forcelocal=False):
172 def instore(repo, hash, forcelocal=False):
173 return os.path.exists(storepath(repo, hash, forcelocal))
173 return os.path.exists(storepath(repo, hash, forcelocal))
174
174
175 def storepath(repo, hash, forcelocal=False):
175 def storepath(repo, hash, forcelocal=False):
176 if not forcelocal and repo.shared():
176 if not forcelocal and repo.shared():
177 return repo.vfs.reljoin(repo.sharedpath, longname, hash)
177 return repo.vfs.reljoin(repo.sharedpath, longname, hash)
178 return repo.join(longname, hash)
178 return repo.join(longname, hash)
179
179
180 def findstorepath(repo, hash):
180 def findstorepath(repo, hash):
181 '''Search through the local store path(s) to find the file for the given
181 '''Search through the local store path(s) to find the file for the given
182 hash. If the file is not found, its path in the primary store is returned.
182 hash. If the file is not found, its path in the primary store is returned.
183 The return value is a tuple of (path, exists(path)).
183 The return value is a tuple of (path, exists(path)).
184 '''
184 '''
185 # For shared repos, the primary store is in the share source. But for
185 # For shared repos, the primary store is in the share source. But for
186 # backward compatibility, force a lookup in the local store if it wasn't
186 # backward compatibility, force a lookup in the local store if it wasn't
187 # found in the share source.
187 # found in the share source.
188 path = storepath(repo, hash, False)
188 path = storepath(repo, hash, False)
189
189
190 if instore(repo, hash):
190 if instore(repo, hash):
191 return (path, True)
191 return (path, True)
192 elif repo.shared() and instore(repo, hash, True):
192 elif repo.shared() and instore(repo, hash, True):
193 return storepath(repo, hash, True)
193 return storepath(repo, hash, True)
194
194
195 return (path, False)
195 return (path, False)
196
196
197 def copyfromcache(repo, hash, filename):
197 def copyfromcache(repo, hash, filename):
198 '''Copy the specified largefile from the repo or system cache to
198 '''Copy the specified largefile from the repo or system cache to
199 filename in the repository. Return true on success or false if the
199 filename in the repository. Return true on success or false if the
200 file was not found in either cache (which should not happened:
200 file was not found in either cache (which should not happened:
201 this is meant to be called only after ensuring that the needed
201 this is meant to be called only after ensuring that the needed
202 largefile exists in the cache).'''
202 largefile exists in the cache).'''
203 path = findfile(repo, hash)
203 path = findfile(repo, hash)
204 if path is None:
204 if path is None:
205 return False
205 return False
206 util.makedirs(os.path.dirname(repo.wjoin(filename)))
206 util.makedirs(os.path.dirname(repo.wjoin(filename)))
207 # The write may fail before the file is fully written, but we
207 # The write may fail before the file is fully written, but we
208 # don't use atomic writes in the working copy.
208 # don't use atomic writes in the working copy.
209 dest = repo.wjoin(filename)
209 dest = repo.wjoin(filename)
210 with open(path, 'rb') as srcfd:
210 with open(path, 'rb') as srcfd:
211 with open(dest, 'wb') as destfd:
211 with open(dest, 'wb') as destfd:
212 gothash = copyandhash(srcfd, destfd)
212 gothash = copyandhash(srcfd, destfd)
213 if gothash != hash:
213 if gothash != hash:
214 repo.ui.warn(_('%s: data corruption in %s with hash %s\n')
214 repo.ui.warn(_('%s: data corruption in %s with hash %s\n')
215 % (filename, path, gothash))
215 % (filename, path, gothash))
216 util.unlink(dest)
216 util.unlink(dest)
217 return False
217 return False
218 return True
218 return True
219
219
220 def copytostore(repo, rev, file, uploaded=False):
220 def copytostore(repo, rev, file, uploaded=False):
221 hash = readstandin(repo, file, rev)
221 hash = readstandin(repo, file, rev)
222 if instore(repo, hash):
222 if instore(repo, hash):
223 return
223 return
224 absfile = repo.wjoin(file)
224 absfile = repo.wjoin(file)
225 if os.path.exists(absfile):
225 if os.path.exists(absfile):
226 copytostoreabsolute(repo, absfile, hash)
226 copytostoreabsolute(repo, absfile, hash)
227 else:
227 else:
228 repo.ui.warn(_("%s: largefile %s not available from local store\n") %
228 repo.ui.warn(_("%s: largefile %s not available from local store\n") %
229 (file, hash))
229 (file, hash))
230
230
231 def copyalltostore(repo, node):
231 def copyalltostore(repo, node):
232 '''Copy all largefiles in a given revision to the store'''
232 '''Copy all largefiles in a given revision to the store'''
233
233
234 ctx = repo[node]
234 ctx = repo[node]
235 for filename in ctx.files():
235 for filename in ctx.files():
236 if isstandin(filename) and filename in ctx.manifest():
236 if isstandin(filename) and filename in ctx.manifest():
237 realfile = splitstandin(filename)
237 realfile = splitstandin(filename)
238 copytostore(repo, ctx.node(), realfile)
238 copytostore(repo, ctx.node(), realfile)
239
239
240
240
241 def copytostoreabsolute(repo, file, hash):
241 def copytostoreabsolute(repo, file, hash):
242 if inusercache(repo.ui, hash):
242 if inusercache(repo.ui, hash):
243 link(usercachepath(repo.ui, hash), storepath(repo, hash))
243 link(usercachepath(repo.ui, hash), storepath(repo, hash))
244 else:
244 else:
245 util.makedirs(os.path.dirname(storepath(repo, hash)))
245 util.makedirs(os.path.dirname(storepath(repo, hash)))
246 dst = util.atomictempfile(storepath(repo, hash),
246 dst = util.atomictempfile(storepath(repo, hash),
247 createmode=repo.store.createmode)
247 createmode=repo.store.createmode)
248 for chunk in util.filechunkiter(open(file, 'rb')):
248 for chunk in util.filechunkiter(open(file, 'rb')):
249 dst.write(chunk)
249 dst.write(chunk)
250 dst.close()
250 dst.close()
251 linktousercache(repo, hash)
251 linktousercache(repo, hash)
252
252
253 def linktousercache(repo, hash):
253 def linktousercache(repo, hash):
254 path = usercachepath(repo.ui, hash)
254 path = usercachepath(repo.ui, hash)
255 if path:
255 if path:
256 link(storepath(repo, hash), path)
256 link(storepath(repo, hash), path)
257
257
258 def getstandinmatcher(repo, rmatcher=None):
258 def getstandinmatcher(repo, rmatcher=None):
259 '''Return a match object that applies rmatcher to the standin directory'''
259 '''Return a match object that applies rmatcher to the standin directory'''
260 standindir = repo.wjoin(shortname)
260 standindir = repo.wjoin(shortname)
261
261
262 # no warnings about missing files or directories
262 # no warnings about missing files or directories
263 badfn = lambda f, msg: None
263 badfn = lambda f, msg: None
264
264
265 if rmatcher and not rmatcher.always():
265 if rmatcher and not rmatcher.always():
266 pats = [os.path.join(standindir, pat) for pat in rmatcher.files()]
266 pats = [os.path.join(standindir, pat) for pat in rmatcher.files()]
267 if not pats:
267 if not pats:
268 pats = [standindir]
268 pats = [standindir]
269 match = scmutil.match(repo[None], pats, badfn=badfn)
269 match = scmutil.match(repo[None], pats, badfn=badfn)
270 # if pats is empty, it would incorrectly always match, so clear _always
270 # if pats is empty, it would incorrectly always match, so clear _always
271 match._always = False
271 match._always = False
272 else:
272 else:
273 # no patterns: relative to repo root
273 # no patterns: relative to repo root
274 match = scmutil.match(repo[None], [standindir], badfn=badfn)
274 match = scmutil.match(repo[None], [standindir], badfn=badfn)
275 return match
275 return match
276
276
277 def composestandinmatcher(repo, rmatcher):
277 def composestandinmatcher(repo, rmatcher):
278 '''Return a matcher that accepts standins corresponding to the
278 '''Return a matcher that accepts standins corresponding to the
279 files accepted by rmatcher. Pass the list of files in the matcher
279 files accepted by rmatcher. Pass the list of files in the matcher
280 as the paths specified by the user.'''
280 as the paths specified by the user.'''
281 smatcher = getstandinmatcher(repo, rmatcher)
281 smatcher = getstandinmatcher(repo, rmatcher)
282 isstandin = smatcher.matchfn
282 isstandin = smatcher.matchfn
283 def composedmatchfn(f):
283 def composedmatchfn(f):
284 return isstandin(f) and rmatcher.matchfn(splitstandin(f))
284 return isstandin(f) and rmatcher.matchfn(splitstandin(f))
285 smatcher.matchfn = composedmatchfn
285 smatcher.matchfn = composedmatchfn
286
286
287 return smatcher
287 return smatcher
288
288
289 def standin(filename):
289 def standin(filename):
290 '''Return the repo-relative path to the standin for the specified big
290 '''Return the repo-relative path to the standin for the specified big
291 file.'''
291 file.'''
292 # Notes:
292 # Notes:
293 # 1) Some callers want an absolute path, but for instance addlargefiles
293 # 1) Some callers want an absolute path, but for instance addlargefiles
294 # needs it repo-relative so it can be passed to repo[None].add(). So
294 # needs it repo-relative so it can be passed to repo[None].add(). So
295 # leave it up to the caller to use repo.wjoin() to get an absolute path.
295 # leave it up to the caller to use repo.wjoin() to get an absolute path.
296 # 2) Join with '/' because that's what dirstate always uses, even on
296 # 2) Join with '/' because that's what dirstate always uses, even on
297 # Windows. Change existing separator to '/' first in case we are
297 # Windows. Change existing separator to '/' first in case we are
298 # passed filenames from an external source (like the command line).
298 # passed filenames from an external source (like the command line).
299 return shortnameslash + util.pconvert(filename)
299 return shortnameslash + util.pconvert(filename)
300
300
301 def isstandin(filename):
301 def isstandin(filename):
302 '''Return true if filename is a big file standin. filename must be
302 '''Return true if filename is a big file standin. filename must be
303 in Mercurial's internal form (slash-separated).'''
303 in Mercurial's internal form (slash-separated).'''
304 return filename.startswith(shortnameslash)
304 return filename.startswith(shortnameslash)
305
305
306 def splitstandin(filename):
306 def splitstandin(filename):
307 # Split on / because that's what dirstate always uses, even on Windows.
307 # Split on / because that's what dirstate always uses, even on Windows.
308 # Change local separator to / first just in case we are passed filenames
308 # Change local separator to / first just in case we are passed filenames
309 # from an external source (like the command line).
309 # from an external source (like the command line).
310 bits = util.pconvert(filename).split('/', 1)
310 bits = util.pconvert(filename).split('/', 1)
311 if len(bits) == 2 and bits[0] == shortname:
311 if len(bits) == 2 and bits[0] == shortname:
312 return bits[1]
312 return bits[1]
313 else:
313 else:
314 return None
314 return None
315
315
316 def updatestandin(repo, standin):
316 def updatestandin(repo, standin):
317 file = repo.wjoin(splitstandin(standin))
317 file = repo.wjoin(splitstandin(standin))
318 if os.path.exists(file):
318 if os.path.exists(file):
319 hash = hashfile(file)
319 hash = hashfile(file)
320 executable = getexecutable(file)
320 executable = getexecutable(file)
321 writestandin(repo, standin, hash, executable)
321 writestandin(repo, standin, hash, executable)
322 else:
322 else:
323 raise error.Abort(_('%s: file not found!') % splitstandin(standin))
323 raise error.Abort(_('%s: file not found!') % splitstandin(standin))
324
324
325 def readstandin(repo, filename, node=None):
325 def readstandin(repo, filename, node=None):
326 '''read hex hash from standin for filename at given node, or working
326 '''read hex hash from standin for filename at given node, or working
327 directory if no node is given'''
327 directory if no node is given'''
328 return repo[node][standin(filename)].data().strip()
328 return repo[node][standin(filename)].data().strip()
329
329
330 def writestandin(repo, standin, hash, executable):
330 def writestandin(repo, standin, hash, executable):
331 '''write hash to <repo.root>/<standin>'''
331 '''write hash to <repo.root>/<standin>'''
332 repo.wwrite(standin, hash + '\n', executable and 'x' or '')
332 repo.wwrite(standin, hash + '\n', executable and 'x' or '')
333
333
334 def copyandhash(instream, outfile):
334 def copyandhash(instream, outfile):
335 '''Read bytes from instream (iterable) and write them to outfile,
335 '''Read bytes from instream (iterable) and write them to outfile,
336 computing the SHA-1 hash of the data along the way. Return the hash.'''
336 computing the SHA-1 hash of the data along the way. Return the hash.'''
337 hasher = util.sha1('')
337 hasher = util.sha1('')
338 for data in instream:
338 for data in instream:
339 hasher.update(data)
339 hasher.update(data)
340 outfile.write(data)
340 outfile.write(data)
341 return hasher.hexdigest()
341 return hasher.hexdigest()
342
342
343 def hashrepofile(repo, file):
343 def hashrepofile(repo, file):
344 return hashfile(repo.wjoin(file))
344 return hashfile(repo.wjoin(file))
345
345
346 def hashfile(file):
346 def hashfile(file):
347 if not os.path.exists(file):
347 if not os.path.exists(file):
348 return ''
348 return ''
349 hasher = util.sha1('')
349 hasher = util.sha1('')
350 fd = open(file, 'rb')
350 fd = open(file, 'rb')
351 for data in util.filechunkiter(fd, 128 * 1024):
351 for data in util.filechunkiter(fd, 128 * 1024):
352 hasher.update(data)
352 hasher.update(data)
353 fd.close()
353 fd.close()
354 return hasher.hexdigest()
354 return hasher.hexdigest()
355
355
356 def getexecutable(filename):
356 def getexecutable(filename):
357 mode = os.stat(filename).st_mode
357 mode = os.stat(filename).st_mode
358 return ((mode & stat.S_IXUSR) and
358 return ((mode & stat.S_IXUSR) and
359 (mode & stat.S_IXGRP) and
359 (mode & stat.S_IXGRP) and
360 (mode & stat.S_IXOTH))
360 (mode & stat.S_IXOTH))
361
361
362 def urljoin(first, second, *arg):
362 def urljoin(first, second, *arg):
363 def join(left, right):
363 def join(left, right):
364 if not left.endswith('/'):
364 if not left.endswith('/'):
365 left += '/'
365 left += '/'
366 if right.startswith('/'):
366 if right.startswith('/'):
367 right = right[1:]
367 right = right[1:]
368 return left + right
368 return left + right
369
369
370 url = join(first, second)
370 url = join(first, second)
371 for a in arg:
371 for a in arg:
372 url = join(url, a)
372 url = join(url, a)
373 return url
373 return url
374
374
375 def hexsha1(data):
375 def hexsha1(data):
376 """hexsha1 returns the hex-encoded sha1 sum of the data in the file-like
376 """hexsha1 returns the hex-encoded sha1 sum of the data in the file-like
377 object data"""
377 object data"""
378 h = util.sha1()
378 h = util.sha1()
379 for chunk in util.filechunkiter(data):
379 for chunk in util.filechunkiter(data):
380 h.update(chunk)
380 h.update(chunk)
381 return h.hexdigest()
381 return h.hexdigest()
382
382
383 def httpsendfile(ui, filename):
383 def httpsendfile(ui, filename):
384 return httpconnection.httpsendfile(ui, filename, 'rb')
384 return httpconnection.httpsendfile(ui, filename, 'rb')
385
385
386 def unixpath(path):
386 def unixpath(path):
387 '''Return a version of path normalized for use with the lfdirstate.'''
387 '''Return a version of path normalized for use with the lfdirstate.'''
388 return util.pconvert(os.path.normpath(path))
388 return util.pconvert(os.path.normpath(path))
389
389
390 def islfilesrepo(repo):
390 def islfilesrepo(repo):
391 if ('largefiles' in repo.requirements and
391 if ('largefiles' in repo.requirements and
392 any(shortnameslash in f[0] for f in repo.store.datafiles())):
392 any(shortnameslash in f[0] for f in repo.store.datafiles())):
393 return True
393 return True
394
394
395 return any(openlfdirstate(repo.ui, repo, False))
395 return any(openlfdirstate(repo.ui, repo, False))
396
396
397 class storeprotonotcapable(Exception):
397 class storeprotonotcapable(Exception):
398 def __init__(self, storetypes):
398 def __init__(self, storetypes):
399 self.storetypes = storetypes
399 self.storetypes = storetypes
400
400
401 def getstandinsstate(repo):
401 def getstandinsstate(repo):
402 standins = []
402 standins = []
403 matcher = getstandinmatcher(repo)
403 matcher = getstandinmatcher(repo)
404 for standin in repo.dirstate.walk(matcher, [], False, False):
404 for standin in repo.dirstate.walk(matcher, [], False, False):
405 lfile = splitstandin(standin)
405 lfile = splitstandin(standin)
406 try:
406 try:
407 hash = readstandin(repo, lfile)
407 hash = readstandin(repo, lfile)
408 except IOError:
408 except IOError:
409 hash = None
409 hash = None
410 standins.append((lfile, hash))
410 standins.append((lfile, hash))
411 return standins
411 return standins
412
412
413 def synclfdirstate(repo, lfdirstate, lfile, normallookup):
413 def synclfdirstate(repo, lfdirstate, lfile, normallookup):
414 lfstandin = standin(lfile)
414 lfstandin = standin(lfile)
415 if lfstandin in repo.dirstate:
415 if lfstandin in repo.dirstate:
416 stat = repo.dirstate._map[lfstandin]
416 stat = repo.dirstate._map[lfstandin]
417 state, mtime = stat[0], stat[3]
417 state, mtime = stat[0], stat[3]
418 else:
418 else:
419 state, mtime = '?', -1
419 state, mtime = '?', -1
420 if state == 'n':
420 if state == 'n':
421 if (normallookup or mtime < 0 or
421 if (normallookup or mtime < 0 or
422 not os.path.exists(repo.wjoin(lfile))):
422 not os.path.exists(repo.wjoin(lfile))):
423 # state 'n' doesn't ensure 'clean' in this case
423 # state 'n' doesn't ensure 'clean' in this case
424 lfdirstate.normallookup(lfile)
424 lfdirstate.normallookup(lfile)
425 else:
425 else:
426 lfdirstate.normal(lfile)
426 lfdirstate.normal(lfile)
427 elif state == 'm':
427 elif state == 'm':
428 lfdirstate.normallookup(lfile)
428 lfdirstate.normallookup(lfile)
429 elif state == 'r':
429 elif state == 'r':
430 lfdirstate.remove(lfile)
430 lfdirstate.remove(lfile)
431 elif state == 'a':
431 elif state == 'a':
432 lfdirstate.add(lfile)
432 lfdirstate.add(lfile)
433 elif state == '?':
433 elif state == '?':
434 lfdirstate.drop(lfile)
434 lfdirstate.drop(lfile)
435
435
436 def markcommitted(orig, ctx, node):
436 def markcommitted(orig, ctx, node):
437 repo = ctx.repo()
437 repo = ctx.repo()
438
438
439 orig(node)
439 orig(node)
440
440
441 # ATTENTION: "ctx.files()" may differ from "repo[node].files()"
441 # ATTENTION: "ctx.files()" may differ from "repo[node].files()"
442 # because files coming from the 2nd parent are omitted in the latter.
442 # because files coming from the 2nd parent are omitted in the latter.
443 #
443 #
444 # The former should be used to get targets of "synclfdirstate",
444 # The former should be used to get targets of "synclfdirstate",
445 # because such files:
445 # because such files:
446 # - are marked as "a" by "patch.patch()" (e.g. via transplant), and
446 # - are marked as "a" by "patch.patch()" (e.g. via transplant), and
447 # - have to be marked as "n" after commit, but
447 # - have to be marked as "n" after commit, but
448 # - aren't listed in "repo[node].files()"
448 # - aren't listed in "repo[node].files()"
449
449
450 lfdirstate = openlfdirstate(repo.ui, repo)
450 lfdirstate = openlfdirstate(repo.ui, repo)
451 for f in ctx.files():
451 for f in ctx.files():
452 if isstandin(f):
452 if isstandin(f):
453 lfile = splitstandin(f)
453 lfile = splitstandin(f)
454 synclfdirstate(repo, lfdirstate, lfile, False)
454 synclfdirstate(repo, lfdirstate, lfile, False)
455 lfdirstate.write()
455 lfdirstate.write()
456
456
457 # As part of committing, copy all of the largefiles into the cache.
457 # As part of committing, copy all of the largefiles into the cache.
458 copyalltostore(repo, node)
458 copyalltostore(repo, node)
459
459
460 def getlfilestoupdate(oldstandins, newstandins):
460 def getlfilestoupdate(oldstandins, newstandins):
461 changedstandins = set(oldstandins).symmetric_difference(set(newstandins))
461 changedstandins = set(oldstandins).symmetric_difference(set(newstandins))
462 filelist = []
462 filelist = []
463 for f in changedstandins:
463 for f in changedstandins:
464 if f[0] not in filelist:
464 if f[0] not in filelist:
465 filelist.append(f[0])
465 filelist.append(f[0])
466 return filelist
466 return filelist
467
467
468 def getlfilestoupload(repo, missing, addfunc):
468 def getlfilestoupload(repo, missing, addfunc):
469 for i, n in enumerate(missing):
469 for i, n in enumerate(missing):
470 repo.ui.progress(_('finding outgoing largefiles'), i,
470 repo.ui.progress(_('finding outgoing largefiles'), i,
471 unit=_('revision'), total=len(missing))
471 unit=_('revisions'), total=len(missing))
472 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
472 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
473
473
474 oldlfstatus = repo.lfstatus
474 oldlfstatus = repo.lfstatus
475 repo.lfstatus = False
475 repo.lfstatus = False
476 try:
476 try:
477 ctx = repo[n]
477 ctx = repo[n]
478 finally:
478 finally:
479 repo.lfstatus = oldlfstatus
479 repo.lfstatus = oldlfstatus
480
480
481 files = set(ctx.files())
481 files = set(ctx.files())
482 if len(parents) == 2:
482 if len(parents) == 2:
483 mc = ctx.manifest()
483 mc = ctx.manifest()
484 mp1 = ctx.parents()[0].manifest()
484 mp1 = ctx.parents()[0].manifest()
485 mp2 = ctx.parents()[1].manifest()
485 mp2 = ctx.parents()[1].manifest()
486 for f in mp1:
486 for f in mp1:
487 if f not in mc:
487 if f not in mc:
488 files.add(f)
488 files.add(f)
489 for f in mp2:
489 for f in mp2:
490 if f not in mc:
490 if f not in mc:
491 files.add(f)
491 files.add(f)
492 for f in mc:
492 for f in mc:
493 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
493 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
494 files.add(f)
494 files.add(f)
495 for fn in files:
495 for fn in files:
496 if isstandin(fn) and fn in ctx:
496 if isstandin(fn) and fn in ctx:
497 addfunc(fn, ctx[fn].data().strip())
497 addfunc(fn, ctx[fn].data().strip())
498 repo.ui.progress(_('finding outgoing largefiles'), None)
498 repo.ui.progress(_('finding outgoing largefiles'), None)
499
499
500 def updatestandinsbymatch(repo, match):
500 def updatestandinsbymatch(repo, match):
501 '''Update standins in the working directory according to specified match
501 '''Update standins in the working directory according to specified match
502
502
503 This returns (possibly modified) ``match`` object to be used for
503 This returns (possibly modified) ``match`` object to be used for
504 subsequent commit process.
504 subsequent commit process.
505 '''
505 '''
506
506
507 ui = repo.ui
507 ui = repo.ui
508
508
509 # Case 1: user calls commit with no specific files or
509 # Case 1: user calls commit with no specific files or
510 # include/exclude patterns: refresh and commit all files that
510 # include/exclude patterns: refresh and commit all files that
511 # are "dirty".
511 # are "dirty".
512 if match is None or match.always():
512 if match is None or match.always():
513 # Spend a bit of time here to get a list of files we know
513 # Spend a bit of time here to get a list of files we know
514 # are modified so we can compare only against those.
514 # are modified so we can compare only against those.
515 # It can cost a lot of time (several seconds)
515 # It can cost a lot of time (several seconds)
516 # otherwise to update all standins if the largefiles are
516 # otherwise to update all standins if the largefiles are
517 # large.
517 # large.
518 lfdirstate = openlfdirstate(ui, repo)
518 lfdirstate = openlfdirstate(ui, repo)
519 dirtymatch = match_.always(repo.root, repo.getcwd())
519 dirtymatch = match_.always(repo.root, repo.getcwd())
520 unsure, s = lfdirstate.status(dirtymatch, [], False, False,
520 unsure, s = lfdirstate.status(dirtymatch, [], False, False,
521 False)
521 False)
522 modifiedfiles = unsure + s.modified + s.added + s.removed
522 modifiedfiles = unsure + s.modified + s.added + s.removed
523 lfiles = listlfiles(repo)
523 lfiles = listlfiles(repo)
524 # this only loops through largefiles that exist (not
524 # this only loops through largefiles that exist (not
525 # removed/renamed)
525 # removed/renamed)
526 for lfile in lfiles:
526 for lfile in lfiles:
527 if lfile in modifiedfiles:
527 if lfile in modifiedfiles:
528 if os.path.exists(
528 if os.path.exists(
529 repo.wjoin(standin(lfile))):
529 repo.wjoin(standin(lfile))):
530 # this handles the case where a rebase is being
530 # this handles the case where a rebase is being
531 # performed and the working copy is not updated
531 # performed and the working copy is not updated
532 # yet.
532 # yet.
533 if os.path.exists(repo.wjoin(lfile)):
533 if os.path.exists(repo.wjoin(lfile)):
534 updatestandin(repo,
534 updatestandin(repo,
535 standin(lfile))
535 standin(lfile))
536
536
537 return match
537 return match
538
538
539 lfiles = listlfiles(repo)
539 lfiles = listlfiles(repo)
540 match._files = repo._subdirlfs(match.files(), lfiles)
540 match._files = repo._subdirlfs(match.files(), lfiles)
541
541
542 # Case 2: user calls commit with specified patterns: refresh
542 # Case 2: user calls commit with specified patterns: refresh
543 # any matching big files.
543 # any matching big files.
544 smatcher = composestandinmatcher(repo, match)
544 smatcher = composestandinmatcher(repo, match)
545 standins = repo.dirstate.walk(smatcher, [], False, False)
545 standins = repo.dirstate.walk(smatcher, [], False, False)
546
546
547 # No matching big files: get out of the way and pass control to
547 # No matching big files: get out of the way and pass control to
548 # the usual commit() method.
548 # the usual commit() method.
549 if not standins:
549 if not standins:
550 return match
550 return match
551
551
552 # Refresh all matching big files. It's possible that the
552 # Refresh all matching big files. It's possible that the
553 # commit will end up failing, in which case the big files will
553 # commit will end up failing, in which case the big files will
554 # stay refreshed. No harm done: the user modified them and
554 # stay refreshed. No harm done: the user modified them and
555 # asked to commit them, so sooner or later we're going to
555 # asked to commit them, so sooner or later we're going to
556 # refresh the standins. Might as well leave them refreshed.
556 # refresh the standins. Might as well leave them refreshed.
557 lfdirstate = openlfdirstate(ui, repo)
557 lfdirstate = openlfdirstate(ui, repo)
558 for fstandin in standins:
558 for fstandin in standins:
559 lfile = splitstandin(fstandin)
559 lfile = splitstandin(fstandin)
560 if lfdirstate[lfile] != 'r':
560 if lfdirstate[lfile] != 'r':
561 updatestandin(repo, fstandin)
561 updatestandin(repo, fstandin)
562
562
563 # Cook up a new matcher that only matches regular files or
563 # Cook up a new matcher that only matches regular files or
564 # standins corresponding to the big files requested by the
564 # standins corresponding to the big files requested by the
565 # user. Have to modify _files to prevent commit() from
565 # user. Have to modify _files to prevent commit() from
566 # complaining "not tracked" for big files.
566 # complaining "not tracked" for big files.
567 match = copy.copy(match)
567 match = copy.copy(match)
568 origmatchfn = match.matchfn
568 origmatchfn = match.matchfn
569
569
570 # Check both the list of largefiles and the list of
570 # Check both the list of largefiles and the list of
571 # standins because if a largefile was removed, it
571 # standins because if a largefile was removed, it
572 # won't be in the list of largefiles at this point
572 # won't be in the list of largefiles at this point
573 match._files += sorted(standins)
573 match._files += sorted(standins)
574
574
575 actualfiles = []
575 actualfiles = []
576 for f in match._files:
576 for f in match._files:
577 fstandin = standin(f)
577 fstandin = standin(f)
578
578
579 # For largefiles, only one of the normal and standin should be
579 # For largefiles, only one of the normal and standin should be
580 # committed (except if one of them is a remove). In the case of a
580 # committed (except if one of them is a remove). In the case of a
581 # standin removal, drop the normal file if it is unknown to dirstate.
581 # standin removal, drop the normal file if it is unknown to dirstate.
582 # Thus, skip plain largefile names but keep the standin.
582 # Thus, skip plain largefile names but keep the standin.
583 if f in lfiles or fstandin in standins:
583 if f in lfiles or fstandin in standins:
584 if repo.dirstate[fstandin] != 'r':
584 if repo.dirstate[fstandin] != 'r':
585 if repo.dirstate[f] != 'r':
585 if repo.dirstate[f] != 'r':
586 continue
586 continue
587 elif repo.dirstate[f] == '?':
587 elif repo.dirstate[f] == '?':
588 continue
588 continue
589
589
590 actualfiles.append(f)
590 actualfiles.append(f)
591 match._files = actualfiles
591 match._files = actualfiles
592
592
593 def matchfn(f):
593 def matchfn(f):
594 if origmatchfn(f):
594 if origmatchfn(f):
595 return f not in lfiles
595 return f not in lfiles
596 else:
596 else:
597 return f in standins
597 return f in standins
598
598
599 match.matchfn = matchfn
599 match.matchfn = matchfn
600
600
601 return match
601 return match
602
602
603 class automatedcommithook(object):
603 class automatedcommithook(object):
604 '''Stateful hook to update standins at the 1st commit of resuming
604 '''Stateful hook to update standins at the 1st commit of resuming
605
605
606 For efficiency, updating standins in the working directory should
606 For efficiency, updating standins in the working directory should
607 be avoided while automated committing (like rebase, transplant and
607 be avoided while automated committing (like rebase, transplant and
608 so on), because they should be updated before committing.
608 so on), because they should be updated before committing.
609
609
610 But the 1st commit of resuming automated committing (e.g. ``rebase
610 But the 1st commit of resuming automated committing (e.g. ``rebase
611 --continue``) should update them, because largefiles may be
611 --continue``) should update them, because largefiles may be
612 modified manually.
612 modified manually.
613 '''
613 '''
614 def __init__(self, resuming):
614 def __init__(self, resuming):
615 self.resuming = resuming
615 self.resuming = resuming
616
616
617 def __call__(self, repo, match):
617 def __call__(self, repo, match):
618 if self.resuming:
618 if self.resuming:
619 self.resuming = False # avoids updating at subsequent commits
619 self.resuming = False # avoids updating at subsequent commits
620 return updatestandinsbymatch(repo, match)
620 return updatestandinsbymatch(repo, match)
621 else:
621 else:
622 return match
622 return match
623
623
624 def getstatuswriter(ui, repo, forcibly=None):
624 def getstatuswriter(ui, repo, forcibly=None):
625 '''Return the function to write largefiles specific status out
625 '''Return the function to write largefiles specific status out
626
626
627 If ``forcibly`` is ``None``, this returns the last element of
627 If ``forcibly`` is ``None``, this returns the last element of
628 ``repo._lfstatuswriters`` as "default" writer function.
628 ``repo._lfstatuswriters`` as "default" writer function.
629
629
630 Otherwise, this returns the function to always write out (or
630 Otherwise, this returns the function to always write out (or
631 ignore if ``not forcibly``) status.
631 ignore if ``not forcibly``) status.
632 '''
632 '''
633 if forcibly is None and util.safehasattr(repo, '_largefilesenabled'):
633 if forcibly is None and util.safehasattr(repo, '_largefilesenabled'):
634 return repo._lfstatuswriters[-1]
634 return repo._lfstatuswriters[-1]
635 else:
635 else:
636 if forcibly:
636 if forcibly:
637 return ui.status # forcibly WRITE OUT
637 return ui.status # forcibly WRITE OUT
638 else:
638 else:
639 return lambda *msg, **opts: None # forcibly IGNORE
639 return lambda *msg, **opts: None # forcibly IGNORE
@@ -1,1101 +1,1101 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
40
41 TODO: Ideally, this should mention the largefile, not the standin
41 TODO: Ideally, this should mention the largefile, not the standin
42 $ hg log -T '{rev}\n' --stat 'set:clean()'
42 $ hg log -T '{rev}\n' --stat 'set:clean()'
43 0
43 0
44 .hglf/dira/dirb/largefile | 1 +
44 .hglf/dira/dirb/largefile | 1 +
45 1 files changed, 1 insertions(+), 0 deletions(-)
45 1 files changed, 1 insertions(+), 0 deletions(-)
46
46
47 $ hg ci -m "deep copy"
47 $ hg ci -m "deep copy"
48 Invoking status precommit hook
48 Invoking status precommit hook
49 A dira/foo/largefile
49 A dira/foo/largefile
50 $ find . | sort
50 $ find . | sort
51 .
51 .
52 ./dirb
52 ./dirb
53 ./dirb/largefile
53 ./dirb/largefile
54 ./foo
54 ./foo
55 ./foo/largefile
55 ./foo/largefile
56 $ hg mv foo/largefile baz/largefile
56 $ hg mv foo/largefile baz/largefile
57 $ hg ci -m "moved"
57 $ hg ci -m "moved"
58 Invoking status precommit hook
58 Invoking status precommit hook
59 A dira/baz/largefile
59 A dira/baz/largefile
60 R dira/foo/largefile
60 R dira/foo/largefile
61 $ find . | sort
61 $ find . | sort
62 .
62 .
63 ./baz
63 ./baz
64 ./baz/largefile
64 ./baz/largefile
65 ./dirb
65 ./dirb
66 ./dirb/largefile
66 ./dirb/largefile
67 $ cd ..
67 $ cd ..
68 $ hg mv dira dirc
68 $ hg mv dira dirc
69 moving .hglf/dira/baz/largefile to .hglf/dirc/baz/largefile (glob)
69 moving .hglf/dira/baz/largefile to .hglf/dirc/baz/largefile (glob)
70 moving .hglf/dira/dirb/largefile to .hglf/dirc/dirb/largefile (glob)
70 moving .hglf/dira/dirb/largefile to .hglf/dirc/dirb/largefile (glob)
71 $ find * | sort
71 $ find * | sort
72 dirc
72 dirc
73 dirc/baz
73 dirc/baz
74 dirc/baz/largefile
74 dirc/baz/largefile
75 dirc/dirb
75 dirc/dirb
76 dirc/dirb/largefile
76 dirc/dirb/largefile
77
77
78 $ hg clone -q . ../fetch
78 $ hg clone -q . ../fetch
79 $ hg --config extensions.fetch= fetch ../fetch
79 $ hg --config extensions.fetch= fetch ../fetch
80 abort: uncommitted changes
80 abort: uncommitted changes
81 [255]
81 [255]
82 $ hg up -qC
82 $ hg up -qC
83 $ cd ..
83 $ cd ..
84
84
85 Clone a local repository owned by another user
85 Clone a local repository owned by another user
86 ===================================================
86 ===================================================
87
87
88 #if unix-permissions
88 #if unix-permissions
89
89
90 We have to simulate that here by setting $HOME and removing write permissions
90 We have to simulate that here by setting $HOME and removing write permissions
91 $ ORIGHOME="$HOME"
91 $ ORIGHOME="$HOME"
92 $ mkdir alice
92 $ mkdir alice
93 $ HOME="`pwd`/alice"
93 $ HOME="`pwd`/alice"
94 $ cd alice
94 $ cd alice
95 $ hg init pubrepo
95 $ hg init pubrepo
96 $ cd pubrepo
96 $ cd pubrepo
97 $ dd if=/dev/zero bs=1k count=11k > a-large-file 2> /dev/null
97 $ dd if=/dev/zero bs=1k count=11k > a-large-file 2> /dev/null
98 $ hg add --large a-large-file
98 $ hg add --large a-large-file
99 $ hg commit -m "Add a large file"
99 $ hg commit -m "Add a large file"
100 Invoking status precommit hook
100 Invoking status precommit hook
101 A a-large-file
101 A a-large-file
102 $ cd ..
102 $ cd ..
103 $ chmod -R a-w pubrepo
103 $ chmod -R a-w pubrepo
104 $ cd ..
104 $ cd ..
105 $ mkdir bob
105 $ mkdir bob
106 $ HOME="`pwd`/bob"
106 $ HOME="`pwd`/bob"
107 $ cd bob
107 $ cd bob
108 $ hg clone --pull ../alice/pubrepo pubrepo
108 $ hg clone --pull ../alice/pubrepo pubrepo
109 requesting all changes
109 requesting all changes
110 adding changesets
110 adding changesets
111 adding manifests
111 adding manifests
112 adding file changes
112 adding file changes
113 added 1 changesets with 1 changes to 1 files
113 added 1 changesets with 1 changes to 1 files
114 updating to branch default
114 updating to branch default
115 getting changed largefiles
115 getting changed largefiles
116 1 largefiles updated, 0 removed
116 1 largefiles updated, 0 removed
117 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
117 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
118 $ cd ..
118 $ cd ..
119 $ chmod -R u+w alice/pubrepo
119 $ chmod -R u+w alice/pubrepo
120 $ HOME="$ORIGHOME"
120 $ HOME="$ORIGHOME"
121
121
122 #endif
122 #endif
123
123
124
124
125 Symlink to a large largefile should behave the same as a symlink to a normal file
125 Symlink to a large largefile should behave the same as a symlink to a normal file
126 =====================================================================================
126 =====================================================================================
127
127
128 #if symlink
128 #if symlink
129
129
130 $ hg init largesymlink
130 $ hg init largesymlink
131 $ cd largesymlink
131 $ cd largesymlink
132 $ dd if=/dev/zero bs=1k count=10k of=largefile 2>/dev/null
132 $ dd if=/dev/zero bs=1k count=10k of=largefile 2>/dev/null
133 $ hg add --large largefile
133 $ hg add --large largefile
134 $ hg commit -m "commit a large file"
134 $ hg commit -m "commit a large file"
135 Invoking status precommit hook
135 Invoking status precommit hook
136 A largefile
136 A largefile
137 $ ln -s largefile largelink
137 $ ln -s largefile largelink
138 $ hg add largelink
138 $ hg add largelink
139 $ hg commit -m "commit a large symlink"
139 $ hg commit -m "commit a large symlink"
140 Invoking status precommit hook
140 Invoking status precommit hook
141 A largelink
141 A largelink
142 $ rm -f largelink
142 $ rm -f largelink
143 $ hg up >/dev/null
143 $ hg up >/dev/null
144 $ test -f largelink
144 $ test -f largelink
145 [1]
145 [1]
146 $ test -L largelink
146 $ test -L largelink
147 [1]
147 [1]
148 $ rm -f largelink # make next part of the test independent of the previous
148 $ rm -f largelink # make next part of the test independent of the previous
149 $ hg up -C >/dev/null
149 $ hg up -C >/dev/null
150 $ test -f largelink
150 $ test -f largelink
151 $ test -L largelink
151 $ test -L largelink
152 $ cd ..
152 $ cd ..
153
153
154 #endif
154 #endif
155
155
156
156
157 test for pattern matching on 'hg status':
157 test for pattern matching on 'hg status':
158 ==============================================
158 ==============================================
159
159
160
160
161 to boost performance, largefiles checks whether specified patterns are
161 to boost performance, largefiles checks whether specified patterns are
162 related to largefiles in working directory (NOT to STANDIN) or not.
162 related to largefiles in working directory (NOT to STANDIN) or not.
163
163
164 $ hg init statusmatch
164 $ hg init statusmatch
165 $ cd statusmatch
165 $ cd statusmatch
166
166
167 $ mkdir -p a/b/c/d
167 $ mkdir -p a/b/c/d
168 $ echo normal > a/b/c/d/e.normal.txt
168 $ echo normal > a/b/c/d/e.normal.txt
169 $ hg add a/b/c/d/e.normal.txt
169 $ hg add a/b/c/d/e.normal.txt
170 $ echo large > a/b/c/d/e.large.txt
170 $ echo large > a/b/c/d/e.large.txt
171 $ hg add --large a/b/c/d/e.large.txt
171 $ hg add --large a/b/c/d/e.large.txt
172 $ mkdir -p a/b/c/x
172 $ mkdir -p a/b/c/x
173 $ echo normal > a/b/c/x/y.normal.txt
173 $ echo normal > a/b/c/x/y.normal.txt
174 $ hg add a/b/c/x/y.normal.txt
174 $ hg add a/b/c/x/y.normal.txt
175 $ hg commit -m 'add files'
175 $ hg commit -m 'add files'
176 Invoking status precommit hook
176 Invoking status precommit hook
177 A a/b/c/d/e.large.txt
177 A a/b/c/d/e.large.txt
178 A a/b/c/d/e.normal.txt
178 A a/b/c/d/e.normal.txt
179 A a/b/c/x/y.normal.txt
179 A a/b/c/x/y.normal.txt
180
180
181 (1) no pattern: no performance boost
181 (1) no pattern: no performance boost
182 $ hg status -A
182 $ hg status -A
183 C a/b/c/d/e.large.txt
183 C a/b/c/d/e.large.txt
184 C a/b/c/d/e.normal.txt
184 C a/b/c/d/e.normal.txt
185 C a/b/c/x/y.normal.txt
185 C a/b/c/x/y.normal.txt
186
186
187 (2) pattern not related to largefiles: performance boost
187 (2) pattern not related to largefiles: performance boost
188 $ hg status -A a/b/c/x
188 $ hg status -A a/b/c/x
189 C a/b/c/x/y.normal.txt
189 C a/b/c/x/y.normal.txt
190
190
191 (3) pattern related to largefiles: no performance boost
191 (3) pattern related to largefiles: no performance boost
192 $ hg status -A a/b/c/d
192 $ hg status -A a/b/c/d
193 C a/b/c/d/e.large.txt
193 C a/b/c/d/e.large.txt
194 C a/b/c/d/e.normal.txt
194 C a/b/c/d/e.normal.txt
195
195
196 (4) pattern related to STANDIN (not to largefiles): performance boost
196 (4) pattern related to STANDIN (not to largefiles): performance boost
197 $ hg status -A .hglf/a
197 $ hg status -A .hglf/a
198 C .hglf/a/b/c/d/e.large.txt
198 C .hglf/a/b/c/d/e.large.txt
199
199
200 (5) mixed case: no performance boost
200 (5) mixed case: no performance boost
201 $ hg status -A a/b/c/x a/b/c/d
201 $ hg status -A a/b/c/x a/b/c/d
202 C a/b/c/d/e.large.txt
202 C a/b/c/d/e.large.txt
203 C a/b/c/d/e.normal.txt
203 C a/b/c/d/e.normal.txt
204 C a/b/c/x/y.normal.txt
204 C a/b/c/x/y.normal.txt
205
205
206 verify that largefiles doesn't break filesets
206 verify that largefiles doesn't break filesets
207
207
208 $ hg log --rev . --exclude "set:binary()"
208 $ hg log --rev . --exclude "set:binary()"
209 changeset: 0:41bd42f10efa
209 changeset: 0:41bd42f10efa
210 tag: tip
210 tag: tip
211 user: test
211 user: test
212 date: Thu Jan 01 00:00:00 1970 +0000
212 date: Thu Jan 01 00:00:00 1970 +0000
213 summary: add files
213 summary: add files
214
214
215 verify that large files in subrepos handled properly
215 verify that large files in subrepos handled properly
216 $ hg init subrepo
216 $ hg init subrepo
217 $ echo "subrepo = subrepo" > .hgsub
217 $ echo "subrepo = subrepo" > .hgsub
218 $ hg add .hgsub
218 $ hg add .hgsub
219 $ hg ci -m "add subrepo"
219 $ hg ci -m "add subrepo"
220 Invoking status precommit hook
220 Invoking status precommit hook
221 A .hgsub
221 A .hgsub
222 ? .hgsubstate
222 ? .hgsubstate
223 $ echo "rev 1" > subrepo/large.txt
223 $ echo "rev 1" > subrepo/large.txt
224 $ hg add --large subrepo/large.txt
224 $ hg add --large subrepo/large.txt
225 $ hg sum
225 $ hg sum
226 parent: 1:8ee150ea2e9c tip
226 parent: 1:8ee150ea2e9c tip
227 add subrepo
227 add subrepo
228 branch: default
228 branch: default
229 commit: 1 subrepos
229 commit: 1 subrepos
230 update: (current)
230 update: (current)
231 phases: 2 draft
231 phases: 2 draft
232 $ hg st
232 $ hg st
233 $ hg st -S
233 $ hg st -S
234 A subrepo/large.txt
234 A subrepo/large.txt
235 $ hg ci -S -m "commit top repo"
235 $ hg ci -S -m "commit top repo"
236 committing subrepository subrepo
236 committing subrepository subrepo
237 Invoking status precommit hook
237 Invoking status precommit hook
238 A large.txt
238 A large.txt
239 Invoking status precommit hook
239 Invoking status precommit hook
240 M .hgsubstate
240 M .hgsubstate
241 # No differences
241 # No differences
242 $ hg st -S
242 $ hg st -S
243 $ hg sum
243 $ hg sum
244 parent: 2:ce4cd0c527a6 tip
244 parent: 2:ce4cd0c527a6 tip
245 commit top repo
245 commit top repo
246 branch: default
246 branch: default
247 commit: (clean)
247 commit: (clean)
248 update: (current)
248 update: (current)
249 phases: 3 draft
249 phases: 3 draft
250 $ echo "rev 2" > subrepo/large.txt
250 $ echo "rev 2" > subrepo/large.txt
251 $ hg st -S
251 $ hg st -S
252 M subrepo/large.txt
252 M subrepo/large.txt
253 $ hg sum
253 $ hg sum
254 parent: 2:ce4cd0c527a6 tip
254 parent: 2:ce4cd0c527a6 tip
255 commit top repo
255 commit top repo
256 branch: default
256 branch: default
257 commit: 1 subrepos
257 commit: 1 subrepos
258 update: (current)
258 update: (current)
259 phases: 3 draft
259 phases: 3 draft
260 $ hg ci -m "this commit should fail without -S"
260 $ hg ci -m "this commit should fail without -S"
261 abort: uncommitted changes in subrepository 'subrepo'
261 abort: uncommitted changes in subrepository 'subrepo'
262 (use --subrepos for recursive commit)
262 (use --subrepos for recursive commit)
263 [255]
263 [255]
264
264
265 Add a normal file to the subrepo, then test archiving
265 Add a normal file to the subrepo, then test archiving
266
266
267 $ echo 'normal file' > subrepo/normal.txt
267 $ echo 'normal file' > subrepo/normal.txt
268 $ touch large.dat
268 $ touch large.dat
269 $ mv subrepo/large.txt subrepo/renamed-large.txt
269 $ mv subrepo/large.txt subrepo/renamed-large.txt
270 $ hg addremove -S --dry-run
270 $ hg addremove -S --dry-run
271 adding large.dat as a largefile
271 adding large.dat as a largefile
272 removing subrepo/large.txt
272 removing subrepo/large.txt
273 adding subrepo/normal.txt
273 adding subrepo/normal.txt
274 adding subrepo/renamed-large.txt
274 adding subrepo/renamed-large.txt
275 $ hg status -S
275 $ hg status -S
276 ! subrepo/large.txt
276 ! subrepo/large.txt
277 ? large.dat
277 ? large.dat
278 ? subrepo/normal.txt
278 ? subrepo/normal.txt
279 ? subrepo/renamed-large.txt
279 ? subrepo/renamed-large.txt
280
280
281 $ hg addremove --dry-run subrepo
281 $ hg addremove --dry-run subrepo
282 removing subrepo/large.txt (glob)
282 removing subrepo/large.txt (glob)
283 adding subrepo/normal.txt (glob)
283 adding subrepo/normal.txt (glob)
284 adding subrepo/renamed-large.txt (glob)
284 adding subrepo/renamed-large.txt (glob)
285 $ hg status -S
285 $ hg status -S
286 ! subrepo/large.txt
286 ! subrepo/large.txt
287 ? large.dat
287 ? large.dat
288 ? subrepo/normal.txt
288 ? subrepo/normal.txt
289 ? subrepo/renamed-large.txt
289 ? subrepo/renamed-large.txt
290 $ cd ..
290 $ cd ..
291
291
292 $ hg -R statusmatch addremove --dry-run statusmatch/subrepo
292 $ hg -R statusmatch addremove --dry-run statusmatch/subrepo
293 removing statusmatch/subrepo/large.txt (glob)
293 removing statusmatch/subrepo/large.txt (glob)
294 adding statusmatch/subrepo/normal.txt (glob)
294 adding statusmatch/subrepo/normal.txt (glob)
295 adding statusmatch/subrepo/renamed-large.txt (glob)
295 adding statusmatch/subrepo/renamed-large.txt (glob)
296 $ hg -R statusmatch status -S
296 $ hg -R statusmatch status -S
297 ! subrepo/large.txt
297 ! subrepo/large.txt
298 ? large.dat
298 ? large.dat
299 ? subrepo/normal.txt
299 ? subrepo/normal.txt
300 ? subrepo/renamed-large.txt
300 ? subrepo/renamed-large.txt
301
301
302 $ hg -R statusmatch addremove --dry-run -S
302 $ hg -R statusmatch addremove --dry-run -S
303 adding large.dat as a largefile
303 adding large.dat as a largefile
304 removing subrepo/large.txt
304 removing subrepo/large.txt
305 adding subrepo/normal.txt
305 adding subrepo/normal.txt
306 adding subrepo/renamed-large.txt
306 adding subrepo/renamed-large.txt
307 $ cd statusmatch
307 $ cd statusmatch
308
308
309 $ mv subrepo/renamed-large.txt subrepo/large.txt
309 $ mv subrepo/renamed-large.txt subrepo/large.txt
310 $ hg addremove subrepo
310 $ hg addremove subrepo
311 adding subrepo/normal.txt (glob)
311 adding subrepo/normal.txt (glob)
312 $ hg forget subrepo/normal.txt
312 $ hg forget subrepo/normal.txt
313
313
314 $ hg addremove -S
314 $ hg addremove -S
315 adding large.dat as a largefile
315 adding large.dat as a largefile
316 adding subrepo/normal.txt
316 adding subrepo/normal.txt
317 $ rm large.dat
317 $ rm large.dat
318
318
319 $ hg addremove subrepo
319 $ hg addremove subrepo
320 $ hg addremove -S
320 $ hg addremove -S
321 removing large.dat
321 removing large.dat
322
322
323 Lock in subrepo, otherwise the change isn't archived
323 Lock in subrepo, otherwise the change isn't archived
324
324
325 $ hg ci -S -m "add normal file to top level"
325 $ hg ci -S -m "add normal file to top level"
326 committing subrepository subrepo
326 committing subrepository subrepo
327 Invoking status precommit hook
327 Invoking status precommit hook
328 M large.txt
328 M large.txt
329 A normal.txt
329 A normal.txt
330 Invoking status precommit hook
330 Invoking status precommit hook
331 M .hgsubstate
331 M .hgsubstate
332 $ hg archive -S ../lf_subrepo_archive
332 $ hg archive -S ../lf_subrepo_archive
333 $ find ../lf_subrepo_archive | sort
333 $ find ../lf_subrepo_archive | sort
334 ../lf_subrepo_archive
334 ../lf_subrepo_archive
335 ../lf_subrepo_archive/.hg_archival.txt
335 ../lf_subrepo_archive/.hg_archival.txt
336 ../lf_subrepo_archive/.hgsub
336 ../lf_subrepo_archive/.hgsub
337 ../lf_subrepo_archive/.hgsubstate
337 ../lf_subrepo_archive/.hgsubstate
338 ../lf_subrepo_archive/a
338 ../lf_subrepo_archive/a
339 ../lf_subrepo_archive/a/b
339 ../lf_subrepo_archive/a/b
340 ../lf_subrepo_archive/a/b/c
340 ../lf_subrepo_archive/a/b/c
341 ../lf_subrepo_archive/a/b/c/d
341 ../lf_subrepo_archive/a/b/c/d
342 ../lf_subrepo_archive/a/b/c/d/e.large.txt
342 ../lf_subrepo_archive/a/b/c/d/e.large.txt
343 ../lf_subrepo_archive/a/b/c/d/e.normal.txt
343 ../lf_subrepo_archive/a/b/c/d/e.normal.txt
344 ../lf_subrepo_archive/a/b/c/x
344 ../lf_subrepo_archive/a/b/c/x
345 ../lf_subrepo_archive/a/b/c/x/y.normal.txt
345 ../lf_subrepo_archive/a/b/c/x/y.normal.txt
346 ../lf_subrepo_archive/subrepo
346 ../lf_subrepo_archive/subrepo
347 ../lf_subrepo_archive/subrepo/large.txt
347 ../lf_subrepo_archive/subrepo/large.txt
348 ../lf_subrepo_archive/subrepo/normal.txt
348 ../lf_subrepo_archive/subrepo/normal.txt
349 $ cat ../lf_subrepo_archive/.hg_archival.txt
349 $ cat ../lf_subrepo_archive/.hg_archival.txt
350 repo: 41bd42f10efa43698cc02052ea0977771cba506d
350 repo: 41bd42f10efa43698cc02052ea0977771cba506d
351 node: d56a95e6522858bc08a724c4fe2bdee066d1c30b
351 node: d56a95e6522858bc08a724c4fe2bdee066d1c30b
352 branch: default
352 branch: default
353 latesttag: null
353 latesttag: null
354 latesttagdistance: 4
354 latesttagdistance: 4
355 changessincelatesttag: 4
355 changessincelatesttag: 4
356
356
357 Test update with subrepos.
357 Test update with subrepos.
358
358
359 $ hg update 0
359 $ hg update 0
360 getting changed largefiles
360 getting changed largefiles
361 0 largefiles updated, 1 removed
361 0 largefiles updated, 1 removed
362 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
362 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
363 $ hg status -S
363 $ hg status -S
364 $ hg update tip
364 $ hg update tip
365 getting changed largefiles
365 getting changed largefiles
366 1 largefiles updated, 0 removed
366 1 largefiles updated, 0 removed
367 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
367 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
368 $ hg status -S
368 $ hg status -S
369 # modify a large file
369 # modify a large file
370 $ echo "modified" > subrepo/large.txt
370 $ echo "modified" > subrepo/large.txt
371 $ hg st -S
371 $ hg st -S
372 M subrepo/large.txt
372 M subrepo/large.txt
373 # update -C should revert the change.
373 # update -C should revert the change.
374 $ hg update -C
374 $ hg update -C
375 getting changed largefiles
375 getting changed largefiles
376 1 largefiles updated, 0 removed
376 1 largefiles updated, 0 removed
377 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
377 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
378 $ hg status -S
378 $ hg status -S
379
379
380 $ hg forget -v subrepo/large.txt
380 $ hg forget -v subrepo/large.txt
381 removing subrepo/large.txt (glob)
381 removing subrepo/large.txt (glob)
382
382
383 Test reverting a forgotten file
383 Test reverting a forgotten file
384 $ hg revert -R subrepo subrepo/large.txt
384 $ hg revert -R subrepo subrepo/large.txt
385 $ hg status -SA subrepo/large.txt
385 $ hg status -SA subrepo/large.txt
386 C subrepo/large.txt
386 C subrepo/large.txt
387
387
388 $ hg rm -v subrepo/large.txt
388 $ hg rm -v subrepo/large.txt
389 removing subrepo/large.txt (glob)
389 removing subrepo/large.txt (glob)
390 $ hg revert -R subrepo subrepo/large.txt
390 $ hg revert -R subrepo subrepo/large.txt
391 $ rm subrepo/large.txt
391 $ rm subrepo/large.txt
392 $ hg addremove -S
392 $ hg addremove -S
393 removing subrepo/large.txt
393 removing subrepo/large.txt
394 $ hg st -S
394 $ hg st -S
395 R subrepo/large.txt
395 R subrepo/large.txt
396
396
397 Test archiving a revision that references a subrepo that is not yet
397 Test archiving a revision that references a subrepo that is not yet
398 cloned (see test-subrepo-recursion.t):
398 cloned (see test-subrepo-recursion.t):
399
399
400 $ hg clone -U . ../empty
400 $ hg clone -U . ../empty
401 $ cd ../empty
401 $ cd ../empty
402 $ hg archive --subrepos -r tip ../archive.tar.gz
402 $ hg archive --subrepos -r tip ../archive.tar.gz
403 cloning subrepo subrepo from $TESTTMP/statusmatch/subrepo
403 cloning subrepo subrepo from $TESTTMP/statusmatch/subrepo
404 $ cd ..
404 $ cd ..
405
405
406
406
407
407
408
408
409
409
410
410
411 Test addremove, forget and others
411 Test addremove, forget and others
412 ==============================================
412 ==============================================
413
413
414 Test that addremove picks up largefiles prior to the initial commit (issue3541)
414 Test that addremove picks up largefiles prior to the initial commit (issue3541)
415
415
416 $ hg init addrm2
416 $ hg init addrm2
417 $ cd addrm2
417 $ cd addrm2
418 $ touch large.dat
418 $ touch large.dat
419 $ touch large2.dat
419 $ touch large2.dat
420 $ touch normal
420 $ touch normal
421 $ hg add --large large.dat
421 $ hg add --large large.dat
422 $ hg addremove -v
422 $ hg addremove -v
423 adding large2.dat as a largefile
423 adding large2.dat as a largefile
424 adding normal
424 adding normal
425
425
426 Test that forgetting all largefiles reverts to islfilesrepo() == False
426 Test that forgetting all largefiles reverts to islfilesrepo() == False
427 (addremove will add *.dat as normal files now)
427 (addremove will add *.dat as normal files now)
428 $ hg forget large.dat
428 $ hg forget large.dat
429 $ hg forget large2.dat
429 $ hg forget large2.dat
430 $ hg addremove -v
430 $ hg addremove -v
431 adding large.dat
431 adding large.dat
432 adding large2.dat
432 adding large2.dat
433
433
434 Test commit's addremove option prior to the first commit
434 Test commit's addremove option prior to the first commit
435 $ hg forget large.dat
435 $ hg forget large.dat
436 $ hg forget large2.dat
436 $ hg forget large2.dat
437 $ hg add --large large.dat
437 $ hg add --large large.dat
438 $ hg ci -Am "commit"
438 $ hg ci -Am "commit"
439 adding large2.dat as a largefile
439 adding large2.dat as a largefile
440 Invoking status precommit hook
440 Invoking status precommit hook
441 A large.dat
441 A large.dat
442 A large2.dat
442 A large2.dat
443 A normal
443 A normal
444 $ find .hglf | sort
444 $ find .hglf | sort
445 .hglf
445 .hglf
446 .hglf/large.dat
446 .hglf/large.dat
447 .hglf/large2.dat
447 .hglf/large2.dat
448
448
449 Test actions on largefiles using relative paths from subdir
449 Test actions on largefiles using relative paths from subdir
450
450
451 $ mkdir sub
451 $ mkdir sub
452 $ cd sub
452 $ cd sub
453 $ echo anotherlarge > anotherlarge
453 $ echo anotherlarge > anotherlarge
454 $ hg add --large anotherlarge
454 $ hg add --large anotherlarge
455 $ hg st
455 $ hg st
456 A sub/anotherlarge
456 A sub/anotherlarge
457 $ hg st anotherlarge
457 $ hg st anotherlarge
458 A anotherlarge
458 A anotherlarge
459 $ hg commit -m anotherlarge anotherlarge
459 $ hg commit -m anotherlarge anotherlarge
460 Invoking status precommit hook
460 Invoking status precommit hook
461 A sub/anotherlarge
461 A sub/anotherlarge
462 $ hg log anotherlarge
462 $ hg log anotherlarge
463 changeset: 1:9627a577c5e9
463 changeset: 1:9627a577c5e9
464 tag: tip
464 tag: tip
465 user: test
465 user: test
466 date: Thu Jan 01 00:00:00 1970 +0000
466 date: Thu Jan 01 00:00:00 1970 +0000
467 summary: anotherlarge
467 summary: anotherlarge
468
468
469 $ hg --debug log -T '{rev}: {desc}\n' ../sub/anotherlarge
469 $ hg --debug log -T '{rev}: {desc}\n' ../sub/anotherlarge
470 updated patterns: ['../.hglf/sub/../sub/anotherlarge', '../sub/anotherlarge']
470 updated patterns: ['../.hglf/sub/../sub/anotherlarge', '../sub/anotherlarge']
471 1: anotherlarge
471 1: anotherlarge
472
472
473 $ hg log -G anotherlarge
473 $ hg log -G anotherlarge
474 @ changeset: 1:9627a577c5e9
474 @ changeset: 1:9627a577c5e9
475 | tag: tip
475 | tag: tip
476 | user: test
476 | user: test
477 | date: Thu Jan 01 00:00:00 1970 +0000
477 | date: Thu Jan 01 00:00:00 1970 +0000
478 | summary: anotherlarge
478 | summary: anotherlarge
479 |
479 |
480
480
481 $ hg log glob:another*
481 $ hg log glob:another*
482 changeset: 1:9627a577c5e9
482 changeset: 1:9627a577c5e9
483 tag: tip
483 tag: tip
484 user: test
484 user: test
485 date: Thu Jan 01 00:00:00 1970 +0000
485 date: Thu Jan 01 00:00:00 1970 +0000
486 summary: anotherlarge
486 summary: anotherlarge
487
487
488 $ hg --debug log -T '{rev}: {desc}\n' -G glob:another*
488 $ hg --debug log -T '{rev}: {desc}\n' -G glob:another*
489 updated patterns: ['glob:../.hglf/sub/another*', 'glob:another*']
489 updated patterns: ['glob:../.hglf/sub/another*', 'glob:another*']
490 @ 1: anotherlarge
490 @ 1: anotherlarge
491 |
491 |
492
492
493 #if no-msys
493 #if no-msys
494 $ hg --debug log -T '{rev}: {desc}\n' 'glob:../.hglf/sub/another*' # no-msys
494 $ hg --debug log -T '{rev}: {desc}\n' 'glob:../.hglf/sub/another*' # no-msys
495 updated patterns: ['glob:../.hglf/sub/another*']
495 updated patterns: ['glob:../.hglf/sub/another*']
496 1: anotherlarge
496 1: anotherlarge
497
497
498 $ hg --debug log -G -T '{rev}: {desc}\n' 'glob:../.hglf/sub/another*' # no-msys
498 $ hg --debug log -G -T '{rev}: {desc}\n' 'glob:../.hglf/sub/another*' # no-msys
499 updated patterns: ['glob:../.hglf/sub/another*']
499 updated patterns: ['glob:../.hglf/sub/another*']
500 @ 1: anotherlarge
500 @ 1: anotherlarge
501 |
501 |
502 #endif
502 #endif
503
503
504 $ echo more >> anotherlarge
504 $ echo more >> anotherlarge
505 $ hg st .
505 $ hg st .
506 M anotherlarge
506 M anotherlarge
507 $ hg cat anotherlarge
507 $ hg cat anotherlarge
508 anotherlarge
508 anotherlarge
509 $ hg revert anotherlarge
509 $ hg revert anotherlarge
510 $ hg st
510 $ hg st
511 ? sub/anotherlarge.orig
511 ? sub/anotherlarge.orig
512
512
513 Test orig files go where we want them
513 Test orig files go where we want them
514 $ echo moremore >> anotherlarge
514 $ echo moremore >> anotherlarge
515 $ hg revert anotherlarge -v --config 'ui.origbackuppath=.hg/origbackups'
515 $ hg revert anotherlarge -v --config 'ui.origbackuppath=.hg/origbackups'
516 creating directory: $TESTTMP/addrm2/.hg/origbackups/.hglf/sub (glob)
516 creating directory: $TESTTMP/addrm2/.hg/origbackups/.hglf/sub (glob)
517 saving current version of ../.hglf/sub/anotherlarge as $TESTTMP/addrm2/.hg/origbackups/.hglf/sub/anotherlarge.orig (glob)
517 saving current version of ../.hglf/sub/anotherlarge as $TESTTMP/addrm2/.hg/origbackups/.hglf/sub/anotherlarge.orig (glob)
518 reverting ../.hglf/sub/anotherlarge (glob)
518 reverting ../.hglf/sub/anotherlarge (glob)
519 creating directory: $TESTTMP/addrm2/.hg/origbackups/sub (glob)
519 creating directory: $TESTTMP/addrm2/.hg/origbackups/sub (glob)
520 found 90c622cf65cebe75c5842f9136c459333faf392e in store
520 found 90c622cf65cebe75c5842f9136c459333faf392e in store
521 found 90c622cf65cebe75c5842f9136c459333faf392e in store
521 found 90c622cf65cebe75c5842f9136c459333faf392e in store
522 $ ls ../.hg/origbackups/sub
522 $ ls ../.hg/origbackups/sub
523 anotherlarge.orig
523 anotherlarge.orig
524 $ cd ..
524 $ cd ..
525
525
526 Test glob logging from the root dir
526 Test glob logging from the root dir
527 $ hg log glob:**another*
527 $ hg log glob:**another*
528 changeset: 1:9627a577c5e9
528 changeset: 1:9627a577c5e9
529 tag: tip
529 tag: tip
530 user: test
530 user: test
531 date: Thu Jan 01 00:00:00 1970 +0000
531 date: Thu Jan 01 00:00:00 1970 +0000
532 summary: anotherlarge
532 summary: anotherlarge
533
533
534 $ hg log -G glob:**another*
534 $ hg log -G glob:**another*
535 @ changeset: 1:9627a577c5e9
535 @ changeset: 1:9627a577c5e9
536 | tag: tip
536 | tag: tip
537 | user: test
537 | user: test
538 | date: Thu Jan 01 00:00:00 1970 +0000
538 | date: Thu Jan 01 00:00:00 1970 +0000
539 | summary: anotherlarge
539 | summary: anotherlarge
540 |
540 |
541
541
542 $ cd ..
542 $ cd ..
543
543
544 Log from outer space
544 Log from outer space
545 $ hg --debug log -R addrm2 -T '{rev}: {desc}\n' 'addrm2/sub/anotherlarge'
545 $ hg --debug log -R addrm2 -T '{rev}: {desc}\n' 'addrm2/sub/anotherlarge'
546 updated patterns: ['addrm2/.hglf/sub/anotherlarge', 'addrm2/sub/anotherlarge']
546 updated patterns: ['addrm2/.hglf/sub/anotherlarge', 'addrm2/sub/anotherlarge']
547 1: anotherlarge
547 1: anotherlarge
548 $ hg --debug log -R addrm2 -T '{rev}: {desc}\n' 'addrm2/.hglf/sub/anotherlarge'
548 $ hg --debug log -R addrm2 -T '{rev}: {desc}\n' 'addrm2/.hglf/sub/anotherlarge'
549 updated patterns: ['addrm2/.hglf/sub/anotherlarge']
549 updated patterns: ['addrm2/.hglf/sub/anotherlarge']
550 1: anotherlarge
550 1: anotherlarge
551
551
552
552
553 Check error message while exchange
553 Check error message while exchange
554 =========================================================
554 =========================================================
555
555
556 issue3651: summary/outgoing with largefiles shows "no remote repo"
556 issue3651: summary/outgoing with largefiles shows "no remote repo"
557 unexpectedly
557 unexpectedly
558
558
559 $ mkdir issue3651
559 $ mkdir issue3651
560 $ cd issue3651
560 $ cd issue3651
561
561
562 $ hg init src
562 $ hg init src
563 $ echo a > src/a
563 $ echo a > src/a
564 $ hg -R src add --large src/a
564 $ hg -R src add --large src/a
565 $ hg -R src commit -m '#0'
565 $ hg -R src commit -m '#0'
566 Invoking status precommit hook
566 Invoking status precommit hook
567 A a
567 A a
568
568
569 check messages when no remote repository is specified:
569 check messages when no remote repository is specified:
570 "no remote repo" route for "hg outgoing --large" is not tested here,
570 "no remote repo" route for "hg outgoing --large" is not tested here,
571 because it can't be reproduced easily.
571 because it can't be reproduced easily.
572
572
573 $ hg init clone1
573 $ hg init clone1
574 $ hg -R clone1 -q pull src
574 $ hg -R clone1 -q pull src
575 $ hg -R clone1 -q update
575 $ hg -R clone1 -q update
576 $ hg -R clone1 paths | grep default
576 $ hg -R clone1 paths | grep default
577 [1]
577 [1]
578
578
579 $ hg -R clone1 summary --large
579 $ hg -R clone1 summary --large
580 parent: 0:fc0bd45326d3 tip
580 parent: 0:fc0bd45326d3 tip
581 #0
581 #0
582 branch: default
582 branch: default
583 commit: (clean)
583 commit: (clean)
584 update: (current)
584 update: (current)
585 phases: 1 draft
585 phases: 1 draft
586 largefiles: (no remote repo)
586 largefiles: (no remote repo)
587
587
588 check messages when there is no files to upload:
588 check messages when there is no files to upload:
589
589
590 $ hg -q clone src clone2
590 $ hg -q clone src clone2
591 $ hg -R clone2 paths | grep default
591 $ hg -R clone2 paths | grep default
592 default = $TESTTMP/issue3651/src (glob)
592 default = $TESTTMP/issue3651/src (glob)
593
593
594 $ hg -R clone2 summary --large
594 $ hg -R clone2 summary --large
595 parent: 0:fc0bd45326d3 tip
595 parent: 0:fc0bd45326d3 tip
596 #0
596 #0
597 branch: default
597 branch: default
598 commit: (clean)
598 commit: (clean)
599 update: (current)
599 update: (current)
600 phases: 1 draft
600 phases: 1 draft
601 largefiles: (no files to upload)
601 largefiles: (no files to upload)
602 $ hg -R clone2 outgoing --large
602 $ hg -R clone2 outgoing --large
603 comparing with $TESTTMP/issue3651/src (glob)
603 comparing with $TESTTMP/issue3651/src (glob)
604 searching for changes
604 searching for changes
605 no changes found
605 no changes found
606 largefiles: no files to upload
606 largefiles: no files to upload
607 [1]
607 [1]
608
608
609 $ hg -R clone2 outgoing --large --graph --template "{rev}"
609 $ hg -R clone2 outgoing --large --graph --template "{rev}"
610 comparing with $TESTTMP/issue3651/src (glob)
610 comparing with $TESTTMP/issue3651/src (glob)
611 searching for changes
611 searching for changes
612 no changes found
612 no changes found
613 largefiles: no files to upload
613 largefiles: no files to upload
614
614
615 check messages when there are files to upload:
615 check messages when there are files to upload:
616
616
617 $ echo b > clone2/b
617 $ echo b > clone2/b
618 $ hg -R clone2 add --large clone2/b
618 $ hg -R clone2 add --large clone2/b
619 $ hg -R clone2 commit -m '#1'
619 $ hg -R clone2 commit -m '#1'
620 Invoking status precommit hook
620 Invoking status precommit hook
621 A b
621 A b
622 $ hg -R clone2 summary --large
622 $ hg -R clone2 summary --large
623 parent: 1:1acbe71ce432 tip
623 parent: 1:1acbe71ce432 tip
624 #1
624 #1
625 branch: default
625 branch: default
626 commit: (clean)
626 commit: (clean)
627 update: (current)
627 update: (current)
628 phases: 2 draft
628 phases: 2 draft
629 largefiles: 1 entities for 1 files to upload
629 largefiles: 1 entities for 1 files to upload
630 $ hg -R clone2 outgoing --large
630 $ hg -R clone2 outgoing --large
631 comparing with $TESTTMP/issue3651/src (glob)
631 comparing with $TESTTMP/issue3651/src (glob)
632 searching for changes
632 searching for changes
633 changeset: 1:1acbe71ce432
633 changeset: 1:1acbe71ce432
634 tag: tip
634 tag: tip
635 user: test
635 user: test
636 date: Thu Jan 01 00:00:00 1970 +0000
636 date: Thu Jan 01 00:00:00 1970 +0000
637 summary: #1
637 summary: #1
638
638
639 largefiles to upload (1 entities):
639 largefiles to upload (1 entities):
640 b
640 b
641
641
642 $ hg -R clone2 outgoing --large --graph --template "{rev}"
642 $ hg -R clone2 outgoing --large --graph --template "{rev}"
643 comparing with $TESTTMP/issue3651/src (glob)
643 comparing with $TESTTMP/issue3651/src (glob)
644 searching for changes
644 searching for changes
645 @ 1
645 @ 1
646
646
647 largefiles to upload (1 entities):
647 largefiles to upload (1 entities):
648 b
648 b
649
649
650
650
651 $ cp clone2/b clone2/b1
651 $ cp clone2/b clone2/b1
652 $ cp clone2/b clone2/b2
652 $ cp clone2/b clone2/b2
653 $ hg -R clone2 add --large clone2/b1 clone2/b2
653 $ hg -R clone2 add --large clone2/b1 clone2/b2
654 $ hg -R clone2 commit -m '#2: add largefiles referring same entity'
654 $ hg -R clone2 commit -m '#2: add largefiles referring same entity'
655 Invoking status precommit hook
655 Invoking status precommit hook
656 A b1
656 A b1
657 A b2
657 A b2
658 $ hg -R clone2 summary --large
658 $ hg -R clone2 summary --large
659 parent: 2:6095d0695d70 tip
659 parent: 2:6095d0695d70 tip
660 #2: add largefiles referring same entity
660 #2: add largefiles referring same entity
661 branch: default
661 branch: default
662 commit: (clean)
662 commit: (clean)
663 update: (current)
663 update: (current)
664 phases: 3 draft
664 phases: 3 draft
665 largefiles: 1 entities for 3 files to upload
665 largefiles: 1 entities for 3 files to upload
666 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
666 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
667 comparing with $TESTTMP/issue3651/src (glob)
667 comparing with $TESTTMP/issue3651/src (glob)
668 searching for changes
668 searching for changes
669 1:1acbe71ce432
669 1:1acbe71ce432
670 2:6095d0695d70
670 2:6095d0695d70
671 largefiles to upload (1 entities):
671 largefiles to upload (1 entities):
672 b
672 b
673 b1
673 b1
674 b2
674 b2
675
675
676 $ hg -R clone2 cat -r 1 clone2/.hglf/b
676 $ hg -R clone2 cat -r 1 clone2/.hglf/b
677 89e6c98d92887913cadf06b2adb97f26cde4849b
677 89e6c98d92887913cadf06b2adb97f26cde4849b
678 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug --config progress.debug=true
678 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug --config progress.debug=true
679 comparing with $TESTTMP/issue3651/src (glob)
679 comparing with $TESTTMP/issue3651/src (glob)
680 query 1; heads
680 query 1; heads
681 searching for changes
681 searching for changes
682 all remote heads known locally
682 all remote heads known locally
683 1:1acbe71ce432
683 1:1acbe71ce432
684 2:6095d0695d70
684 2:6095d0695d70
685 finding outgoing largefiles: 0/2 revision (0.00%)
685 finding outgoing largefiles: 0/2 revisions (0.00%)
686 finding outgoing largefiles: 1/2 revision (50.00%)
686 finding outgoing largefiles: 1/2 revisions (50.00%)
687 largefiles to upload (1 entities):
687 largefiles to upload (1 entities):
688 b
688 b
689 89e6c98d92887913cadf06b2adb97f26cde4849b
689 89e6c98d92887913cadf06b2adb97f26cde4849b
690 b1
690 b1
691 89e6c98d92887913cadf06b2adb97f26cde4849b
691 89e6c98d92887913cadf06b2adb97f26cde4849b
692 b2
692 b2
693 89e6c98d92887913cadf06b2adb97f26cde4849b
693 89e6c98d92887913cadf06b2adb97f26cde4849b
694
694
695
695
696 $ echo bbb > clone2/b
696 $ echo bbb > clone2/b
697 $ hg -R clone2 commit -m '#3: add new largefile entity as existing file'
697 $ hg -R clone2 commit -m '#3: add new largefile entity as existing file'
698 Invoking status precommit hook
698 Invoking status precommit hook
699 M b
699 M b
700 $ echo bbbb > clone2/b
700 $ echo bbbb > clone2/b
701 $ hg -R clone2 commit -m '#4: add new largefile entity as existing file'
701 $ hg -R clone2 commit -m '#4: add new largefile entity as existing file'
702 Invoking status precommit hook
702 Invoking status precommit hook
703 M b
703 M b
704 $ cp clone2/b1 clone2/b
704 $ cp clone2/b1 clone2/b
705 $ hg -R clone2 commit -m '#5: refer existing largefile entity again'
705 $ hg -R clone2 commit -m '#5: refer existing largefile entity again'
706 Invoking status precommit hook
706 Invoking status precommit hook
707 M b
707 M b
708 $ hg -R clone2 summary --large
708 $ hg -R clone2 summary --large
709 parent: 5:036794ea641c tip
709 parent: 5:036794ea641c tip
710 #5: refer existing largefile entity again
710 #5: refer existing largefile entity again
711 branch: default
711 branch: default
712 commit: (clean)
712 commit: (clean)
713 update: (current)
713 update: (current)
714 phases: 6 draft
714 phases: 6 draft
715 largefiles: 3 entities for 3 files to upload
715 largefiles: 3 entities for 3 files to upload
716 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
716 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
717 comparing with $TESTTMP/issue3651/src (glob)
717 comparing with $TESTTMP/issue3651/src (glob)
718 searching for changes
718 searching for changes
719 1:1acbe71ce432
719 1:1acbe71ce432
720 2:6095d0695d70
720 2:6095d0695d70
721 3:7983dce246cc
721 3:7983dce246cc
722 4:233f12ada4ae
722 4:233f12ada4ae
723 5:036794ea641c
723 5:036794ea641c
724 largefiles to upload (3 entities):
724 largefiles to upload (3 entities):
725 b
725 b
726 b1
726 b1
727 b2
727 b2
728
728
729 $ hg -R clone2 cat -r 3 clone2/.hglf/b
729 $ hg -R clone2 cat -r 3 clone2/.hglf/b
730 c801c9cfe94400963fcb683246217d5db77f9a9a
730 c801c9cfe94400963fcb683246217d5db77f9a9a
731 $ hg -R clone2 cat -r 4 clone2/.hglf/b
731 $ hg -R clone2 cat -r 4 clone2/.hglf/b
732 13f9ed0898e315bf59dc2973fec52037b6f441a2
732 13f9ed0898e315bf59dc2973fec52037b6f441a2
733 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug --config progress.debug=true
733 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug --config progress.debug=true
734 comparing with $TESTTMP/issue3651/src (glob)
734 comparing with $TESTTMP/issue3651/src (glob)
735 query 1; heads
735 query 1; heads
736 searching for changes
736 searching for changes
737 all remote heads known locally
737 all remote heads known locally
738 1:1acbe71ce432
738 1:1acbe71ce432
739 2:6095d0695d70
739 2:6095d0695d70
740 3:7983dce246cc
740 3:7983dce246cc
741 4:233f12ada4ae
741 4:233f12ada4ae
742 5:036794ea641c
742 5:036794ea641c
743 finding outgoing largefiles: 0/5 revision (0.00%)
743 finding outgoing largefiles: 0/5 revisions (0.00%)
744 finding outgoing largefiles: 1/5 revision (20.00%)
744 finding outgoing largefiles: 1/5 revisions (20.00%)
745 finding outgoing largefiles: 2/5 revision (40.00%)
745 finding outgoing largefiles: 2/5 revisions (40.00%)
746 finding outgoing largefiles: 3/5 revision (60.00%)
746 finding outgoing largefiles: 3/5 revisions (60.00%)
747 finding outgoing largefiles: 4/5 revision (80.00%)
747 finding outgoing largefiles: 4/5 revisions (80.00%)
748 largefiles to upload (3 entities):
748 largefiles to upload (3 entities):
749 b
749 b
750 13f9ed0898e315bf59dc2973fec52037b6f441a2
750 13f9ed0898e315bf59dc2973fec52037b6f441a2
751 89e6c98d92887913cadf06b2adb97f26cde4849b
751 89e6c98d92887913cadf06b2adb97f26cde4849b
752 c801c9cfe94400963fcb683246217d5db77f9a9a
752 c801c9cfe94400963fcb683246217d5db77f9a9a
753 b1
753 b1
754 89e6c98d92887913cadf06b2adb97f26cde4849b
754 89e6c98d92887913cadf06b2adb97f26cde4849b
755 b2
755 b2
756 89e6c98d92887913cadf06b2adb97f26cde4849b
756 89e6c98d92887913cadf06b2adb97f26cde4849b
757
757
758
758
759 Pushing revision #1 causes uploading entity 89e6c98d9288, which is
759 Pushing revision #1 causes uploading entity 89e6c98d9288, which is
760 shared also by largefiles b1, b2 in revision #2 and b in revision #5.
760 shared also by largefiles b1, b2 in revision #2 and b in revision #5.
761
761
762 Then, entity 89e6c98d9288 is not treated as "outgoing entity" at "hg
762 Then, entity 89e6c98d9288 is not treated as "outgoing entity" at "hg
763 summary" and "hg outgoing", even though files in outgoing revision #2
763 summary" and "hg outgoing", even though files in outgoing revision #2
764 and #5 refer it.
764 and #5 refer it.
765
765
766 $ hg -R clone2 push -r 1 -q
766 $ hg -R clone2 push -r 1 -q
767 $ hg -R clone2 summary --large
767 $ hg -R clone2 summary --large
768 parent: 5:036794ea641c tip
768 parent: 5:036794ea641c tip
769 #5: refer existing largefile entity again
769 #5: refer existing largefile entity again
770 branch: default
770 branch: default
771 commit: (clean)
771 commit: (clean)
772 update: (current)
772 update: (current)
773 phases: 6 draft
773 phases: 6 draft
774 largefiles: 2 entities for 1 files to upload
774 largefiles: 2 entities for 1 files to upload
775 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
775 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
776 comparing with $TESTTMP/issue3651/src (glob)
776 comparing with $TESTTMP/issue3651/src (glob)
777 searching for changes
777 searching for changes
778 2:6095d0695d70
778 2:6095d0695d70
779 3:7983dce246cc
779 3:7983dce246cc
780 4:233f12ada4ae
780 4:233f12ada4ae
781 5:036794ea641c
781 5:036794ea641c
782 largefiles to upload (2 entities):
782 largefiles to upload (2 entities):
783 b
783 b
784
784
785 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug --config progress.debug=true
785 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug --config progress.debug=true
786 comparing with $TESTTMP/issue3651/src (glob)
786 comparing with $TESTTMP/issue3651/src (glob)
787 query 1; heads
787 query 1; heads
788 searching for changes
788 searching for changes
789 all remote heads known locally
789 all remote heads known locally
790 2:6095d0695d70
790 2:6095d0695d70
791 3:7983dce246cc
791 3:7983dce246cc
792 4:233f12ada4ae
792 4:233f12ada4ae
793 5:036794ea641c
793 5:036794ea641c
794 finding outgoing largefiles: 0/4 revision (0.00%)
794 finding outgoing largefiles: 0/4 revisions (0.00%)
795 finding outgoing largefiles: 1/4 revision (25.00%)
795 finding outgoing largefiles: 1/4 revisions (25.00%)
796 finding outgoing largefiles: 2/4 revision (50.00%)
796 finding outgoing largefiles: 2/4 revisions (50.00%)
797 finding outgoing largefiles: 3/4 revision (75.00%)
797 finding outgoing largefiles: 3/4 revisions (75.00%)
798 largefiles to upload (2 entities):
798 largefiles to upload (2 entities):
799 b
799 b
800 13f9ed0898e315bf59dc2973fec52037b6f441a2
800 13f9ed0898e315bf59dc2973fec52037b6f441a2
801 c801c9cfe94400963fcb683246217d5db77f9a9a
801 c801c9cfe94400963fcb683246217d5db77f9a9a
802
802
803
803
804 $ cd ..
804 $ cd ..
805
805
806 merge action 'd' for 'local renamed directory to d2/g' which has no filename
806 merge action 'd' for 'local renamed directory to d2/g' which has no filename
807 ==================================================================================
807 ==================================================================================
808
808
809 $ hg init merge-action
809 $ hg init merge-action
810 $ cd merge-action
810 $ cd merge-action
811 $ touch l
811 $ touch l
812 $ hg add --large l
812 $ hg add --large l
813 $ mkdir d1
813 $ mkdir d1
814 $ touch d1/f
814 $ touch d1/f
815 $ hg ci -Aqm0
815 $ hg ci -Aqm0
816 Invoking status precommit hook
816 Invoking status precommit hook
817 A d1/f
817 A d1/f
818 A l
818 A l
819 $ echo > d1/f
819 $ echo > d1/f
820 $ touch d1/g
820 $ touch d1/g
821 $ hg ci -Aqm1
821 $ hg ci -Aqm1
822 Invoking status precommit hook
822 Invoking status precommit hook
823 M d1/f
823 M d1/f
824 A d1/g
824 A d1/g
825 $ hg up -qr0
825 $ hg up -qr0
826 $ hg mv d1 d2
826 $ hg mv d1 d2
827 moving d1/f to d2/f (glob)
827 moving d1/f to d2/f (glob)
828 $ hg ci -qm2
828 $ hg ci -qm2
829 Invoking status precommit hook
829 Invoking status precommit hook
830 A d2/f
830 A d2/f
831 R d1/f
831 R d1/f
832 $ hg merge
832 $ hg merge
833 merging d2/f and d1/f to d2/f
833 merging d2/f and d1/f to d2/f
834 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
834 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
835 (branch merge, don't forget to commit)
835 (branch merge, don't forget to commit)
836 $ cd ..
836 $ cd ..
837
837
838
838
839 Merge conflicts:
839 Merge conflicts:
840 =====================
840 =====================
841
841
842 $ hg init merge
842 $ hg init merge
843 $ cd merge
843 $ cd merge
844 $ echo 0 > f-different
844 $ echo 0 > f-different
845 $ echo 0 > f-same
845 $ echo 0 > f-same
846 $ echo 0 > f-unchanged-1
846 $ echo 0 > f-unchanged-1
847 $ echo 0 > f-unchanged-2
847 $ echo 0 > f-unchanged-2
848 $ hg add --large *
848 $ hg add --large *
849 $ hg ci -m0
849 $ hg ci -m0
850 Invoking status precommit hook
850 Invoking status precommit hook
851 A f-different
851 A f-different
852 A f-same
852 A f-same
853 A f-unchanged-1
853 A f-unchanged-1
854 A f-unchanged-2
854 A f-unchanged-2
855 $ echo tmp1 > f-unchanged-1
855 $ echo tmp1 > f-unchanged-1
856 $ echo tmp1 > f-unchanged-2
856 $ echo tmp1 > f-unchanged-2
857 $ echo tmp1 > f-same
857 $ echo tmp1 > f-same
858 $ hg ci -m1
858 $ hg ci -m1
859 Invoking status precommit hook
859 Invoking status precommit hook
860 M f-same
860 M f-same
861 M f-unchanged-1
861 M f-unchanged-1
862 M f-unchanged-2
862 M f-unchanged-2
863 $ echo 2 > f-different
863 $ echo 2 > f-different
864 $ echo 0 > f-unchanged-1
864 $ echo 0 > f-unchanged-1
865 $ echo 1 > f-unchanged-2
865 $ echo 1 > f-unchanged-2
866 $ echo 1 > f-same
866 $ echo 1 > f-same
867 $ hg ci -m2
867 $ hg ci -m2
868 Invoking status precommit hook
868 Invoking status precommit hook
869 M f-different
869 M f-different
870 M f-same
870 M f-same
871 M f-unchanged-1
871 M f-unchanged-1
872 M f-unchanged-2
872 M f-unchanged-2
873 $ hg up -qr0
873 $ hg up -qr0
874 $ echo tmp2 > f-unchanged-1
874 $ echo tmp2 > f-unchanged-1
875 $ echo tmp2 > f-unchanged-2
875 $ echo tmp2 > f-unchanged-2
876 $ echo tmp2 > f-same
876 $ echo tmp2 > f-same
877 $ hg ci -m3
877 $ hg ci -m3
878 Invoking status precommit hook
878 Invoking status precommit hook
879 M f-same
879 M f-same
880 M f-unchanged-1
880 M f-unchanged-1
881 M f-unchanged-2
881 M f-unchanged-2
882 created new head
882 created new head
883 $ echo 1 > f-different
883 $ echo 1 > f-different
884 $ echo 1 > f-unchanged-1
884 $ echo 1 > f-unchanged-1
885 $ echo 0 > f-unchanged-2
885 $ echo 0 > f-unchanged-2
886 $ echo 1 > f-same
886 $ echo 1 > f-same
887 $ hg ci -m4
887 $ hg ci -m4
888 Invoking status precommit hook
888 Invoking status precommit hook
889 M f-different
889 M f-different
890 M f-same
890 M f-same
891 M f-unchanged-1
891 M f-unchanged-1
892 M f-unchanged-2
892 M f-unchanged-2
893 $ hg merge
893 $ hg merge
894 largefile f-different has a merge conflict
894 largefile f-different has a merge conflict
895 ancestor was 09d2af8dd22201dd8d48e5dcfcaed281ff9422c7
895 ancestor was 09d2af8dd22201dd8d48e5dcfcaed281ff9422c7
896 keep (l)ocal e5fa44f2b31c1fb553b6021e7360d07d5d91ff5e or
896 keep (l)ocal e5fa44f2b31c1fb553b6021e7360d07d5d91ff5e or
897 take (o)ther 7448d8798a4380162d4b56f9b452e2f6f9e24e7a? l
897 take (o)ther 7448d8798a4380162d4b56f9b452e2f6f9e24e7a? l
898 getting changed largefiles
898 getting changed largefiles
899 1 largefiles updated, 0 removed
899 1 largefiles updated, 0 removed
900 0 files updated, 4 files merged, 0 files removed, 0 files unresolved
900 0 files updated, 4 files merged, 0 files removed, 0 files unresolved
901 (branch merge, don't forget to commit)
901 (branch merge, don't forget to commit)
902 $ cat f-different
902 $ cat f-different
903 1
903 1
904 $ cat f-same
904 $ cat f-same
905 1
905 1
906 $ cat f-unchanged-1
906 $ cat f-unchanged-1
907 1
907 1
908 $ cat f-unchanged-2
908 $ cat f-unchanged-2
909 1
909 1
910 $ cd ..
910 $ cd ..
911
911
912 Test largefile insulation (do not enabled a side effect
912 Test largefile insulation (do not enabled a side effect
913 ========================================================
913 ========================================================
914
914
915 Check whether "largefiles" feature is supported only in repositories
915 Check whether "largefiles" feature is supported only in repositories
916 enabling largefiles extension.
916 enabling largefiles extension.
917
917
918 $ mkdir individualenabling
918 $ mkdir individualenabling
919 $ cd individualenabling
919 $ cd individualenabling
920
920
921 $ hg init enabledlocally
921 $ hg init enabledlocally
922 $ echo large > enabledlocally/large
922 $ echo large > enabledlocally/large
923 $ hg -R enabledlocally add --large enabledlocally/large
923 $ hg -R enabledlocally add --large enabledlocally/large
924 $ hg -R enabledlocally commit -m '#0'
924 $ hg -R enabledlocally commit -m '#0'
925 Invoking status precommit hook
925 Invoking status precommit hook
926 A large
926 A large
927
927
928 $ hg init notenabledlocally
928 $ hg init notenabledlocally
929 $ echo large > notenabledlocally/large
929 $ echo large > notenabledlocally/large
930 $ hg -R notenabledlocally add --large notenabledlocally/large
930 $ hg -R notenabledlocally add --large notenabledlocally/large
931 $ hg -R notenabledlocally commit -m '#0'
931 $ hg -R notenabledlocally commit -m '#0'
932 Invoking status precommit hook
932 Invoking status precommit hook
933 A large
933 A large
934
934
935 $ cat >> $HGRCPATH <<EOF
935 $ cat >> $HGRCPATH <<EOF
936 > [extensions]
936 > [extensions]
937 > # disable globally
937 > # disable globally
938 > largefiles=!
938 > largefiles=!
939 > EOF
939 > EOF
940 $ cat >> enabledlocally/.hg/hgrc <<EOF
940 $ cat >> enabledlocally/.hg/hgrc <<EOF
941 > [extensions]
941 > [extensions]
942 > # enable locally
942 > # enable locally
943 > largefiles=
943 > largefiles=
944 > EOF
944 > EOF
945 $ hg -R enabledlocally root
945 $ hg -R enabledlocally root
946 $TESTTMP/individualenabling/enabledlocally (glob)
946 $TESTTMP/individualenabling/enabledlocally (glob)
947 $ hg -R notenabledlocally root
947 $ hg -R notenabledlocally root
948 abort: repository requires features unknown to this Mercurial: largefiles!
948 abort: repository requires features unknown to this Mercurial: largefiles!
949 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
949 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
950 [255]
950 [255]
951
951
952 $ hg init push-dst
952 $ hg init push-dst
953 $ hg -R enabledlocally push push-dst
953 $ hg -R enabledlocally push push-dst
954 pushing to push-dst
954 pushing to push-dst
955 abort: required features are not supported in the destination: largefiles
955 abort: required features are not supported in the destination: largefiles
956 [255]
956 [255]
957
957
958 $ hg init pull-src
958 $ hg init pull-src
959 $ hg -R pull-src pull enabledlocally
959 $ hg -R pull-src pull enabledlocally
960 pulling from enabledlocally
960 pulling from enabledlocally
961 abort: required features are not supported in the destination: largefiles
961 abort: required features are not supported in the destination: largefiles
962 [255]
962 [255]
963
963
964 $ hg clone enabledlocally clone-dst
964 $ hg clone enabledlocally clone-dst
965 abort: repository requires features unknown to this Mercurial: largefiles!
965 abort: repository requires features unknown to this Mercurial: largefiles!
966 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
966 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
967 [255]
967 [255]
968 $ test -d clone-dst
968 $ test -d clone-dst
969 [1]
969 [1]
970 $ hg clone --pull enabledlocally clone-pull-dst
970 $ hg clone --pull enabledlocally clone-pull-dst
971 abort: required features are not supported in the destination: largefiles
971 abort: required features are not supported in the destination: largefiles
972 [255]
972 [255]
973 $ test -d clone-pull-dst
973 $ test -d clone-pull-dst
974 [1]
974 [1]
975
975
976 #if serve
976 #if serve
977
977
978 Test largefiles specific peer setup, when largefiles is enabled
978 Test largefiles specific peer setup, when largefiles is enabled
979 locally (issue4109)
979 locally (issue4109)
980
980
981 $ hg showconfig extensions | grep largefiles
981 $ hg showconfig extensions | grep largefiles
982 extensions.largefiles=!
982 extensions.largefiles=!
983 $ mkdir -p $TESTTMP/individualenabling/usercache
983 $ mkdir -p $TESTTMP/individualenabling/usercache
984
984
985 $ hg serve -R enabledlocally -d -p $HGPORT --pid-file hg.pid
985 $ hg serve -R enabledlocally -d -p $HGPORT --pid-file hg.pid
986 $ cat hg.pid >> $DAEMON_PIDS
986 $ cat hg.pid >> $DAEMON_PIDS
987
987
988 $ hg init pull-dst
988 $ hg init pull-dst
989 $ cat > pull-dst/.hg/hgrc <<EOF
989 $ cat > pull-dst/.hg/hgrc <<EOF
990 > [extensions]
990 > [extensions]
991 > # enable locally
991 > # enable locally
992 > largefiles=
992 > largefiles=
993 > [largefiles]
993 > [largefiles]
994 > # ignore system cache to force largefiles specific wire proto access
994 > # ignore system cache to force largefiles specific wire proto access
995 > usercache=$TESTTMP/individualenabling/usercache
995 > usercache=$TESTTMP/individualenabling/usercache
996 > EOF
996 > EOF
997 $ hg -R pull-dst -q pull -u http://localhost:$HGPORT
997 $ hg -R pull-dst -q pull -u http://localhost:$HGPORT
998
998
999 $ killdaemons.py
999 $ killdaemons.py
1000 #endif
1000 #endif
1001
1001
1002 Test overridden functions work correctly even for repos disabling
1002 Test overridden functions work correctly even for repos disabling
1003 largefiles (issue4547)
1003 largefiles (issue4547)
1004
1004
1005 $ hg showconfig extensions | grep largefiles
1005 $ hg showconfig extensions | grep largefiles
1006 extensions.largefiles=!
1006 extensions.largefiles=!
1007
1007
1008 (test updating implied by clone)
1008 (test updating implied by clone)
1009
1009
1010 $ hg init enabled-but-no-largefiles
1010 $ hg init enabled-but-no-largefiles
1011 $ echo normal1 > enabled-but-no-largefiles/normal1
1011 $ echo normal1 > enabled-but-no-largefiles/normal1
1012 $ hg -R enabled-but-no-largefiles add enabled-but-no-largefiles/normal1
1012 $ hg -R enabled-but-no-largefiles add enabled-but-no-largefiles/normal1
1013 $ hg -R enabled-but-no-largefiles commit -m '#0@enabled-but-no-largefiles'
1013 $ hg -R enabled-but-no-largefiles commit -m '#0@enabled-but-no-largefiles'
1014 Invoking status precommit hook
1014 Invoking status precommit hook
1015 A normal1
1015 A normal1
1016 $ cat >> enabled-but-no-largefiles/.hg/hgrc <<EOF
1016 $ cat >> enabled-but-no-largefiles/.hg/hgrc <<EOF
1017 > [extensions]
1017 > [extensions]
1018 > # enable locally
1018 > # enable locally
1019 > largefiles=
1019 > largefiles=
1020 > EOF
1020 > EOF
1021 $ hg clone -q enabled-but-no-largefiles no-largefiles
1021 $ hg clone -q enabled-but-no-largefiles no-largefiles
1022
1022
1023 $ echo normal2 > enabled-but-no-largefiles/normal2
1023 $ echo normal2 > enabled-but-no-largefiles/normal2
1024 $ hg -R enabled-but-no-largefiles add enabled-but-no-largefiles/normal2
1024 $ hg -R enabled-but-no-largefiles add enabled-but-no-largefiles/normal2
1025 $ hg -R enabled-but-no-largefiles commit -m '#1@enabled-but-no-largefiles'
1025 $ hg -R enabled-but-no-largefiles commit -m '#1@enabled-but-no-largefiles'
1026 Invoking status precommit hook
1026 Invoking status precommit hook
1027 A normal2
1027 A normal2
1028
1028
1029 $ echo normal3 > no-largefiles/normal3
1029 $ echo normal3 > no-largefiles/normal3
1030 $ hg -R no-largefiles add no-largefiles/normal3
1030 $ hg -R no-largefiles add no-largefiles/normal3
1031 $ hg -R no-largefiles commit -m '#1@no-largefiles'
1031 $ hg -R no-largefiles commit -m '#1@no-largefiles'
1032 Invoking status precommit hook
1032 Invoking status precommit hook
1033 A normal3
1033 A normal3
1034
1034
1035 $ hg -R no-largefiles -q pull --rebase
1035 $ hg -R no-largefiles -q pull --rebase
1036 Invoking status precommit hook
1036 Invoking status precommit hook
1037 A normal3
1037 A normal3
1038
1038
1039 (test reverting)
1039 (test reverting)
1040
1040
1041 $ hg init subrepo-root
1041 $ hg init subrepo-root
1042 $ cat >> subrepo-root/.hg/hgrc <<EOF
1042 $ cat >> subrepo-root/.hg/hgrc <<EOF
1043 > [extensions]
1043 > [extensions]
1044 > # enable locally
1044 > # enable locally
1045 > largefiles=
1045 > largefiles=
1046 > EOF
1046 > EOF
1047 $ echo large > subrepo-root/large
1047 $ echo large > subrepo-root/large
1048 $ hg -R subrepo-root add --large subrepo-root/large
1048 $ hg -R subrepo-root add --large subrepo-root/large
1049 $ hg clone -q no-largefiles subrepo-root/no-largefiles
1049 $ hg clone -q no-largefiles subrepo-root/no-largefiles
1050 $ cat > subrepo-root/.hgsub <<EOF
1050 $ cat > subrepo-root/.hgsub <<EOF
1051 > no-largefiles = no-largefiles
1051 > no-largefiles = no-largefiles
1052 > EOF
1052 > EOF
1053 $ hg -R subrepo-root add subrepo-root/.hgsub
1053 $ hg -R subrepo-root add subrepo-root/.hgsub
1054 $ hg -R subrepo-root commit -m '#0'
1054 $ hg -R subrepo-root commit -m '#0'
1055 Invoking status precommit hook
1055 Invoking status precommit hook
1056 A .hgsub
1056 A .hgsub
1057 A large
1057 A large
1058 ? .hgsubstate
1058 ? .hgsubstate
1059 $ echo dirty >> subrepo-root/large
1059 $ echo dirty >> subrepo-root/large
1060 $ echo dirty >> subrepo-root/no-largefiles/normal1
1060 $ echo dirty >> subrepo-root/no-largefiles/normal1
1061 $ hg -R subrepo-root status -S
1061 $ hg -R subrepo-root status -S
1062 M large
1062 M large
1063 M no-largefiles/normal1
1063 M no-largefiles/normal1
1064 $ hg -R subrepo-root revert --all
1064 $ hg -R subrepo-root revert --all
1065 reverting subrepo-root/.hglf/large (glob)
1065 reverting subrepo-root/.hglf/large (glob)
1066 reverting subrepo no-largefiles
1066 reverting subrepo no-largefiles
1067 reverting subrepo-root/no-largefiles/normal1 (glob)
1067 reverting subrepo-root/no-largefiles/normal1 (glob)
1068
1068
1069 $ cd ..
1069 $ cd ..
1070
1070
1071
1071
1072 Test "pull --rebase" when rebase is enabled before largefiles (issue3861)
1072 Test "pull --rebase" when rebase is enabled before largefiles (issue3861)
1073 =========================================================================
1073 =========================================================================
1074
1074
1075 $ hg showconfig extensions | grep largefiles
1075 $ hg showconfig extensions | grep largefiles
1076 extensions.largefiles=!
1076 extensions.largefiles=!
1077
1077
1078 $ mkdir issue3861
1078 $ mkdir issue3861
1079 $ cd issue3861
1079 $ cd issue3861
1080 $ hg init src
1080 $ hg init src
1081 $ hg clone -q src dst
1081 $ hg clone -q src dst
1082 $ echo a > src/a
1082 $ echo a > src/a
1083 $ hg -R src commit -Aqm "#0"
1083 $ hg -R src commit -Aqm "#0"
1084 Invoking status precommit hook
1084 Invoking status precommit hook
1085 A a
1085 A a
1086
1086
1087 $ cat >> dst/.hg/hgrc <<EOF
1087 $ cat >> dst/.hg/hgrc <<EOF
1088 > [extensions]
1088 > [extensions]
1089 > largefiles=
1089 > largefiles=
1090 > EOF
1090 > EOF
1091 $ hg -R dst pull --rebase
1091 $ hg -R dst pull --rebase
1092 pulling from $TESTTMP/issue3861/src (glob)
1092 pulling from $TESTTMP/issue3861/src (glob)
1093 requesting all changes
1093 requesting all changes
1094 adding changesets
1094 adding changesets
1095 adding manifests
1095 adding manifests
1096 adding file changes
1096 adding file changes
1097 added 1 changesets with 1 changes to 1 files
1097 added 1 changesets with 1 changes to 1 files
1098 nothing to rebase - updating instead
1098 nothing to rebase - updating instead
1099 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1099 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1100
1100
1101 $ cd ..
1101 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now