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