##// END OF EJS Templates
lfs: allow the pointer file to be viewed with `hg cat -T '{rawdata}'`...
Matt Harbison -
r35681:a9858349 default
parent child Browse files
Show More
@@ -1,246 +1,248 b''
1 1 # lfs - hash-preserving large file support using Git-LFS protocol
2 2 #
3 3 # Copyright 2017 Facebook, Inc.
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 """lfs - large file support (EXPERIMENTAL)
9 9
10 10 Configs::
11 11
12 12 [lfs]
13 13 # Remote endpoint. Multiple protocols are supported:
14 14 # - http(s)://user:pass@example.com/path
15 15 # git-lfs endpoint
16 16 # - file:///tmp/path
17 17 # local filesystem, usually for testing
18 18 # if unset, lfs will prompt setting this when it must use this value.
19 19 # (default: unset)
20 20 url = https://example.com/lfs
21 21
22 22 # Which files to track in LFS. Path tests are "**.extname" for file
23 23 # extensions, and "path:under/some/directory" for path prefix. Both
24 24 # are relative to the repository root, and the latter must be quoted.
25 25 # File size can be tested with the "size()" fileset, and tests can be
26 26 # joined with fileset operators. (See "hg help filesets.operators".)
27 27 #
28 28 # Some examples:
29 29 # - all() # everything
30 30 # - none() # nothing
31 31 # - size(">20MB") # larger than 20MB
32 32 # - !**.txt # anything not a *.txt file
33 33 # - **.zip | **.tar.gz | **.7z # some types of compressed files
34 34 # - "path:bin" # files under "bin" in the project root
35 35 # - (**.php & size(">2MB")) | (**.js & size(">5MB")) | **.tar.gz
36 36 # | ("path:bin" & !"path:/bin/README") | size(">1GB")
37 37 # (default: none())
38 38 track = size(">10M")
39 39
40 40 # how many times to retry before giving up on transferring an object
41 41 retry = 5
42 42
43 43 # the local directory to store lfs files for sharing across local clones.
44 44 # If not set, the cache is located in an OS specific cache location.
45 45 usercache = /path/to/global/cache
46 46 """
47 47
48 48 from __future__ import absolute_import
49 49
50 50 from mercurial.i18n import _
51 51
52 52 from mercurial import (
53 53 bundle2,
54 54 changegroup,
55 cmdutil,
55 56 context,
56 57 exchange,
57 58 extensions,
58 59 filelog,
59 60 fileset,
60 61 hg,
61 62 localrepo,
62 63 minifileset,
63 64 node,
64 65 pycompat,
65 66 registrar,
66 67 revlog,
67 68 scmutil,
68 69 templatekw,
69 70 upgrade,
70 71 vfs as vfsmod,
71 72 wireproto,
72 73 )
73 74
74 75 from . import (
75 76 blobstore,
76 77 wrapper,
77 78 )
78 79
79 80 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
80 81 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
81 82 # be specifying the version(s) of Mercurial they are tested with, or
82 83 # leave the attribute unspecified.
83 84 testedwith = 'ships-with-hg-core'
84 85
85 86 configtable = {}
86 87 configitem = registrar.configitem(configtable)
87 88
88 89 configitem('experimental', 'lfs.user-agent',
89 90 default=None,
90 91 )
91 92
92 93 configitem('lfs', 'url',
93 94 default=None,
94 95 )
95 96 configitem('lfs', 'usercache',
96 97 default=None,
97 98 )
98 99 # Deprecated
99 100 configitem('lfs', 'threshold',
100 101 default=None,
101 102 )
102 103 configitem('lfs', 'track',
103 104 default='none()',
104 105 )
105 106 configitem('lfs', 'retry',
106 107 default=5,
107 108 )
108 109
109 110 cmdtable = {}
110 111 command = registrar.command(cmdtable)
111 112
112 113 templatekeyword = registrar.templatekeyword()
113 114
114 115 def featuresetup(ui, supported):
115 116 # don't die on seeing a repo with the lfs requirement
116 117 supported |= {'lfs'}
117 118
118 119 def uisetup(ui):
119 120 localrepo.localrepository.featuresetupfuncs.add(featuresetup)
120 121
121 122 def reposetup(ui, repo):
122 123 # Nothing to do with a remote repo
123 124 if not repo.local():
124 125 return
125 126
126 127 trackspec = repo.ui.config('lfs', 'track')
127 128
128 129 # deprecated config: lfs.threshold
129 130 threshold = repo.ui.configbytes('lfs', 'threshold')
130 131 if threshold:
131 132 fileset.parse(trackspec) # make sure syntax errors are confined
132 133 trackspec = "(%s) | size('>%d')" % (trackspec, threshold)
133 134
134 135 repo.svfs.options['lfstrack'] = minifileset.compile(trackspec)
135 136 repo.svfs.lfslocalblobstore = blobstore.local(repo)
136 137 repo.svfs.lfsremoteblobstore = blobstore.remote(repo)
137 138
138 139 # Push hook
139 140 repo.prepushoutgoinghooks.add('lfs', wrapper.prepush)
140 141
141 142 if 'lfs' not in repo.requirements:
142 143 def checkrequireslfs(ui, repo, **kwargs):
143 144 if 'lfs' not in repo.requirements:
144 145 last = kwargs.get('node_last')
145 146 _bin = node.bin
146 147 if last:
147 148 s = repo.set('%n:%n', _bin(kwargs['node']), _bin(last))
148 149 else:
149 150 s = repo.set('%n', _bin(kwargs['node']))
150 151 for ctx in s:
151 152 # TODO: is there a way to just walk the files in the commit?
152 153 if any(ctx[f].islfs() for f in ctx.files() if f in ctx):
153 154 repo.requirements.add('lfs')
154 155 repo._writerequirements()
155 156 break
156 157
157 158 ui.setconfig('hooks', 'commit.lfs', checkrequireslfs, 'lfs')
158 159 ui.setconfig('hooks', 'pretxnchangegroup.lfs', checkrequireslfs, 'lfs')
159 160
160 161 def wrapfilelog(filelog):
161 162 wrapfunction = extensions.wrapfunction
162 163
163 164 wrapfunction(filelog, 'addrevision', wrapper.filelogaddrevision)
164 165 wrapfunction(filelog, 'renamed', wrapper.filelogrenamed)
165 166 wrapfunction(filelog, 'size', wrapper.filelogsize)
166 167
167 168 def extsetup(ui):
168 169 wrapfilelog(filelog.filelog)
169 170
170 171 wrapfunction = extensions.wrapfunction
171 172
173 wrapfunction(cmdutil, '_updatecatformatter', wrapper._updatecatformatter)
172 174 wrapfunction(scmutil, 'wrapconvertsink', wrapper.convertsink)
173 175
174 176 wrapfunction(upgrade, '_finishdatamigration',
175 177 wrapper.upgradefinishdatamigration)
176 178
177 179 wrapfunction(upgrade, 'preservedrequirements',
178 180 wrapper.upgraderequirements)
179 181
180 182 wrapfunction(upgrade, 'supporteddestrequirements',
181 183 wrapper.upgraderequirements)
182 184
183 185 wrapfunction(changegroup,
184 186 'supportedoutgoingversions',
185 187 wrapper.supportedoutgoingversions)
186 188 wrapfunction(changegroup,
187 189 'allsupportedversions',
188 190 wrapper.allsupportedversions)
189 191
190 192 wrapfunction(exchange, 'push', wrapper.push)
191 193 wrapfunction(wireproto, '_capabilities', wrapper._capabilities)
192 194
193 195 wrapfunction(context.basefilectx, 'cmp', wrapper.filectxcmp)
194 196 wrapfunction(context.basefilectx, 'isbinary', wrapper.filectxisbinary)
195 197 context.basefilectx.islfs = wrapper.filectxislfs
196 198
197 199 revlog.addflagprocessor(
198 200 revlog.REVIDX_EXTSTORED,
199 201 (
200 202 wrapper.readfromstore,
201 203 wrapper.writetostore,
202 204 wrapper.bypasscheckhash,
203 205 ),
204 206 )
205 207
206 208 wrapfunction(hg, 'clone', wrapper.hgclone)
207 209 wrapfunction(hg, 'postshare', wrapper.hgpostshare)
208 210
209 211 # Make bundle choose changegroup3 instead of changegroup2. This affects
210 212 # "hg bundle" command. Note: it does not cover all bundle formats like
211 213 # "packed1". Using "packed1" with lfs will likely cause trouble.
212 214 names = [k for k, v in exchange._bundlespeccgversions.items() if v == '02']
213 215 for k in names:
214 216 exchange._bundlespeccgversions[k] = '03'
215 217
216 218 # bundlerepo uses "vfsmod.readonlyvfs(othervfs)", we need to make sure lfs
217 219 # options and blob stores are passed from othervfs to the new readonlyvfs.
218 220 wrapfunction(vfsmod.readonlyvfs, '__init__', wrapper.vfsinit)
219 221
220 222 # when writing a bundle via "hg bundle" command, upload related LFS blobs
221 223 wrapfunction(bundle2, 'writenewbundle', wrapper.writenewbundle)
222 224
223 225 @templatekeyword('lfs_files')
224 226 def lfsfiles(repo, ctx, **args):
225 227 """List of strings. LFS files added or modified by the changeset."""
226 228 args = pycompat.byteskwargs(args)
227 229
228 230 pointers = wrapper.pointersfromctx(ctx) # {path: pointer}
229 231 files = sorted(pointers.keys())
230 232
231 233 makemap = lambda v: {
232 234 'file': v,
233 235 'oid': pointers[v].oid(),
234 236 }
235 237
236 238 # TODO: make the separator ', '?
237 239 f = templatekw._showlist('lfs_file', files, args)
238 240 return templatekw._hybrid(f, files, makemap, pycompat.identity)
239 241
240 242 @command('debuglfsupload',
241 243 [('r', 'rev', [], _('upload large files introduced by REV'))])
242 244 def debuglfsupload(ui, repo, **opts):
243 245 """upload lfs blobs added by the working copy parent or given revisions"""
244 246 revs = opts.get('rev', [])
245 247 pointers = wrapper.extractpointers(repo, scmutil.revrange(repo, revs))
246 248 wrapper.uploadblobs(repo, pointers)
@@ -1,346 +1,350 b''
1 1 # wrapper.py - methods wrapping core mercurial logic
2 2 #
3 3 # Copyright 2017 Facebook, Inc.
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import hashlib
11 11
12 12 from mercurial.i18n import _
13 13 from mercurial.node import bin, nullid, short
14 14
15 15 from mercurial import (
16 16 error,
17 17 filelog,
18 18 revlog,
19 19 util,
20 20 )
21 21
22 22 from ..largefiles import lfutil
23 23
24 24 from . import (
25 25 blobstore,
26 26 pointer,
27 27 )
28 28
29 29 def supportedoutgoingversions(orig, repo):
30 30 versions = orig(repo)
31 31 if 'lfs' in repo.requirements:
32 32 versions.discard('01')
33 33 versions.discard('02')
34 34 versions.add('03')
35 35 return versions
36 36
37 37 def allsupportedversions(orig, ui):
38 38 versions = orig(ui)
39 39 versions.add('03')
40 40 return versions
41 41
42 42 def _capabilities(orig, repo, proto):
43 43 '''Wrap server command to announce lfs server capability'''
44 44 caps = orig(repo, proto)
45 45 # XXX: change to 'lfs=serve' when separate git server isn't required?
46 46 caps.append('lfs')
47 47 return caps
48 48
49 49 def bypasscheckhash(self, text):
50 50 return False
51 51
52 52 def readfromstore(self, text):
53 53 """Read filelog content from local blobstore transform for flagprocessor.
54 54
55 55 Default tranform for flagprocessor, returning contents from blobstore.
56 56 Returns a 2-typle (text, validatehash) where validatehash is True as the
57 57 contents of the blobstore should be checked using checkhash.
58 58 """
59 59 p = pointer.deserialize(text)
60 60 oid = p.oid()
61 61 store = self.opener.lfslocalblobstore
62 62 if not store.has(oid):
63 63 p.filename = self.filename
64 64 self.opener.lfsremoteblobstore.readbatch([p], store)
65 65
66 66 # The caller will validate the content
67 67 text = store.read(oid, verify=False)
68 68
69 69 # pack hg filelog metadata
70 70 hgmeta = {}
71 71 for k in p.keys():
72 72 if k.startswith('x-hg-'):
73 73 name = k[len('x-hg-'):]
74 74 hgmeta[name] = p[k]
75 75 if hgmeta or text.startswith('\1\n'):
76 76 text = filelog.packmeta(hgmeta, text)
77 77
78 78 return (text, True)
79 79
80 80 def writetostore(self, text):
81 81 # hg filelog metadata (includes rename, etc)
82 82 hgmeta, offset = filelog.parsemeta(text)
83 83 if offset and offset > 0:
84 84 # lfs blob does not contain hg filelog metadata
85 85 text = text[offset:]
86 86
87 87 # git-lfs only supports sha256
88 88 oid = hashlib.sha256(text).hexdigest()
89 89 self.opener.lfslocalblobstore.write(oid, text)
90 90
91 91 # replace contents with metadata
92 92 longoid = 'sha256:%s' % oid
93 93 metadata = pointer.gitlfspointer(oid=longoid, size=str(len(text)))
94 94
95 95 # by default, we expect the content to be binary. however, LFS could also
96 96 # be used for non-binary content. add a special entry for non-binary data.
97 97 # this will be used by filectx.isbinary().
98 98 if not util.binary(text):
99 99 # not hg filelog metadata (affecting commit hash), no "x-hg-" prefix
100 100 metadata['x-is-binary'] = '0'
101 101
102 102 # translate hg filelog metadata to lfs metadata with "x-hg-" prefix
103 103 if hgmeta is not None:
104 104 for k, v in hgmeta.iteritems():
105 105 metadata['x-hg-%s' % k] = v
106 106
107 107 rawtext = metadata.serialize()
108 108 return (rawtext, False)
109 109
110 110 def _islfs(rlog, node=None, rev=None):
111 111 if rev is None:
112 112 if node is None:
113 113 # both None - likely working copy content where node is not ready
114 114 return False
115 115 rev = rlog.rev(node)
116 116 else:
117 117 node = rlog.node(rev)
118 118 if node == nullid:
119 119 return False
120 120 flags = rlog.flags(rev)
121 121 return bool(flags & revlog.REVIDX_EXTSTORED)
122 122
123 123 def filelogaddrevision(orig, self, text, transaction, link, p1, p2,
124 124 cachedelta=None, node=None,
125 125 flags=revlog.REVIDX_DEFAULT_FLAGS, **kwds):
126 126 textlen = len(text)
127 127 # exclude hg rename meta from file size
128 128 meta, offset = filelog.parsemeta(text)
129 129 if offset:
130 130 textlen -= offset
131 131
132 132 lfstrack = self.opener.options['lfstrack']
133 133
134 134 # Always exclude hg owned files
135 135 if not self.filename.startswith('.hg') and lfstrack(self.filename, textlen):
136 136 flags |= revlog.REVIDX_EXTSTORED
137 137
138 138 return orig(self, text, transaction, link, p1, p2, cachedelta=cachedelta,
139 139 node=node, flags=flags, **kwds)
140 140
141 141 def filelogrenamed(orig, self, node):
142 142 if _islfs(self, node):
143 143 rawtext = self.revision(node, raw=True)
144 144 if not rawtext:
145 145 return False
146 146 metadata = pointer.deserialize(rawtext)
147 147 if 'x-hg-copy' in metadata and 'x-hg-copyrev' in metadata:
148 148 return metadata['x-hg-copy'], bin(metadata['x-hg-copyrev'])
149 149 else:
150 150 return False
151 151 return orig(self, node)
152 152
153 153 def filelogsize(orig, self, rev):
154 154 if _islfs(self, rev=rev):
155 155 # fast path: use lfs metadata to answer size
156 156 rawtext = self.revision(rev, raw=True)
157 157 metadata = pointer.deserialize(rawtext)
158 158 return int(metadata['size'])
159 159 return orig(self, rev)
160 160
161 161 def filectxcmp(orig, self, fctx):
162 162 """returns True if text is different than fctx"""
163 163 # some fctx (ex. hg-git) is not based on basefilectx and do not have islfs
164 164 if self.islfs() and getattr(fctx, 'islfs', lambda: False)():
165 165 # fast path: check LFS oid
166 166 p1 = pointer.deserialize(self.rawdata())
167 167 p2 = pointer.deserialize(fctx.rawdata())
168 168 return p1.oid() != p2.oid()
169 169 return orig(self, fctx)
170 170
171 171 def filectxisbinary(orig, self):
172 172 if self.islfs():
173 173 # fast path: use lfs metadata to answer isbinary
174 174 metadata = pointer.deserialize(self.rawdata())
175 175 # if lfs metadata says nothing, assume it's binary by default
176 176 return bool(int(metadata.get('x-is-binary', 1)))
177 177 return orig(self)
178 178
179 179 def filectxislfs(self):
180 180 return _islfs(self.filelog(), self.filenode())
181 181
182 def _updatecatformatter(orig, fm, ctx, matcher, path, decode):
183 orig(fm, ctx, matcher, path, decode)
184 fm.data(rawdata=ctx[path].rawdata())
185
182 186 def convertsink(orig, sink):
183 187 sink = orig(sink)
184 188 if sink.repotype == 'hg':
185 189 class lfssink(sink.__class__):
186 190 def putcommit(self, files, copies, parents, commit, source, revmap,
187 191 full, cleanp2):
188 192 pc = super(lfssink, self).putcommit
189 193 node = pc(files, copies, parents, commit, source, revmap, full,
190 194 cleanp2)
191 195
192 196 if 'lfs' not in self.repo.requirements:
193 197 ctx = self.repo[node]
194 198
195 199 # The file list may contain removed files, so check for
196 200 # membership before assuming it is in the context.
197 201 if any(f in ctx and ctx[f].islfs() for f, n in files):
198 202 self.repo.requirements.add('lfs')
199 203 self.repo._writerequirements()
200 204
201 205 # Permanently enable lfs locally
202 206 self.repo.vfs.append(
203 207 'hgrc', util.tonativeeol('\n[extensions]\nlfs=\n'))
204 208
205 209 return node
206 210
207 211 sink.__class__ = lfssink
208 212
209 213 return sink
210 214
211 215 def vfsinit(orig, self, othervfs):
212 216 orig(self, othervfs)
213 217 # copy lfs related options
214 218 for k, v in othervfs.options.items():
215 219 if k.startswith('lfs'):
216 220 self.options[k] = v
217 221 # also copy lfs blobstores. note: this can run before reposetup, so lfs
218 222 # blobstore attributes are not always ready at this time.
219 223 for name in ['lfslocalblobstore', 'lfsremoteblobstore']:
220 224 if util.safehasattr(othervfs, name):
221 225 setattr(self, name, getattr(othervfs, name))
222 226
223 227 def hgclone(orig, ui, opts, *args, **kwargs):
224 228 result = orig(ui, opts, *args, **kwargs)
225 229
226 230 if result is not None:
227 231 sourcerepo, destrepo = result
228 232 repo = destrepo.local()
229 233
230 234 # When cloning to a remote repo (like through SSH), no repo is available
231 235 # from the peer. Therefore the hgrc can't be updated.
232 236 if not repo:
233 237 return result
234 238
235 239 # If lfs is required for this repo, permanently enable it locally
236 240 if 'lfs' in repo.requirements:
237 241 repo.vfs.append('hgrc',
238 242 util.tonativeeol('\n[extensions]\nlfs=\n'))
239 243
240 244 return result
241 245
242 246 def hgpostshare(orig, sourcerepo, destrepo, bookmarks=True, defaultpath=None):
243 247 orig(sourcerepo, destrepo, bookmarks, defaultpath)
244 248
245 249 # If lfs is required for this repo, permanently enable it locally
246 250 if 'lfs' in destrepo.requirements:
247 251 destrepo.vfs.append('hgrc', util.tonativeeol('\n[extensions]\nlfs=\n'))
248 252
249 253 def _canskipupload(repo):
250 254 # if remotestore is a null store, upload is a no-op and can be skipped
251 255 return isinstance(repo.svfs.lfsremoteblobstore, blobstore._nullremote)
252 256
253 257 def candownload(repo):
254 258 # if remotestore is a null store, downloads will lead to nothing
255 259 return not isinstance(repo.svfs.lfsremoteblobstore, blobstore._nullremote)
256 260
257 261 def uploadblobsfromrevs(repo, revs):
258 262 '''upload lfs blobs introduced by revs
259 263
260 264 Note: also used by other extensions e. g. infinitepush. avoid renaming.
261 265 '''
262 266 if _canskipupload(repo):
263 267 return
264 268 pointers = extractpointers(repo, revs)
265 269 uploadblobs(repo, pointers)
266 270
267 271 def prepush(pushop):
268 272 """Prepush hook.
269 273
270 274 Read through the revisions to push, looking for filelog entries that can be
271 275 deserialized into metadata so that we can block the push on their upload to
272 276 the remote blobstore.
273 277 """
274 278 return uploadblobsfromrevs(pushop.repo, pushop.outgoing.missing)
275 279
276 280 def push(orig, repo, remote, *args, **kwargs):
277 281 """bail on push if the extension isn't enabled on remote when needed"""
278 282 if 'lfs' in repo.requirements:
279 283 # If the remote peer is for a local repo, the requirement tests in the
280 284 # base class method enforce lfs support. Otherwise, some revisions in
281 285 # this repo use lfs, and the remote repo needs the extension loaded.
282 286 if not remote.local() and not remote.capable('lfs'):
283 287 # This is a copy of the message in exchange.push() when requirements
284 288 # are missing between local repos.
285 289 m = _("required features are not supported in the destination: %s")
286 290 raise error.Abort(m % 'lfs',
287 291 hint=_('enable the lfs extension on the server'))
288 292 return orig(repo, remote, *args, **kwargs)
289 293
290 294 def writenewbundle(orig, ui, repo, source, filename, bundletype, outgoing,
291 295 *args, **kwargs):
292 296 """upload LFS blobs added by outgoing revisions on 'hg bundle'"""
293 297 uploadblobsfromrevs(repo, outgoing.missing)
294 298 return orig(ui, repo, source, filename, bundletype, outgoing, *args,
295 299 **kwargs)
296 300
297 301 def extractpointers(repo, revs):
298 302 """return a list of lfs pointers added by given revs"""
299 303 repo.ui.debug('lfs: computing set of blobs to upload\n')
300 304 pointers = {}
301 305 for r in revs:
302 306 ctx = repo[r]
303 307 for p in pointersfromctx(ctx).values():
304 308 pointers[p.oid()] = p
305 309 return sorted(pointers.values())
306 310
307 311 def pointersfromctx(ctx):
308 312 """return a dict {path: pointer} for given single changectx"""
309 313 result = {}
310 314 for f in ctx.files():
311 315 if f not in ctx:
312 316 continue
313 317 fctx = ctx[f]
314 318 if not _islfs(fctx.filelog(), fctx.filenode()):
315 319 continue
316 320 try:
317 321 result[f] = pointer.deserialize(fctx.rawdata())
318 322 except pointer.InvalidPointer as ex:
319 323 raise error.Abort(_('lfs: corrupted pointer (%s@%s): %s\n')
320 324 % (f, short(ctx.node()), ex))
321 325 return result
322 326
323 327 def uploadblobs(repo, pointers):
324 328 """upload given pointers from local blobstore"""
325 329 if not pointers:
326 330 return
327 331
328 332 remoteblob = repo.svfs.lfsremoteblobstore
329 333 remoteblob.writebatch(pointers, repo.svfs.lfslocalblobstore)
330 334
331 335 def upgradefinishdatamigration(orig, ui, srcrepo, dstrepo, requirements):
332 336 orig(ui, srcrepo, dstrepo, requirements)
333 337
334 338 srclfsvfs = srcrepo.svfs.lfslocalblobstore.vfs
335 339 dstlfsvfs = dstrepo.svfs.lfslocalblobstore.vfs
336 340
337 341 for dirpath, dirs, files in srclfsvfs.walk():
338 342 for oid in files:
339 343 ui.write(_('copying lfs blob %s\n') % oid)
340 344 lfutil.link(srclfsvfs.join(oid), dstlfsvfs.join(oid))
341 345
342 346 def upgraderequirements(orig, repo):
343 347 reqs = orig(repo)
344 348 if 'lfs' in repo.requirements:
345 349 reqs.add('lfs')
346 350 return reqs
@@ -1,975 +1,981 b''
1 1 # Initial setup
2 2
3 3 $ cat >> $HGRCPATH << EOF
4 4 > [extensions]
5 5 > lfs=
6 6 > [lfs]
7 7 > # Test deprecated config
8 8 > threshold=1000B
9 9 > EOF
10 10
11 11 $ LONG=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
12 12
13 13 # Prepare server and enable extension
14 14 $ hg init server
15 15 $ hg clone -q server client
16 16 $ cd client
17 17
18 18 # Commit small file
19 19 $ echo s > smallfile
20 20 $ echo '**.py = LF' > .hgeol
21 21 $ hg --config lfs.track='size(">1000B") | "path:.hgeol"' commit -Aqm "add small file"
22 22 $ hg debugdata .hgeol 0
23 23 **.py = LF
24 24
25 25 # Commit large file
26 26 $ echo $LONG > largefile
27 27 $ grep lfs .hg/requires
28 28 [1]
29 29 $ hg commit --traceback -Aqm "add large file"
30 30 $ grep lfs .hg/requires
31 31 lfs
32 32
33 33 # Ensure metadata is stored
34 34 $ hg debugdata largefile 0
35 35 version https://git-lfs.github.com/spec/v1
36 36 oid sha256:f11e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
37 37 size 1501
38 38 x-is-binary 0
39 39
40 40 # Check the blobstore is populated
41 41 $ find .hg/store/lfs/objects | sort
42 42 .hg/store/lfs/objects
43 43 .hg/store/lfs/objects/f1
44 44 .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
45 45
46 46 # Check the blob stored contains the actual contents of the file
47 47 $ cat .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
48 48 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
49 49
50 50 # Push changes to the server
51 51
52 52 $ hg push
53 53 pushing to $TESTTMP/server
54 54 searching for changes
55 55 abort: lfs.url needs to be configured
56 56 [255]
57 57
58 58 $ cat >> $HGRCPATH << EOF
59 59 > [lfs]
60 60 > url=file:$TESTTMP/dummy-remote/
61 61 > EOF
62 62
63 63 Push to a local non-lfs repo with the extension enabled will add the
64 64 lfs requirement
65 65
66 66 $ grep lfs $TESTTMP/server/.hg/requires
67 67 [1]
68 68 $ hg push -v | egrep -v '^(uncompressed| )'
69 69 pushing to $TESTTMP/server
70 70 searching for changes
71 71 lfs: found f11e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b in the local lfs store
72 72 2 changesets found
73 73 adding changesets
74 74 adding manifests
75 75 adding file changes
76 76 added 2 changesets with 3 changes to 3 files
77 77 calling hook pretxnchangegroup.lfs: hgext.lfs.checkrequireslfs
78 78 $ grep lfs $TESTTMP/server/.hg/requires
79 79 lfs
80 80
81 81 # Unknown URL scheme
82 82
83 83 $ hg push --config lfs.url=ftp://foobar
84 84 abort: lfs: unknown url scheme: ftp
85 85 [255]
86 86
87 87 $ cd ../
88 88
89 89 # Initialize new client (not cloning) and setup extension
90 90 $ hg init client2
91 91 $ cd client2
92 92 $ cat >> .hg/hgrc <<EOF
93 93 > [paths]
94 94 > default = $TESTTMP/server
95 95 > EOF
96 96
97 97 # Pull from server
98 98
99 99 Pulling a local lfs repo into a local non-lfs repo with the extension
100 100 enabled adds the lfs requirement
101 101
102 102 $ grep lfs .hg/requires $TESTTMP/server/.hg/requires
103 103 $TESTTMP/server/.hg/requires:lfs
104 104 $ hg pull default
105 105 pulling from $TESTTMP/server
106 106 requesting all changes
107 107 adding changesets
108 108 adding manifests
109 109 adding file changes
110 110 added 2 changesets with 3 changes to 3 files
111 111 new changesets 0ead593177f7:b88141481348
112 112 (run 'hg update' to get a working copy)
113 113 $ grep lfs .hg/requires $TESTTMP/server/.hg/requires
114 114 .hg/requires:lfs
115 115 $TESTTMP/server/.hg/requires:lfs
116 116
117 117 # Check the blobstore is not yet populated
118 118 $ [ -d .hg/store/lfs/objects ]
119 119 [1]
120 120
121 121 # Update to the last revision containing the large file
122 122 $ hg update
123 123 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
124 124
125 125 # Check the blobstore has been populated on update
126 126 $ find .hg/store/lfs/objects | sort
127 127 .hg/store/lfs/objects
128 128 .hg/store/lfs/objects/f1
129 129 .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
130 130
131 131 # Check the contents of the file are fetched from blobstore when requested
132 132 $ hg cat -r . largefile
133 133 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
134 134
135 135 # Check the file has been copied in the working copy
136 136 $ cat largefile
137 137 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
138 138
139 139 $ cd ..
140 140
141 141 # Check rename, and switch between large and small files
142 142
143 143 $ hg init repo3
144 144 $ cd repo3
145 145 $ cat >> .hg/hgrc << EOF
146 146 > [lfs]
147 147 > track=size(">10B")
148 148 > EOF
149 149
150 150 $ echo LONGER-THAN-TEN-BYTES-WILL-TRIGGER-LFS > large
151 151 $ echo SHORTER > small
152 152 $ hg add . -q
153 153 $ hg commit -m 'commit with lfs content'
154 154
155 155 $ hg mv large l
156 156 $ hg mv small s
157 157 $ hg commit -m 'renames'
158 158
159 159 $ echo SHORT > l
160 160 $ echo BECOME-LARGER-FROM-SHORTER > s
161 161 $ hg commit -m 'large to small, small to large'
162 162
163 163 $ echo 1 >> l
164 164 $ echo 2 >> s
165 165 $ hg commit -m 'random modifications'
166 166
167 167 $ echo RESTORE-TO-BE-LARGE > l
168 168 $ echo SHORTER > s
169 169 $ hg commit -m 'switch large and small again'
170 170
171 171 # Test lfs_files template
172 172
173 173 $ hg log -r 'all()' -T '{rev} {join(lfs_files, ", ")}\n'
174 174 0 large
175 175 1 l
176 176 2 s
177 177 3 s
178 178 4 l
179 179
180 180 # Push and pull the above repo
181 181
182 182 $ hg --cwd .. init repo4
183 183 $ hg push ../repo4
184 184 pushing to ../repo4
185 185 searching for changes
186 186 adding changesets
187 187 adding manifests
188 188 adding file changes
189 189 added 5 changesets with 10 changes to 4 files
190 190
191 191 $ hg --cwd .. init repo5
192 192 $ hg --cwd ../repo5 pull ../repo3
193 193 pulling from ../repo3
194 194 requesting all changes
195 195 adding changesets
196 196 adding manifests
197 197 adding file changes
198 198 added 5 changesets with 10 changes to 4 files
199 199 new changesets fd47a419c4f7:5adf850972b9
200 200 (run 'hg update' to get a working copy)
201 201
202 202 $ cd ..
203 203
204 204 # Test clone
205 205
206 206 $ hg init repo6
207 207 $ cd repo6
208 208 $ cat >> .hg/hgrc << EOF
209 209 > [lfs]
210 210 > track=size(">30B")
211 211 > EOF
212 212
213 213 $ echo LARGE-BECAUSE-IT-IS-MORE-THAN-30-BYTES > large
214 214 $ echo SMALL > small
215 215 $ hg commit -Aqm 'create a lfs file' large small
216 216 $ hg debuglfsupload -r 'all()' -v
217 217 lfs: found 8e92251415339ae9b148c8da89ed5ec665905166a1ab11b09dca8fad83344738 in the local lfs store
218 218
219 219 $ cd ..
220 220
221 221 $ hg clone repo6 repo7
222 222 updating to branch default
223 223 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
224 224 $ cd repo7
225 225 $ hg config extensions --debug | grep lfs
226 226 $TESTTMP/repo7/.hg/hgrc:*: extensions.lfs= (glob)
227 227 $ cat large
228 228 LARGE-BECAUSE-IT-IS-MORE-THAN-30-BYTES
229 229 $ cat small
230 230 SMALL
231 231
232 232 $ cd ..
233 233
234 234 $ hg --config extensions.share= share repo7 sharedrepo
235 235 updating working directory
236 236 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
237 237 $ hg -R sharedrepo config extensions --debug | grep lfs
238 238 $TESTTMP/sharedrepo/.hg/hgrc:*: extensions.lfs= (glob)
239 239
240 240 # Test rename and status
241 241
242 242 $ hg init repo8
243 243 $ cd repo8
244 244 $ cat >> .hg/hgrc << EOF
245 245 > [lfs]
246 246 > track=size(">10B")
247 247 > EOF
248 248
249 249 $ echo THIS-IS-LFS-BECAUSE-10-BYTES > a1
250 250 $ echo SMALL > a2
251 251 $ hg commit -m a -A a1 a2
252 252 $ hg status
253 253 $ hg mv a1 b1
254 254 $ hg mv a2 a1
255 255 $ hg mv b1 a2
256 256 $ hg commit -m b
257 257 $ hg status
258 258 >>> with open('a2', 'wb') as f:
259 259 ... f.write(b'\1\nSTART-WITH-HG-FILELOG-METADATA')
260 260 >>> with open('a1', 'wb') as f:
261 261 ... f.write(b'\1\nMETA\n')
262 262 $ hg commit -m meta
263 263 $ hg status
264 264 $ hg log -T '{rev}: {file_copies} | {file_dels} | {file_adds}\n'
265 265 2: | |
266 266 1: a1 (a2)a2 (a1) | |
267 267 0: | | a1 a2
268 268
269 269 $ for n in a1 a2; do
270 270 > for r in 0 1 2; do
271 271 > printf '\n%s @ %s\n' $n $r
272 272 > hg debugdata $n $r
273 273 > done
274 274 > done
275 275
276 276 a1 @ 0
277 277 version https://git-lfs.github.com/spec/v1
278 278 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
279 279 size 29
280 280 x-is-binary 0
281 281
282 282 a1 @ 1
283 283 \x01 (esc)
284 284 copy: a2
285 285 copyrev: 50470ad23cf937b1f4b9f80bfe54df38e65b50d9
286 286 \x01 (esc)
287 287 SMALL
288 288
289 289 a1 @ 2
290 290 \x01 (esc)
291 291 \x01 (esc)
292 292 \x01 (esc)
293 293 META
294 294
295 295 a2 @ 0
296 296 SMALL
297 297
298 298 a2 @ 1
299 299 version https://git-lfs.github.com/spec/v1
300 300 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
301 301 size 29
302 302 x-hg-copy a1
303 303 x-hg-copyrev be23af27908a582af43e5cda209a5a9b319de8d4
304 304 x-is-binary 0
305 305
306 306 a2 @ 2
307 307 version https://git-lfs.github.com/spec/v1
308 308 oid sha256:876dadc86a8542f9798048f2c47f51dbf8e4359aed883e8ec80c5db825f0d943
309 309 size 32
310 310 x-is-binary 0
311 311
312 312 # Verify commit hashes include rename metadata
313 313
314 314 $ hg log -T '{rev}:{node|short} {desc}\n'
315 315 2:0fae949de7fa meta
316 316 1:9cd6bdffdac0 b
317 317 0:7f96794915f7 a
318 318
319 319 $ cd ..
320 320
321 321 # Test bundle
322 322
323 323 $ hg init repo9
324 324 $ cd repo9
325 325 $ cat >> .hg/hgrc << EOF
326 326 > [lfs]
327 327 > track=size(">10B")
328 328 > [diff]
329 329 > git=1
330 330 > EOF
331 331
332 332 $ for i in 0 single two three 4; do
333 333 > echo 'THIS-IS-LFS-'$i > a
334 334 > hg commit -m a-$i -A a
335 335 > done
336 336
337 337 $ hg update 2 -q
338 338 $ echo 'THIS-IS-LFS-2-CHILD' > a
339 339 $ hg commit -m branching -q
340 340
341 341 $ hg bundle --base 1 bundle.hg -v
342 342 lfs: found 5ab7a3739a5feec94a562d070a14f36dba7cad17e5484a4a89eea8e5f3166888 in the local lfs store
343 343 lfs: found a9c7d1cd6ce2b9bbdf46ed9a862845228717b921c089d0d42e3bcaed29eb612e in the local lfs store
344 344 lfs: found f693890c49c409ec33673b71e53f297681f76c1166daf33b2ad7ebf8b1d3237e in the local lfs store
345 345 lfs: found fda198fea753eb66a252e9856915e1f5cddbe41723bd4b695ece2604ad3c9f75 in the local lfs store
346 346 4 changesets found
347 347 uncompressed size of bundle content:
348 348 * (changelog) (glob)
349 349 * (manifests) (glob)
350 350 * a (glob)
351 351 $ hg --config extensions.strip= strip -r 2 --no-backup --force -q
352 352 $ hg -R bundle.hg log -p -T '{rev} {desc}\n' a
353 353 5 branching
354 354 diff --git a/a b/a
355 355 --- a/a
356 356 +++ b/a
357 357 @@ -1,1 +1,1 @@
358 358 -THIS-IS-LFS-two
359 359 +THIS-IS-LFS-2-CHILD
360 360
361 361 4 a-4
362 362 diff --git a/a b/a
363 363 --- a/a
364 364 +++ b/a
365 365 @@ -1,1 +1,1 @@
366 366 -THIS-IS-LFS-three
367 367 +THIS-IS-LFS-4
368 368
369 369 3 a-three
370 370 diff --git a/a b/a
371 371 --- a/a
372 372 +++ b/a
373 373 @@ -1,1 +1,1 @@
374 374 -THIS-IS-LFS-two
375 375 +THIS-IS-LFS-three
376 376
377 377 2 a-two
378 378 diff --git a/a b/a
379 379 --- a/a
380 380 +++ b/a
381 381 @@ -1,1 +1,1 @@
382 382 -THIS-IS-LFS-single
383 383 +THIS-IS-LFS-two
384 384
385 385 1 a-single
386 386 diff --git a/a b/a
387 387 --- a/a
388 388 +++ b/a
389 389 @@ -1,1 +1,1 @@
390 390 -THIS-IS-LFS-0
391 391 +THIS-IS-LFS-single
392 392
393 393 0 a-0
394 394 diff --git a/a b/a
395 395 new file mode 100644
396 396 --- /dev/null
397 397 +++ b/a
398 398 @@ -0,0 +1,1 @@
399 399 +THIS-IS-LFS-0
400 400
401 401 $ hg bundle -R bundle.hg --base 1 bundle-again.hg -q
402 402 $ hg -R bundle-again.hg log -p -T '{rev} {desc}\n' a
403 403 5 branching
404 404 diff --git a/a b/a
405 405 --- a/a
406 406 +++ b/a
407 407 @@ -1,1 +1,1 @@
408 408 -THIS-IS-LFS-two
409 409 +THIS-IS-LFS-2-CHILD
410 410
411 411 4 a-4
412 412 diff --git a/a b/a
413 413 --- a/a
414 414 +++ b/a
415 415 @@ -1,1 +1,1 @@
416 416 -THIS-IS-LFS-three
417 417 +THIS-IS-LFS-4
418 418
419 419 3 a-three
420 420 diff --git a/a b/a
421 421 --- a/a
422 422 +++ b/a
423 423 @@ -1,1 +1,1 @@
424 424 -THIS-IS-LFS-two
425 425 +THIS-IS-LFS-three
426 426
427 427 2 a-two
428 428 diff --git a/a b/a
429 429 --- a/a
430 430 +++ b/a
431 431 @@ -1,1 +1,1 @@
432 432 -THIS-IS-LFS-single
433 433 +THIS-IS-LFS-two
434 434
435 435 1 a-single
436 436 diff --git a/a b/a
437 437 --- a/a
438 438 +++ b/a
439 439 @@ -1,1 +1,1 @@
440 440 -THIS-IS-LFS-0
441 441 +THIS-IS-LFS-single
442 442
443 443 0 a-0
444 444 diff --git a/a b/a
445 445 new file mode 100644
446 446 --- /dev/null
447 447 +++ b/a
448 448 @@ -0,0 +1,1 @@
449 449 +THIS-IS-LFS-0
450 450
451 451 $ cd ..
452 452
453 453 # Test isbinary
454 454
455 455 $ hg init repo10
456 456 $ cd repo10
457 457 $ cat >> .hg/hgrc << EOF
458 458 > [extensions]
459 459 > lfs=
460 460 > [lfs]
461 461 > track=all()
462 462 > EOF
463 463 $ $PYTHON <<'EOF'
464 464 > def write(path, content):
465 465 > with open(path, 'wb') as f:
466 466 > f.write(content)
467 467 > write('a', b'\0\0')
468 468 > write('b', b'\1\n')
469 469 > write('c', b'\1\n\0')
470 470 > write('d', b'xx')
471 471 > EOF
472 472 $ hg add a b c d
473 473 $ hg diff --stat
474 474 a | Bin
475 475 b | 1 +
476 476 c | Bin
477 477 d | 1 +
478 478 4 files changed, 2 insertions(+), 0 deletions(-)
479 479 $ hg commit -m binarytest
480 480 $ cat > $TESTTMP/dumpbinary.py << EOF
481 481 > def reposetup(ui, repo):
482 482 > for n in 'abcd':
483 483 > ui.write(('%s: binary=%s\n') % (n, repo['.'][n].isbinary()))
484 484 > EOF
485 485 $ hg --config extensions.dumpbinary=$TESTTMP/dumpbinary.py id --trace
486 486 a: binary=True
487 487 b: binary=False
488 488 c: binary=True
489 489 d: binary=False
490 490 b55353847f02 tip
491 491
492 492 $ cd ..
493 493
494 494 # Test fctx.cmp fastpath - diff without LFS blobs
495 495
496 496 $ hg init repo12
497 497 $ cd repo12
498 498 $ cat >> .hg/hgrc <<EOF
499 499 > [lfs]
500 500 > threshold=1
501 501 > EOF
502 502 $ cat > ../patch.diff <<EOF
503 503 > # HG changeset patch
504 504 > 2
505 505 >
506 506 > diff --git a/a b/a
507 507 > old mode 100644
508 508 > new mode 100755
509 509 > EOF
510 510
511 511 $ for i in 1 2 3; do
512 512 > cp ../repo10/a a
513 513 > if [ $i = 3 ]; then
514 514 > # make a content-only change
515 515 > hg import -q --bypass ../patch.diff
516 516 > hg update -q
517 517 > rm ../patch.diff
518 518 > else
519 519 > echo $i >> a
520 520 > hg commit -m $i -A a
521 521 > fi
522 522 > done
523 523 $ [ -d .hg/store/lfs/objects ]
524 524
525 525 $ cd ..
526 526
527 527 $ hg clone repo12 repo13 --noupdate
528 528 $ cd repo13
529 529 $ hg log --removed -p a -T '{desc}\n' --config diff.nobinary=1 --git
530 530 2
531 531 diff --git a/a b/a
532 532 old mode 100644
533 533 new mode 100755
534 534
535 535 2
536 536 diff --git a/a b/a
537 537 Binary file a has changed
538 538
539 539 1
540 540 diff --git a/a b/a
541 541 new file mode 100644
542 542 Binary file a has changed
543 543
544 544 $ [ -d .hg/store/lfs/objects ]
545 545 [1]
546 546
547 547 $ cd ..
548 548
549 549 # Test filter
550 550
551 551 $ hg init repo11
552 552 $ cd repo11
553 553 $ cat >> .hg/hgrc << EOF
554 554 > [lfs]
555 555 > track=(**.a & size(">5B")) | (**.b & !size(">5B"))
556 556 > | (**.c & "path:d" & !"path:d/c.c") | size(">10B")
557 557 > EOF
558 558
559 559 $ mkdir a
560 560 $ echo aaaaaa > a/1.a
561 561 $ echo a > a/2.a
562 562 $ echo aaaaaa > 1.b
563 563 $ echo a > 2.b
564 564 $ echo a > 1.c
565 565 $ mkdir d
566 566 $ echo a > d/c.c
567 567 $ echo a > d/d.c
568 568 $ echo aaaaaaaaaaaa > x
569 569 $ hg add . -q
570 570 $ hg commit -m files
571 571
572 572 $ for p in a/1.a a/2.a 1.b 2.b 1.c d/c.c d/d.c x; do
573 573 > if hg debugdata $p 0 2>&1 | grep git-lfs >/dev/null; then
574 574 > echo "${p}: is lfs"
575 575 > else
576 576 > echo "${p}: not lfs"
577 577 > fi
578 578 > done
579 579 a/1.a: is lfs
580 580 a/2.a: not lfs
581 581 1.b: not lfs
582 582 2.b: is lfs
583 583 1.c: not lfs
584 584 d/c.c: not lfs
585 585 d/d.c: is lfs
586 586 x: is lfs
587 587
588 588 $ cd ..
589 589
590 590 # Verify the repos
591 591
592 592 $ cat > $TESTTMP/dumpflog.py << EOF
593 593 > # print raw revision sizes, flags, and hashes for certain files
594 594 > import hashlib
595 595 > from mercurial import revlog
596 596 > from mercurial.node import short
597 597 > def hash(rawtext):
598 598 > h = hashlib.sha512()
599 599 > h.update(rawtext)
600 600 > return h.hexdigest()[:4]
601 601 > def reposetup(ui, repo):
602 602 > # these 2 files are interesting
603 603 > for name in ['l', 's']:
604 604 > fl = repo.file(name)
605 605 > if len(fl) == 0:
606 606 > continue
607 607 > sizes = [revlog.revlog.rawsize(fl, i) for i in fl]
608 608 > texts = [fl.revision(i, raw=True) for i in fl]
609 609 > flags = [int(fl.flags(i)) for i in fl]
610 610 > hashes = [hash(t) for t in texts]
611 611 > print(' %s: rawsizes=%r flags=%r hashes=%r'
612 612 > % (name, sizes, flags, hashes))
613 613 > EOF
614 614
615 615 $ for i in client client2 server repo3 repo4 repo5 repo6 repo7 repo8 repo9 \
616 616 > repo10; do
617 617 > echo 'repo:' $i
618 618 > hg --cwd $i verify --config extensions.dumpflog=$TESTTMP/dumpflog.py -q
619 619 > done
620 620 repo: client
621 621 repo: client2
622 622 repo: server
623 623 repo: repo3
624 624 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
625 625 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
626 626 repo: repo4
627 627 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
628 628 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
629 629 repo: repo5
630 630 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
631 631 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
632 632 repo: repo6
633 633 repo: repo7
634 634 repo: repo8
635 635 repo: repo9
636 636 repo: repo10
637 637
638 638 repo13 doesn't have any cached lfs files and its source never pushed its
639 639 files. Therefore, the files don't exist in the remote store. Use the files in
640 640 the user cache.
641 641
642 642 $ test -d $TESTTMP/repo13/.hg/store/lfs/objects
643 643 [1]
644 644
645 645 $ hg --config extensions.share= share repo13 repo14
646 646 updating working directory
647 647 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
648 648 $ hg -R repo14 -q verify
649 649
650 650 $ hg clone repo13 repo15
651 651 updating to branch default
652 652 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
653 653 $ hg -R repo15 -q verify
654 654
655 655 If the source repo doesn't have the blob (maybe it was pulled or cloned with
656 656 --noupdate), the blob is still accessible via the global cache to send to the
657 657 remote store.
658 658
659 659 $ rm -rf $TESTTMP/repo15/.hg/store/lfs
660 660 $ hg init repo16
661 661 $ hg -R repo15 push repo16
662 662 pushing to repo16
663 663 searching for changes
664 664 adding changesets
665 665 adding manifests
666 666 adding file changes
667 667 added 3 changesets with 2 changes to 1 files
668 668 $ hg -R repo15 -q verify
669 669
670 670 Test damaged file scenarios. (This also damages the usercache because of the
671 671 hardlinks.)
672 672
673 673 $ echo 'damage' >> repo5/.hg/store/lfs/objects/66/100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
674 674
675 675 Repo with damaged lfs objects in any revision will fail verification.
676 676
677 677 $ hg -R repo5 verify
678 678 checking changesets
679 679 checking manifests
680 680 crosschecking files in changesets and manifests
681 681 checking files
682 682 l@1: unpacking 46a2f24864bc: integrity check failed on data/l.i:0
683 683 large@0: unpacking 2c531e0992ff: integrity check failed on data/large.i:0
684 684 4 files, 5 changesets, 10 total revisions
685 685 2 integrity errors encountered!
686 686 (first damaged changeset appears to be 0)
687 687 [1]
688 688
689 689 Updates work after cloning a damaged repo, if the damaged lfs objects aren't in
690 690 the update destination. Those objects won't be added to the new repo's store
691 691 because they aren't accessed.
692 692
693 693 $ hg clone -v repo5 fromcorrupt
694 694 updating to branch default
695 695 resolving manifests
696 696 getting l
697 697 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the usercache
698 698 getting s
699 699 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
700 700 $ test -f fromcorrupt/.hg/store/lfs/objects/66/100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
701 701 [1]
702 702
703 703 Verify will copy/link all lfs objects into the local store that aren't already
704 704 present. Bypass the corrupted usercache to show that verify works when fed by
705 705 the (uncorrupted) remote store.
706 706
707 707 $ hg -R fromcorrupt --config lfs.usercache=emptycache verify -v
708 708 repository uses revlog format 1
709 709 checking changesets
710 710 checking manifests
711 711 crosschecking files in changesets and manifests
712 712 checking files
713 713 lfs: adding 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e to the usercache
714 714 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the local lfs store
715 715 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
716 716 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the local lfs store
717 717 lfs: adding 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 to the usercache
718 718 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
719 719 lfs: adding b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c to the usercache
720 720 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
721 721 4 files, 5 changesets, 10 total revisions
722 722
723 723 Verify will not copy/link a corrupted file from the usercache into the local
724 724 store, and poison it. (The verify with a good remote now works.)
725 725
726 726 $ rm -r fromcorrupt/.hg/store/lfs/objects/66/100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
727 727 $ hg -R fromcorrupt verify -v
728 728 repository uses revlog format 1
729 729 checking changesets
730 730 checking manifests
731 731 crosschecking files in changesets and manifests
732 732 checking files
733 733 l@1: unpacking 46a2f24864bc: integrity check failed on data/l.i:0
734 734 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
735 735 large@0: unpacking 2c531e0992ff: integrity check failed on data/large.i:0
736 736 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
737 737 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
738 738 4 files, 5 changesets, 10 total revisions
739 739 2 integrity errors encountered!
740 740 (first damaged changeset appears to be 0)
741 741 [1]
742 742 $ hg -R fromcorrupt --config lfs.usercache=emptycache verify -v
743 743 repository uses revlog format 1
744 744 checking changesets
745 745 checking manifests
746 746 crosschecking files in changesets and manifests
747 747 checking files
748 748 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the usercache
749 749 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
750 750 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the local lfs store
751 751 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
752 752 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
753 753 4 files, 5 changesets, 10 total revisions
754 754
755 755 Damaging a file required by the update destination fails the update.
756 756
757 757 $ echo 'damage' >> $TESTTMP/dummy-remote/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
758 758 $ hg --config lfs.usercache=emptycache clone -v repo5 fromcorrupt2
759 759 updating to branch default
760 760 resolving manifests
761 761 getting l
762 762 abort: corrupt remote lfs object: 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
763 763 [255]
764 764
765 765 A corrupted lfs blob is not transferred from a file://remotestore to the
766 766 usercache or local store.
767 767
768 768 $ test -f emptycache/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
769 769 [1]
770 770 $ test -f fromcorrupt2/.hg/store/lfs/objects/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
771 771 [1]
772 772
773 773 $ hg -R fromcorrupt2 verify
774 774 checking changesets
775 775 checking manifests
776 776 crosschecking files in changesets and manifests
777 777 checking files
778 778 l@1: unpacking 46a2f24864bc: integrity check failed on data/l.i:0
779 779 large@0: unpacking 2c531e0992ff: integrity check failed on data/large.i:0
780 780 4 files, 5 changesets, 10 total revisions
781 781 2 integrity errors encountered!
782 782 (first damaged changeset appears to be 0)
783 783 [1]
784 784
785 785 Corrupt local files are not sent upstream. (The alternate dummy remote
786 786 avoids the corrupt lfs object in the original remote.)
787 787
788 788 $ mkdir $TESTTMP/dummy-remote2
789 789 $ hg init dest
790 790 $ hg -R fromcorrupt2 --config lfs.url=file:///$TESTTMP/dummy-remote2 push -v dest
791 791 pushing to dest
792 792 searching for changes
793 793 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
794 794 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
795 795 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
796 796 abort: detected corrupt lfs object: 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
797 797 (run hg verify)
798 798 [255]
799 799
800 800 $ hg -R fromcorrupt2 --config lfs.url=file:///$TESTTMP/dummy-remote2 verify -v
801 801 repository uses revlog format 1
802 802 checking changesets
803 803 checking manifests
804 804 crosschecking files in changesets and manifests
805 805 checking files
806 806 l@1: unpacking 46a2f24864bc: integrity check failed on data/l.i:0
807 807 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
808 808 large@0: unpacking 2c531e0992ff: integrity check failed on data/large.i:0
809 809 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
810 810 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
811 811 4 files, 5 changesets, 10 total revisions
812 812 2 integrity errors encountered!
813 813 (first damaged changeset appears to be 0)
814 814 [1]
815 815
816 816 $ cat $TESTTMP/dummy-remote2/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b | $TESTDIR/f --sha256
817 817 sha256=22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
818 818 $ cat fromcorrupt2/.hg/store/lfs/objects/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b | $TESTDIR/f --sha256
819 819 sha256=22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
820 820 $ test -f $TESTTMP/dummy-remote2/66/100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
821 821 [1]
822 822
823 823 Accessing a corrupt file will complain
824 824
825 825 $ hg --cwd fromcorrupt2 cat -r 0 large
826 826 abort: integrity check failed on data/large.i:0!
827 827 [255]
828 828
829 829 lfs -> normal -> lfs round trip conversions are possible. The 'none()'
830 830 predicate on the command line will override whatever is configured globally and
831 831 locally, and ensures everything converts to a regular file. For lfs -> normal,
832 832 there's no 'lfs' destination repo requirement. For normal -> lfs, there is.
833 833
834 834 $ hg --config extensions.convert= --config 'lfs.track=none()' \
835 835 > convert repo8 convert_normal
836 836 initializing destination convert_normal repository
837 837 scanning source...
838 838 sorting...
839 839 converting...
840 840 2 a
841 841 1 b
842 842 0 meta
843 843 $ grep 'lfs' convert_normal/.hg/requires
844 844 [1]
845 $ hg --cwd convert_normal debugdata a1 0
845 $ hg --cwd convert_normal cat a1 -r 0 -T '{rawdata}'
846 846 THIS-IS-LFS-BECAUSE-10-BYTES
847 847
848 848 $ hg --config extensions.convert= --config lfs.threshold=10B \
849 849 > convert convert_normal convert_lfs
850 850 initializing destination convert_lfs repository
851 851 scanning source...
852 852 sorting...
853 853 converting...
854 854 2 a
855 855 1 b
856 856 0 meta
857
858 $ hg --cwd convert_lfs cat -r 0 a1 -T '{rawdata}'
859 version https://git-lfs.github.com/spec/v1
860 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
861 size 29
862 x-is-binary 0
857 863 $ hg --cwd convert_lfs debugdata a1 0
858 864 version https://git-lfs.github.com/spec/v1
859 865 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
860 866 size 29
861 867 x-is-binary 0
862 868 $ hg --cwd convert_lfs \
863 869 > log -r 'all()' -T '{rev}: {lfs_files % "{file}: {oid}\n"}'
864 870 0: a1: 5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
865 871 1: a2: 5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
866 872 2: a2: 876dadc86a8542f9798048f2c47f51dbf8e4359aed883e8ec80c5db825f0d943
867 873
868 874 $ grep 'lfs' convert_lfs/.hg/requires
869 875 lfs
870 876
871 877 The hashes in all stages of the conversion are unchanged.
872 878
873 879 $ hg -R repo8 log -T '{node|short}\n'
874 880 0fae949de7fa
875 881 9cd6bdffdac0
876 882 7f96794915f7
877 883 $ hg -R convert_normal log -T '{node|short}\n'
878 884 0fae949de7fa
879 885 9cd6bdffdac0
880 886 7f96794915f7
881 887 $ hg -R convert_lfs log -T '{node|short}\n'
882 888 0fae949de7fa
883 889 9cd6bdffdac0
884 890 7f96794915f7
885 891
886 892 This convert is trickier, because it contains deleted files (via `hg mv`)
887 893
888 894 $ hg --config extensions.convert= --config lfs.threshold=1000M \
889 895 > convert repo3 convert_normal2
890 896 initializing destination convert_normal2 repository
891 897 scanning source...
892 898 sorting...
893 899 converting...
894 900 4 commit with lfs content
895 901 3 renames
896 902 2 large to small, small to large
897 903 1 random modifications
898 904 0 switch large and small again
899 905 $ grep 'lfs' convert_normal2/.hg/requires
900 906 [1]
901 907 $ hg --cwd convert_normal2 debugdata large 0
902 908 LONGER-THAN-TEN-BYTES-WILL-TRIGGER-LFS
903 909
904 910 $ hg --config extensions.convert= --config lfs.threshold=10B \
905 911 > convert convert_normal2 convert_lfs2
906 912 initializing destination convert_lfs2 repository
907 913 scanning source...
908 914 sorting...
909 915 converting...
910 916 4 commit with lfs content
911 917 3 renames
912 918 2 large to small, small to large
913 919 1 random modifications
914 920 0 switch large and small again
915 921 $ grep 'lfs' convert_lfs2/.hg/requires
916 922 lfs
917 923 $ hg --cwd convert_lfs2 debugdata large 0
918 924 version https://git-lfs.github.com/spec/v1
919 925 oid sha256:66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
920 926 size 39
921 927 x-is-binary 0
922 928
923 929 $ hg -R convert_lfs2 config --debug extensions | grep lfs
924 930 $TESTTMP/convert_lfs2/.hg/hgrc:*: extensions.lfs= (glob)
925 931
926 932 Committing deleted files works:
927 933
928 934 $ hg init $TESTTMP/repo-del
929 935 $ cd $TESTTMP/repo-del
930 936 $ echo 1 > A
931 937 $ hg commit -m 'add A' -A A
932 938 $ hg rm A
933 939 $ hg commit -m 'rm A'
934 940 $ cd ..
935 941
936 942 Unbundling adds a requirement to a non-lfs repo, if necessary.
937 943
938 944 $ hg bundle -R $TESTTMP/repo-del -qr 0 --base null nolfs.hg
939 945 $ hg bundle -R convert_lfs2 -qr tip --base null lfs.hg
940 946 $ hg init unbundle
941 947 $ hg pull -R unbundle -q nolfs.hg
942 948 $ grep lfs unbundle/.hg/requires
943 949 [1]
944 950 $ hg pull -R unbundle -q lfs.hg
945 951 $ grep lfs unbundle/.hg/requires
946 952 lfs
947 953
948 954 $ hg init no_lfs
949 955 $ cat >> no_lfs/.hg/hgrc <<EOF
950 956 > [experimental]
951 957 > changegroup3 = True
952 958 > [extensions]
953 959 > lfs=!
954 960 > EOF
955 961 $ cp -R no_lfs no_lfs2
956 962
957 963 Pushing from a local lfs repo to a local repo without an lfs requirement and
958 964 with lfs disabled, fails.
959 965
960 966 $ hg push -R convert_lfs2 no_lfs
961 967 pushing to no_lfs
962 968 abort: required features are not supported in the destination: lfs
963 969 [255]
964 970 $ grep lfs no_lfs/.hg/requires
965 971 [1]
966 972
967 973 Pulling from a local lfs repo to a local repo without an lfs requirement and
968 974 with lfs disabled, fails.
969 975
970 976 $ hg pull -R no_lfs2 convert_lfs2
971 977 pulling from convert_lfs2
972 978 abort: required features are not supported in the destination: lfs
973 979 [255]
974 980 $ grep lfs no_lfs2/.hg/requires
975 981 [1]
General Comments 0
You need to be logged in to leave comments. Login now