##// END OF EJS Templates
lfs: only hardlink between the usercache and local store if the blob verifies...
Matt Harbison -
r35493:bb6a80fc @10 default
parent child Browse files
Show More
@@ -1,428 +1,436 b''
1 # blobstore.py - local and remote (speaking Git-LFS protocol) blob storages
1 # blobstore.py - local and remote (speaking Git-LFS protocol) blob storages
2 #
2 #
3 # Copyright 2017 Facebook, Inc.
3 # Copyright 2017 Facebook, Inc.
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import hashlib
10 import hashlib
11 import json
11 import json
12 import os
12 import os
13 import re
13 import re
14 import socket
14 import socket
15
15
16 from mercurial.i18n import _
16 from mercurial.i18n import _
17
17
18 from mercurial import (
18 from mercurial import (
19 error,
19 error,
20 pathutil,
20 pathutil,
21 url as urlmod,
21 url as urlmod,
22 util,
22 util,
23 vfs as vfsmod,
23 vfs as vfsmod,
24 worker,
24 worker,
25 )
25 )
26
26
27 from ..largefiles import lfutil
27 from ..largefiles import lfutil
28
28
29 # 64 bytes for SHA256
29 # 64 bytes for SHA256
30 _lfsre = re.compile(r'\A[a-f0-9]{64}\Z')
30 _lfsre = re.compile(r'\A[a-f0-9]{64}\Z')
31
31
32 class lfsvfs(vfsmod.vfs):
32 class lfsvfs(vfsmod.vfs):
33 def join(self, path):
33 def join(self, path):
34 """split the path at first two characters, like: XX/XXXXX..."""
34 """split the path at first two characters, like: XX/XXXXX..."""
35 if not _lfsre.match(path):
35 if not _lfsre.match(path):
36 raise error.ProgrammingError('unexpected lfs path: %s' % path)
36 raise error.ProgrammingError('unexpected lfs path: %s' % path)
37 return super(lfsvfs, self).join(path[0:2], path[2:])
37 return super(lfsvfs, self).join(path[0:2], path[2:])
38
38
39 def walk(self, path=None, onerror=None):
39 def walk(self, path=None, onerror=None):
40 """Yield (dirpath, [], oids) tuple for blobs under path
40 """Yield (dirpath, [], oids) tuple for blobs under path
41
41
42 Oids only exist in the root of this vfs, so dirpath is always ''.
42 Oids only exist in the root of this vfs, so dirpath is always ''.
43 """
43 """
44 root = os.path.normpath(self.base)
44 root = os.path.normpath(self.base)
45 # when dirpath == root, dirpath[prefixlen:] becomes empty
45 # when dirpath == root, dirpath[prefixlen:] becomes empty
46 # because len(dirpath) < prefixlen.
46 # because len(dirpath) < prefixlen.
47 prefixlen = len(pathutil.normasprefix(root))
47 prefixlen = len(pathutil.normasprefix(root))
48 oids = []
48 oids = []
49
49
50 for dirpath, dirs, files in os.walk(self.reljoin(self.base, path or ''),
50 for dirpath, dirs, files in os.walk(self.reljoin(self.base, path or ''),
51 onerror=onerror):
51 onerror=onerror):
52 dirpath = dirpath[prefixlen:]
52 dirpath = dirpath[prefixlen:]
53
53
54 # Silently skip unexpected files and directories
54 # Silently skip unexpected files and directories
55 if len(dirpath) == 2:
55 if len(dirpath) == 2:
56 oids.extend([dirpath + f for f in files
56 oids.extend([dirpath + f for f in files
57 if _lfsre.match(dirpath + f)])
57 if _lfsre.match(dirpath + f)])
58
58
59 yield ('', [], oids)
59 yield ('', [], oids)
60
60
61 class filewithprogress(object):
61 class filewithprogress(object):
62 """a file-like object that supports __len__ and read.
62 """a file-like object that supports __len__ and read.
63
63
64 Useful to provide progress information for how many bytes are read.
64 Useful to provide progress information for how many bytes are read.
65 """
65 """
66
66
67 def __init__(self, fp, callback):
67 def __init__(self, fp, callback):
68 self._fp = fp
68 self._fp = fp
69 self._callback = callback # func(readsize)
69 self._callback = callback # func(readsize)
70 fp.seek(0, os.SEEK_END)
70 fp.seek(0, os.SEEK_END)
71 self._len = fp.tell()
71 self._len = fp.tell()
72 fp.seek(0)
72 fp.seek(0)
73
73
74 def __len__(self):
74 def __len__(self):
75 return self._len
75 return self._len
76
76
77 def read(self, size):
77 def read(self, size):
78 if self._fp is None:
78 if self._fp is None:
79 return b''
79 return b''
80 data = self._fp.read(size)
80 data = self._fp.read(size)
81 if data:
81 if data:
82 if self._callback:
82 if self._callback:
83 self._callback(len(data))
83 self._callback(len(data))
84 else:
84 else:
85 self._fp.close()
85 self._fp.close()
86 self._fp = None
86 self._fp = None
87 return data
87 return data
88
88
89 class local(object):
89 class local(object):
90 """Local blobstore for large file contents.
90 """Local blobstore for large file contents.
91
91
92 This blobstore is used both as a cache and as a staging area for large blobs
92 This blobstore is used both as a cache and as a staging area for large blobs
93 to be uploaded to the remote blobstore.
93 to be uploaded to the remote blobstore.
94 """
94 """
95
95
96 def __init__(self, repo):
96 def __init__(self, repo):
97 fullpath = repo.svfs.join('lfs/objects')
97 fullpath = repo.svfs.join('lfs/objects')
98 self.vfs = lfsvfs(fullpath)
98 self.vfs = lfsvfs(fullpath)
99 usercache = lfutil._usercachedir(repo.ui, 'lfs')
99 usercache = lfutil._usercachedir(repo.ui, 'lfs')
100 self.cachevfs = lfsvfs(usercache)
100 self.cachevfs = lfsvfs(usercache)
101 self.ui = repo.ui
101 self.ui = repo.ui
102
102
103 def write(self, oid, data, verify=True):
103 def write(self, oid, data, verify=True):
104 """Write blob to local blobstore."""
104 """Write blob to local blobstore."""
105 if verify:
105 if verify:
106 _verify(oid, data)
106 _verify(oid, data)
107
107
108 with self.vfs(oid, 'wb', atomictemp=True) as fp:
108 with self.vfs(oid, 'wb', atomictemp=True) as fp:
109 fp.write(data)
109 fp.write(data)
110
110
111 # XXX: should we verify the content of the cache, and hardlink back to
111 # XXX: should we verify the content of the cache, and hardlink back to
112 # the local store on success, but truncate, write and link on failure?
112 # the local store on success, but truncate, write and link on failure?
113 if not self.cachevfs.exists(oid):
113 if not self.cachevfs.exists(oid):
114 self.ui.note(_('lfs: adding %s to the usercache\n') % oid)
114 if verify or hashlib.sha256(data).hexdigest() == oid:
115 lfutil.link(self.vfs.join(oid), self.cachevfs.join(oid))
115 self.ui.note(_('lfs: adding %s to the usercache\n') % oid)
116 lfutil.link(self.vfs.join(oid), self.cachevfs.join(oid))
116
117
117 def read(self, oid, verify=True):
118 def read(self, oid, verify=True):
118 """Read blob from local blobstore."""
119 """Read blob from local blobstore."""
119 if not self.vfs.exists(oid):
120 if not self.vfs.exists(oid):
120 blob = self._read(self.cachevfs, oid, verify)
121 blob = self._read(self.cachevfs, oid, verify)
121 self.ui.note(_('lfs: found %s in the usercache\n') % oid)
122
122 lfutil.link(self.cachevfs.join(oid), self.vfs.join(oid))
123 # Even if revlog will verify the content, it needs to be verified
124 # now before making the hardlink to avoid propagating corrupt blobs.
125 # Don't abort if corruption is detected, because `hg verify` will
126 # give more useful info about the corruption- simply don't add the
127 # hardlink.
128 if verify or hashlib.sha256(blob).hexdigest() == oid:
129 self.ui.note(_('lfs: found %s in the usercache\n') % oid)
130 lfutil.link(self.cachevfs.join(oid), self.vfs.join(oid))
123 else:
131 else:
124 self.ui.note(_('lfs: found %s in the local lfs store\n') % oid)
132 self.ui.note(_('lfs: found %s in the local lfs store\n') % oid)
125 blob = self._read(self.vfs, oid, verify)
133 blob = self._read(self.vfs, oid, verify)
126 return blob
134 return blob
127
135
128 def _read(self, vfs, oid, verify):
136 def _read(self, vfs, oid, verify):
129 """Read blob (after verifying) from the given store"""
137 """Read blob (after verifying) from the given store"""
130 blob = vfs.read(oid)
138 blob = vfs.read(oid)
131 if verify:
139 if verify:
132 _verify(oid, blob)
140 _verify(oid, blob)
133 return blob
141 return blob
134
142
135 def has(self, oid):
143 def has(self, oid):
136 """Returns True if the local blobstore contains the requested blob,
144 """Returns True if the local blobstore contains the requested blob,
137 False otherwise."""
145 False otherwise."""
138 return self.cachevfs.exists(oid) or self.vfs.exists(oid)
146 return self.cachevfs.exists(oid) or self.vfs.exists(oid)
139
147
140 class _gitlfsremote(object):
148 class _gitlfsremote(object):
141
149
142 def __init__(self, repo, url):
150 def __init__(self, repo, url):
143 ui = repo.ui
151 ui = repo.ui
144 self.ui = ui
152 self.ui = ui
145 baseurl, authinfo = url.authinfo()
153 baseurl, authinfo = url.authinfo()
146 self.baseurl = baseurl.rstrip('/')
154 self.baseurl = baseurl.rstrip('/')
147 useragent = repo.ui.config('experimental', 'lfs.user-agent')
155 useragent = repo.ui.config('experimental', 'lfs.user-agent')
148 if not useragent:
156 if not useragent:
149 useragent = 'mercurial/%s git/2.15.1' % util.version()
157 useragent = 'mercurial/%s git/2.15.1' % util.version()
150 self.urlopener = urlmod.opener(ui, authinfo, useragent)
158 self.urlopener = urlmod.opener(ui, authinfo, useragent)
151 self.retry = ui.configint('lfs', 'retry')
159 self.retry = ui.configint('lfs', 'retry')
152
160
153 def writebatch(self, pointers, fromstore):
161 def writebatch(self, pointers, fromstore):
154 """Batch upload from local to remote blobstore."""
162 """Batch upload from local to remote blobstore."""
155 self._batch(pointers, fromstore, 'upload')
163 self._batch(pointers, fromstore, 'upload')
156
164
157 def readbatch(self, pointers, tostore):
165 def readbatch(self, pointers, tostore):
158 """Batch download from remote to local blostore."""
166 """Batch download from remote to local blostore."""
159 self._batch(pointers, tostore, 'download')
167 self._batch(pointers, tostore, 'download')
160
168
161 def _batchrequest(self, pointers, action):
169 def _batchrequest(self, pointers, action):
162 """Get metadata about objects pointed by pointers for given action
170 """Get metadata about objects pointed by pointers for given action
163
171
164 Return decoded JSON object like {'objects': [{'oid': '', 'size': 1}]}
172 Return decoded JSON object like {'objects': [{'oid': '', 'size': 1}]}
165 See https://github.com/git-lfs/git-lfs/blob/master/docs/api/batch.md
173 See https://github.com/git-lfs/git-lfs/blob/master/docs/api/batch.md
166 """
174 """
167 objects = [{'oid': p.oid(), 'size': p.size()} for p in pointers]
175 objects = [{'oid': p.oid(), 'size': p.size()} for p in pointers]
168 requestdata = json.dumps({
176 requestdata = json.dumps({
169 'objects': objects,
177 'objects': objects,
170 'operation': action,
178 'operation': action,
171 })
179 })
172 batchreq = util.urlreq.request('%s/objects/batch' % self.baseurl,
180 batchreq = util.urlreq.request('%s/objects/batch' % self.baseurl,
173 data=requestdata)
181 data=requestdata)
174 batchreq.add_header('Accept', 'application/vnd.git-lfs+json')
182 batchreq.add_header('Accept', 'application/vnd.git-lfs+json')
175 batchreq.add_header('Content-Type', 'application/vnd.git-lfs+json')
183 batchreq.add_header('Content-Type', 'application/vnd.git-lfs+json')
176 try:
184 try:
177 rawjson = self.urlopener.open(batchreq).read()
185 rawjson = self.urlopener.open(batchreq).read()
178 except util.urlerr.httperror as ex:
186 except util.urlerr.httperror as ex:
179 raise LfsRemoteError(_('LFS HTTP error: %s (action=%s)')
187 raise LfsRemoteError(_('LFS HTTP error: %s (action=%s)')
180 % (ex, action))
188 % (ex, action))
181 try:
189 try:
182 response = json.loads(rawjson)
190 response = json.loads(rawjson)
183 except ValueError:
191 except ValueError:
184 raise LfsRemoteError(_('LFS server returns invalid JSON: %s')
192 raise LfsRemoteError(_('LFS server returns invalid JSON: %s')
185 % rawjson)
193 % rawjson)
186 return response
194 return response
187
195
188 def _checkforservererror(self, pointers, responses):
196 def _checkforservererror(self, pointers, responses):
189 """Scans errors from objects
197 """Scans errors from objects
190
198
191 Returns LfsRemoteError if any objects has an error"""
199 Returns LfsRemoteError if any objects has an error"""
192 for response in responses:
200 for response in responses:
193 error = response.get('error')
201 error = response.get('error')
194 if error:
202 if error:
195 ptrmap = {p.oid(): p for p in pointers}
203 ptrmap = {p.oid(): p for p in pointers}
196 p = ptrmap.get(response['oid'], None)
204 p = ptrmap.get(response['oid'], None)
197 if error['code'] == 404 and p:
205 if error['code'] == 404 and p:
198 filename = getattr(p, 'filename', 'unknown')
206 filename = getattr(p, 'filename', 'unknown')
199 raise LfsRemoteError(
207 raise LfsRemoteError(
200 _(('LFS server error. Remote object '
208 _(('LFS server error. Remote object '
201 'for file %s not found: %r')) % (filename, response))
209 'for file %s not found: %r')) % (filename, response))
202 raise LfsRemoteError(_('LFS server error: %r') % response)
210 raise LfsRemoteError(_('LFS server error: %r') % response)
203
211
204 def _extractobjects(self, response, pointers, action):
212 def _extractobjects(self, response, pointers, action):
205 """extract objects from response of the batch API
213 """extract objects from response of the batch API
206
214
207 response: parsed JSON object returned by batch API
215 response: parsed JSON object returned by batch API
208 return response['objects'] filtered by action
216 return response['objects'] filtered by action
209 raise if any object has an error
217 raise if any object has an error
210 """
218 """
211 # Scan errors from objects - fail early
219 # Scan errors from objects - fail early
212 objects = response.get('objects', [])
220 objects = response.get('objects', [])
213 self._checkforservererror(pointers, objects)
221 self._checkforservererror(pointers, objects)
214
222
215 # Filter objects with given action. Practically, this skips uploading
223 # Filter objects with given action. Practically, this skips uploading
216 # objects which exist in the server.
224 # objects which exist in the server.
217 filteredobjects = [o for o in objects if action in o.get('actions', [])]
225 filteredobjects = [o for o in objects if action in o.get('actions', [])]
218 # But for downloading, we want all objects. Therefore missing objects
226 # But for downloading, we want all objects. Therefore missing objects
219 # should be considered an error.
227 # should be considered an error.
220 if action == 'download':
228 if action == 'download':
221 if len(filteredobjects) < len(objects):
229 if len(filteredobjects) < len(objects):
222 missing = [o.get('oid', '?')
230 missing = [o.get('oid', '?')
223 for o in objects
231 for o in objects
224 if action not in o.get('actions', [])]
232 if action not in o.get('actions', [])]
225 raise LfsRemoteError(
233 raise LfsRemoteError(
226 _('LFS server claims required objects do not exist:\n%s')
234 _('LFS server claims required objects do not exist:\n%s')
227 % '\n'.join(missing))
235 % '\n'.join(missing))
228
236
229 return filteredobjects
237 return filteredobjects
230
238
231 def _basictransfer(self, obj, action, localstore):
239 def _basictransfer(self, obj, action, localstore):
232 """Download or upload a single object using basic transfer protocol
240 """Download or upload a single object using basic transfer protocol
233
241
234 obj: dict, an object description returned by batch API
242 obj: dict, an object description returned by batch API
235 action: string, one of ['upload', 'download']
243 action: string, one of ['upload', 'download']
236 localstore: blobstore.local
244 localstore: blobstore.local
237
245
238 See https://github.com/git-lfs/git-lfs/blob/master/docs/api/\
246 See https://github.com/git-lfs/git-lfs/blob/master/docs/api/\
239 basic-transfers.md
247 basic-transfers.md
240 """
248 """
241 oid = str(obj['oid'])
249 oid = str(obj['oid'])
242
250
243 href = str(obj['actions'][action].get('href'))
251 href = str(obj['actions'][action].get('href'))
244 headers = obj['actions'][action].get('header', {}).items()
252 headers = obj['actions'][action].get('header', {}).items()
245
253
246 request = util.urlreq.request(href)
254 request = util.urlreq.request(href)
247 if action == 'upload':
255 if action == 'upload':
248 # If uploading blobs, read data from local blobstore.
256 # If uploading blobs, read data from local blobstore.
249 with localstore.vfs(oid) as fp:
257 with localstore.vfs(oid) as fp:
250 _verifyfile(oid, fp)
258 _verifyfile(oid, fp)
251 request.data = filewithprogress(localstore.vfs(oid), None)
259 request.data = filewithprogress(localstore.vfs(oid), None)
252 request.get_method = lambda: 'PUT'
260 request.get_method = lambda: 'PUT'
253
261
254 for k, v in headers:
262 for k, v in headers:
255 request.add_header(k, v)
263 request.add_header(k, v)
256
264
257 response = b''
265 response = b''
258 try:
266 try:
259 req = self.urlopener.open(request)
267 req = self.urlopener.open(request)
260 while True:
268 while True:
261 data = req.read(1048576)
269 data = req.read(1048576)
262 if not data:
270 if not data:
263 break
271 break
264 response += data
272 response += data
265 except util.urlerr.httperror as ex:
273 except util.urlerr.httperror as ex:
266 raise LfsRemoteError(_('HTTP error: %s (oid=%s, action=%s)')
274 raise LfsRemoteError(_('HTTP error: %s (oid=%s, action=%s)')
267 % (ex, oid, action))
275 % (ex, oid, action))
268
276
269 if action == 'download':
277 if action == 'download':
270 # If downloading blobs, store downloaded data to local blobstore
278 # If downloading blobs, store downloaded data to local blobstore
271 localstore.write(oid, response, verify=True)
279 localstore.write(oid, response, verify=True)
272
280
273 def _batch(self, pointers, localstore, action):
281 def _batch(self, pointers, localstore, action):
274 if action not in ['upload', 'download']:
282 if action not in ['upload', 'download']:
275 raise error.ProgrammingError('invalid Git-LFS action: %s' % action)
283 raise error.ProgrammingError('invalid Git-LFS action: %s' % action)
276
284
277 response = self._batchrequest(pointers, action)
285 response = self._batchrequest(pointers, action)
278 objects = self._extractobjects(response, pointers, action)
286 objects = self._extractobjects(response, pointers, action)
279 total = sum(x.get('size', 0) for x in objects)
287 total = sum(x.get('size', 0) for x in objects)
280 sizes = {}
288 sizes = {}
281 for obj in objects:
289 for obj in objects:
282 sizes[obj.get('oid')] = obj.get('size', 0)
290 sizes[obj.get('oid')] = obj.get('size', 0)
283 topic = {'upload': _('lfs uploading'),
291 topic = {'upload': _('lfs uploading'),
284 'download': _('lfs downloading')}[action]
292 'download': _('lfs downloading')}[action]
285 if self.ui.verbose and len(objects) > 1:
293 if self.ui.verbose and len(objects) > 1:
286 self.ui.write(_('lfs: need to transfer %d objects (%s)\n')
294 self.ui.write(_('lfs: need to transfer %d objects (%s)\n')
287 % (len(objects), util.bytecount(total)))
295 % (len(objects), util.bytecount(total)))
288 self.ui.progress(topic, 0, total=total)
296 self.ui.progress(topic, 0, total=total)
289 def transfer(chunk):
297 def transfer(chunk):
290 for obj in chunk:
298 for obj in chunk:
291 objsize = obj.get('size', 0)
299 objsize = obj.get('size', 0)
292 if self.ui.verbose:
300 if self.ui.verbose:
293 if action == 'download':
301 if action == 'download':
294 msg = _('lfs: downloading %s (%s)\n')
302 msg = _('lfs: downloading %s (%s)\n')
295 elif action == 'upload':
303 elif action == 'upload':
296 msg = _('lfs: uploading %s (%s)\n')
304 msg = _('lfs: uploading %s (%s)\n')
297 self.ui.write(msg % (obj.get('oid'),
305 self.ui.write(msg % (obj.get('oid'),
298 util.bytecount(objsize)))
306 util.bytecount(objsize)))
299 retry = self.retry
307 retry = self.retry
300 while True:
308 while True:
301 try:
309 try:
302 self._basictransfer(obj, action, localstore)
310 self._basictransfer(obj, action, localstore)
303 yield 1, obj.get('oid')
311 yield 1, obj.get('oid')
304 break
312 break
305 except socket.error as ex:
313 except socket.error as ex:
306 if retry > 0:
314 if retry > 0:
307 if self.ui.verbose:
315 if self.ui.verbose:
308 self.ui.write(
316 self.ui.write(
309 _('lfs: failed: %r (remaining retry %d)\n')
317 _('lfs: failed: %r (remaining retry %d)\n')
310 % (ex, retry))
318 % (ex, retry))
311 retry -= 1
319 retry -= 1
312 continue
320 continue
313 raise
321 raise
314
322
315 oids = worker.worker(self.ui, 0.1, transfer, (),
323 oids = worker.worker(self.ui, 0.1, transfer, (),
316 sorted(objects, key=lambda o: o.get('oid')))
324 sorted(objects, key=lambda o: o.get('oid')))
317 processed = 0
325 processed = 0
318 for _one, oid in oids:
326 for _one, oid in oids:
319 processed += sizes[oid]
327 processed += sizes[oid]
320 self.ui.progress(topic, processed, total=total)
328 self.ui.progress(topic, processed, total=total)
321 if self.ui.verbose:
329 if self.ui.verbose:
322 self.ui.write(_('lfs: processed: %s\n') % oid)
330 self.ui.write(_('lfs: processed: %s\n') % oid)
323 self.ui.progress(topic, pos=None, total=total)
331 self.ui.progress(topic, pos=None, total=total)
324
332
325 def __del__(self):
333 def __del__(self):
326 # copied from mercurial/httppeer.py
334 # copied from mercurial/httppeer.py
327 urlopener = getattr(self, 'urlopener', None)
335 urlopener = getattr(self, 'urlopener', None)
328 if urlopener:
336 if urlopener:
329 for h in urlopener.handlers:
337 for h in urlopener.handlers:
330 h.close()
338 h.close()
331 getattr(h, "close_all", lambda : None)()
339 getattr(h, "close_all", lambda : None)()
332
340
333 class _dummyremote(object):
341 class _dummyremote(object):
334 """Dummy store storing blobs to temp directory."""
342 """Dummy store storing blobs to temp directory."""
335
343
336 def __init__(self, repo, url):
344 def __init__(self, repo, url):
337 fullpath = repo.vfs.join('lfs', url.path)
345 fullpath = repo.vfs.join('lfs', url.path)
338 self.vfs = lfsvfs(fullpath)
346 self.vfs = lfsvfs(fullpath)
339
347
340 def writebatch(self, pointers, fromstore):
348 def writebatch(self, pointers, fromstore):
341 for p in pointers:
349 for p in pointers:
342 content = fromstore.read(p.oid(), verify=True)
350 content = fromstore.read(p.oid(), verify=True)
343 with self.vfs(p.oid(), 'wb', atomictemp=True) as fp:
351 with self.vfs(p.oid(), 'wb', atomictemp=True) as fp:
344 fp.write(content)
352 fp.write(content)
345
353
346 def readbatch(self, pointers, tostore):
354 def readbatch(self, pointers, tostore):
347 for p in pointers:
355 for p in pointers:
348 content = self.vfs.read(p.oid())
356 content = self.vfs.read(p.oid())
349 tostore.write(p.oid(), content, verify=True)
357 tostore.write(p.oid(), content, verify=True)
350
358
351 class _nullremote(object):
359 class _nullremote(object):
352 """Null store storing blobs to /dev/null."""
360 """Null store storing blobs to /dev/null."""
353
361
354 def __init__(self, repo, url):
362 def __init__(self, repo, url):
355 pass
363 pass
356
364
357 def writebatch(self, pointers, fromstore):
365 def writebatch(self, pointers, fromstore):
358 pass
366 pass
359
367
360 def readbatch(self, pointers, tostore):
368 def readbatch(self, pointers, tostore):
361 pass
369 pass
362
370
363 class _promptremote(object):
371 class _promptremote(object):
364 """Prompt user to set lfs.url when accessed."""
372 """Prompt user to set lfs.url when accessed."""
365
373
366 def __init__(self, repo, url):
374 def __init__(self, repo, url):
367 pass
375 pass
368
376
369 def writebatch(self, pointers, fromstore, ui=None):
377 def writebatch(self, pointers, fromstore, ui=None):
370 self._prompt()
378 self._prompt()
371
379
372 def readbatch(self, pointers, tostore, ui=None):
380 def readbatch(self, pointers, tostore, ui=None):
373 self._prompt()
381 self._prompt()
374
382
375 def _prompt(self):
383 def _prompt(self):
376 raise error.Abort(_('lfs.url needs to be configured'))
384 raise error.Abort(_('lfs.url needs to be configured'))
377
385
378 _storemap = {
386 _storemap = {
379 'https': _gitlfsremote,
387 'https': _gitlfsremote,
380 'http': _gitlfsremote,
388 'http': _gitlfsremote,
381 'file': _dummyremote,
389 'file': _dummyremote,
382 'null': _nullremote,
390 'null': _nullremote,
383 None: _promptremote,
391 None: _promptremote,
384 }
392 }
385
393
386 def _verify(oid, content):
394 def _verify(oid, content):
387 realoid = hashlib.sha256(content).hexdigest()
395 realoid = hashlib.sha256(content).hexdigest()
388 if realoid != oid:
396 if realoid != oid:
389 raise error.Abort(_('detected corrupt lfs object: %s') % oid,
397 raise error.Abort(_('detected corrupt lfs object: %s') % oid,
390 hint=_('run hg verify'))
398 hint=_('run hg verify'))
391
399
392 def _verifyfile(oid, fp):
400 def _verifyfile(oid, fp):
393 sha256 = hashlib.sha256()
401 sha256 = hashlib.sha256()
394 while True:
402 while True:
395 data = fp.read(1024 * 1024)
403 data = fp.read(1024 * 1024)
396 if not data:
404 if not data:
397 break
405 break
398 sha256.update(data)
406 sha256.update(data)
399 realoid = sha256.hexdigest()
407 realoid = sha256.hexdigest()
400 if realoid != oid:
408 if realoid != oid:
401 raise error.Abort(_('detected corrupt lfs object: %s') % oid,
409 raise error.Abort(_('detected corrupt lfs object: %s') % oid,
402 hint=_('run hg verify'))
410 hint=_('run hg verify'))
403
411
404 def remote(repo):
412 def remote(repo):
405 """remotestore factory. return a store in _storemap depending on config"""
413 """remotestore factory. return a store in _storemap depending on config"""
406 defaulturl = ''
414 defaulturl = ''
407
415
408 # convert deprecated configs to the new url. TODO: remove this if other
416 # convert deprecated configs to the new url. TODO: remove this if other
409 # places are migrated to the new url config.
417 # places are migrated to the new url config.
410 # deprecated config: lfs.remotestore
418 # deprecated config: lfs.remotestore
411 deprecatedstore = repo.ui.config('lfs', 'remotestore')
419 deprecatedstore = repo.ui.config('lfs', 'remotestore')
412 if deprecatedstore == 'dummy':
420 if deprecatedstore == 'dummy':
413 # deprecated config: lfs.remotepath
421 # deprecated config: lfs.remotepath
414 defaulturl = 'file://' + repo.ui.config('lfs', 'remotepath')
422 defaulturl = 'file://' + repo.ui.config('lfs', 'remotepath')
415 elif deprecatedstore == 'git-lfs':
423 elif deprecatedstore == 'git-lfs':
416 # deprecated config: lfs.remoteurl
424 # deprecated config: lfs.remoteurl
417 defaulturl = repo.ui.config('lfs', 'remoteurl')
425 defaulturl = repo.ui.config('lfs', 'remoteurl')
418 elif deprecatedstore == 'null':
426 elif deprecatedstore == 'null':
419 defaulturl = 'null://'
427 defaulturl = 'null://'
420
428
421 url = util.url(repo.ui.config('lfs', 'url', defaulturl))
429 url = util.url(repo.ui.config('lfs', 'url', defaulturl))
422 scheme = url.scheme
430 scheme = url.scheme
423 if scheme not in _storemap:
431 if scheme not in _storemap:
424 raise error.Abort(_('lfs: unknown url scheme: %s') % scheme)
432 raise error.Abort(_('lfs: unknown url scheme: %s') % scheme)
425 return _storemap[scheme](repo, url)
433 return _storemap[scheme](repo, url)
426
434
427 class LfsRemoteError(error.RevlogError):
435 class LfsRemoteError(error.RevlogError):
428 pass
436 pass
@@ -1,867 +1,857 b''
1 # Initial setup
1 # Initial setup
2
2
3 $ cat >> $HGRCPATH << EOF
3 $ cat >> $HGRCPATH << EOF
4 > [extensions]
4 > [extensions]
5 > lfs=
5 > lfs=
6 > [lfs]
6 > [lfs]
7 > threshold=1000B
7 > threshold=1000B
8 > EOF
8 > EOF
9
9
10 $ LONG=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
10 $ LONG=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
11
11
12 # Prepare server and enable extension
12 # Prepare server and enable extension
13 $ hg init server
13 $ hg init server
14 $ hg clone -q server client
14 $ hg clone -q server client
15 $ cd client
15 $ cd client
16
16
17 # Commit small file
17 # Commit small file
18 $ echo s > smallfile
18 $ echo s > smallfile
19 $ hg commit -Aqm "add small file"
19 $ hg commit -Aqm "add small file"
20
20
21 # Commit large file
21 # Commit large file
22 $ echo $LONG > largefile
22 $ echo $LONG > largefile
23 $ grep lfs .hg/requires
23 $ grep lfs .hg/requires
24 [1]
24 [1]
25 $ hg commit --traceback -Aqm "add large file"
25 $ hg commit --traceback -Aqm "add large file"
26 $ grep lfs .hg/requires
26 $ grep lfs .hg/requires
27 lfs
27 lfs
28
28
29 # Ensure metadata is stored
29 # Ensure metadata is stored
30 $ hg debugdata largefile 0
30 $ hg debugdata largefile 0
31 version https://git-lfs.github.com/spec/v1
31 version https://git-lfs.github.com/spec/v1
32 oid sha256:f11e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
32 oid sha256:f11e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
33 size 1501
33 size 1501
34 x-is-binary 0
34 x-is-binary 0
35
35
36 # Check the blobstore is populated
36 # Check the blobstore is populated
37 $ find .hg/store/lfs/objects | sort
37 $ find .hg/store/lfs/objects | sort
38 .hg/store/lfs/objects
38 .hg/store/lfs/objects
39 .hg/store/lfs/objects/f1
39 .hg/store/lfs/objects/f1
40 .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
40 .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
41
41
42 # Check the blob stored contains the actual contents of the file
42 # Check the blob stored contains the actual contents of the file
43 $ cat .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
43 $ cat .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
44 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
44 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
45
45
46 # Push changes to the server
46 # Push changes to the server
47
47
48 $ hg push
48 $ hg push
49 pushing to $TESTTMP/server
49 pushing to $TESTTMP/server
50 searching for changes
50 searching for changes
51 abort: lfs.url needs to be configured
51 abort: lfs.url needs to be configured
52 [255]
52 [255]
53
53
54 $ cat >> $HGRCPATH << EOF
54 $ cat >> $HGRCPATH << EOF
55 > [lfs]
55 > [lfs]
56 > url=file:$TESTTMP/dummy-remote/
56 > url=file:$TESTTMP/dummy-remote/
57 > EOF
57 > EOF
58
58
59 $ hg push -v | egrep -v '^(uncompressed| )'
59 $ hg push -v | egrep -v '^(uncompressed| )'
60 pushing to $TESTTMP/server
60 pushing to $TESTTMP/server
61 searching for changes
61 searching for changes
62 lfs: found f11e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b in the local lfs store
62 lfs: found f11e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b in the local lfs store
63 2 changesets found
63 2 changesets found
64 adding changesets
64 adding changesets
65 adding manifests
65 adding manifests
66 adding file changes
66 adding file changes
67 added 2 changesets with 2 changes to 2 files
67 added 2 changesets with 2 changes to 2 files
68
68
69 # Unknown URL scheme
69 # Unknown URL scheme
70
70
71 $ hg push --config lfs.url=ftp://foobar
71 $ hg push --config lfs.url=ftp://foobar
72 abort: lfs: unknown url scheme: ftp
72 abort: lfs: unknown url scheme: ftp
73 [255]
73 [255]
74
74
75 $ cd ../
75 $ cd ../
76
76
77 # Initialize new client (not cloning) and setup extension
77 # Initialize new client (not cloning) and setup extension
78 $ hg init client2
78 $ hg init client2
79 $ cd client2
79 $ cd client2
80 $ cat >> .hg/hgrc <<EOF
80 $ cat >> .hg/hgrc <<EOF
81 > [paths]
81 > [paths]
82 > default = $TESTTMP/server
82 > default = $TESTTMP/server
83 > EOF
83 > EOF
84
84
85 # Pull from server
85 # Pull from server
86 $ hg pull default
86 $ hg pull default
87 pulling from $TESTTMP/server
87 pulling from $TESTTMP/server
88 requesting all changes
88 requesting all changes
89 adding changesets
89 adding changesets
90 adding manifests
90 adding manifests
91 adding file changes
91 adding file changes
92 added 2 changesets with 2 changes to 2 files
92 added 2 changesets with 2 changes to 2 files
93 new changesets b29ba743f89d:00c137947d30
93 new changesets b29ba743f89d:00c137947d30
94 (run 'hg update' to get a working copy)
94 (run 'hg update' to get a working copy)
95
95
96 # Check the blobstore is not yet populated
96 # Check the blobstore is not yet populated
97 $ [ -d .hg/store/lfs/objects ]
97 $ [ -d .hg/store/lfs/objects ]
98 [1]
98 [1]
99
99
100 # Update to the last revision containing the large file
100 # Update to the last revision containing the large file
101 $ hg update
101 $ hg update
102 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
102 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
103
103
104 # Check the blobstore has been populated on update
104 # Check the blobstore has been populated on update
105 $ find .hg/store/lfs/objects | sort
105 $ find .hg/store/lfs/objects | sort
106 .hg/store/lfs/objects
106 .hg/store/lfs/objects
107 .hg/store/lfs/objects/f1
107 .hg/store/lfs/objects/f1
108 .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
108 .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
109
109
110 # Check the contents of the file are fetched from blobstore when requested
110 # Check the contents of the file are fetched from blobstore when requested
111 $ hg cat -r . largefile
111 $ hg cat -r . largefile
112 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
112 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
113
113
114 # Check the file has been copied in the working copy
114 # Check the file has been copied in the working copy
115 $ cat largefile
115 $ cat largefile
116 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
116 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
117
117
118 $ cd ..
118 $ cd ..
119
119
120 # Check rename, and switch between large and small files
120 # Check rename, and switch between large and small files
121
121
122 $ hg init repo3
122 $ hg init repo3
123 $ cd repo3
123 $ cd repo3
124 $ cat >> .hg/hgrc << EOF
124 $ cat >> .hg/hgrc << EOF
125 > [lfs]
125 > [lfs]
126 > threshold=10B
126 > threshold=10B
127 > EOF
127 > EOF
128
128
129 $ echo LONGER-THAN-TEN-BYTES-WILL-TRIGGER-LFS > large
129 $ echo LONGER-THAN-TEN-BYTES-WILL-TRIGGER-LFS > large
130 $ echo SHORTER > small
130 $ echo SHORTER > small
131 $ hg add . -q
131 $ hg add . -q
132 $ hg commit -m 'commit with lfs content'
132 $ hg commit -m 'commit with lfs content'
133
133
134 $ hg mv large l
134 $ hg mv large l
135 $ hg mv small s
135 $ hg mv small s
136 $ hg commit -m 'renames'
136 $ hg commit -m 'renames'
137
137
138 $ echo SHORT > l
138 $ echo SHORT > l
139 $ echo BECOME-LARGER-FROM-SHORTER > s
139 $ echo BECOME-LARGER-FROM-SHORTER > s
140 $ hg commit -m 'large to small, small to large'
140 $ hg commit -m 'large to small, small to large'
141
141
142 $ echo 1 >> l
142 $ echo 1 >> l
143 $ echo 2 >> s
143 $ echo 2 >> s
144 $ hg commit -m 'random modifications'
144 $ hg commit -m 'random modifications'
145
145
146 $ echo RESTORE-TO-BE-LARGE > l
146 $ echo RESTORE-TO-BE-LARGE > l
147 $ echo SHORTER > s
147 $ echo SHORTER > s
148 $ hg commit -m 'switch large and small again'
148 $ hg commit -m 'switch large and small again'
149
149
150 # Test lfs_files template
150 # Test lfs_files template
151
151
152 $ hg log -r 'all()' -T '{rev} {join(lfs_files, ", ")}\n'
152 $ hg log -r 'all()' -T '{rev} {join(lfs_files, ", ")}\n'
153 0 large
153 0 large
154 1 l
154 1 l
155 2 s
155 2 s
156 3 s
156 3 s
157 4 l
157 4 l
158
158
159 # Push and pull the above repo
159 # Push and pull the above repo
160
160
161 $ hg --cwd .. init repo4
161 $ hg --cwd .. init repo4
162 $ hg push ../repo4
162 $ hg push ../repo4
163 pushing to ../repo4
163 pushing to ../repo4
164 searching for changes
164 searching for changes
165 adding changesets
165 adding changesets
166 adding manifests
166 adding manifests
167 adding file changes
167 adding file changes
168 added 5 changesets with 10 changes to 4 files
168 added 5 changesets with 10 changes to 4 files
169
169
170 $ hg --cwd .. init repo5
170 $ hg --cwd .. init repo5
171 $ hg --cwd ../repo5 pull ../repo3
171 $ hg --cwd ../repo5 pull ../repo3
172 pulling from ../repo3
172 pulling from ../repo3
173 requesting all changes
173 requesting all changes
174 adding changesets
174 adding changesets
175 adding manifests
175 adding manifests
176 adding file changes
176 adding file changes
177 added 5 changesets with 10 changes to 4 files
177 added 5 changesets with 10 changes to 4 files
178 new changesets fd47a419c4f7:5adf850972b9
178 new changesets fd47a419c4f7:5adf850972b9
179 (run 'hg update' to get a working copy)
179 (run 'hg update' to get a working copy)
180
180
181 $ cd ..
181 $ cd ..
182
182
183 # Test clone
183 # Test clone
184
184
185 $ hg init repo6
185 $ hg init repo6
186 $ cd repo6
186 $ cd repo6
187 $ cat >> .hg/hgrc << EOF
187 $ cat >> .hg/hgrc << EOF
188 > [lfs]
188 > [lfs]
189 > threshold=30B
189 > threshold=30B
190 > EOF
190 > EOF
191
191
192 $ echo LARGE-BECAUSE-IT-IS-MORE-THAN-30-BYTES > large
192 $ echo LARGE-BECAUSE-IT-IS-MORE-THAN-30-BYTES > large
193 $ echo SMALL > small
193 $ echo SMALL > small
194 $ hg commit -Aqm 'create a lfs file' large small
194 $ hg commit -Aqm 'create a lfs file' large small
195 $ hg debuglfsupload -r 'all()' -v
195 $ hg debuglfsupload -r 'all()' -v
196 lfs: found 8e92251415339ae9b148c8da89ed5ec665905166a1ab11b09dca8fad83344738 in the local lfs store
196 lfs: found 8e92251415339ae9b148c8da89ed5ec665905166a1ab11b09dca8fad83344738 in the local lfs store
197
197
198 $ cd ..
198 $ cd ..
199
199
200 $ hg clone repo6 repo7
200 $ hg clone repo6 repo7
201 updating to branch default
201 updating to branch default
202 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
202 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
203 $ cd repo7
203 $ cd repo7
204 $ hg config extensions --debug | grep lfs
204 $ hg config extensions --debug | grep lfs
205 $TESTTMP/repo7/.hg/hgrc:*: extensions.lfs= (glob)
205 $TESTTMP/repo7/.hg/hgrc:*: extensions.lfs= (glob)
206 $ cat large
206 $ cat large
207 LARGE-BECAUSE-IT-IS-MORE-THAN-30-BYTES
207 LARGE-BECAUSE-IT-IS-MORE-THAN-30-BYTES
208 $ cat small
208 $ cat small
209 SMALL
209 SMALL
210
210
211 $ cd ..
211 $ cd ..
212
212
213 $ hg --config extensions.share= share repo7 sharedrepo
213 $ hg --config extensions.share= share repo7 sharedrepo
214 updating working directory
214 updating working directory
215 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
215 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
216 $ hg -R sharedrepo config extensions --debug | grep lfs
216 $ hg -R sharedrepo config extensions --debug | grep lfs
217 $TESTTMP/sharedrepo/.hg/hgrc:*: extensions.lfs= (glob)
217 $TESTTMP/sharedrepo/.hg/hgrc:*: extensions.lfs= (glob)
218
218
219 # Test rename and status
219 # Test rename and status
220
220
221 $ hg init repo8
221 $ hg init repo8
222 $ cd repo8
222 $ cd repo8
223 $ cat >> .hg/hgrc << EOF
223 $ cat >> .hg/hgrc << EOF
224 > [lfs]
224 > [lfs]
225 > threshold=10B
225 > threshold=10B
226 > EOF
226 > EOF
227
227
228 $ echo THIS-IS-LFS-BECAUSE-10-BYTES > a1
228 $ echo THIS-IS-LFS-BECAUSE-10-BYTES > a1
229 $ echo SMALL > a2
229 $ echo SMALL > a2
230 $ hg commit -m a -A a1 a2
230 $ hg commit -m a -A a1 a2
231 $ hg status
231 $ hg status
232 $ hg mv a1 b1
232 $ hg mv a1 b1
233 $ hg mv a2 a1
233 $ hg mv a2 a1
234 $ hg mv b1 a2
234 $ hg mv b1 a2
235 $ hg commit -m b
235 $ hg commit -m b
236 $ hg status
236 $ hg status
237 >>> with open('a2', 'wb') as f:
237 >>> with open('a2', 'wb') as f:
238 ... f.write(b'\1\nSTART-WITH-HG-FILELOG-METADATA')
238 ... f.write(b'\1\nSTART-WITH-HG-FILELOG-METADATA')
239 >>> with open('a1', 'wb') as f:
239 >>> with open('a1', 'wb') as f:
240 ... f.write(b'\1\nMETA\n')
240 ... f.write(b'\1\nMETA\n')
241 $ hg commit -m meta
241 $ hg commit -m meta
242 $ hg status
242 $ hg status
243 $ hg log -T '{rev}: {file_copies} | {file_dels} | {file_adds}\n'
243 $ hg log -T '{rev}: {file_copies} | {file_dels} | {file_adds}\n'
244 2: | |
244 2: | |
245 1: a1 (a2)a2 (a1) | |
245 1: a1 (a2)a2 (a1) | |
246 0: | | a1 a2
246 0: | | a1 a2
247
247
248 $ for n in a1 a2; do
248 $ for n in a1 a2; do
249 > for r in 0 1 2; do
249 > for r in 0 1 2; do
250 > printf '\n%s @ %s\n' $n $r
250 > printf '\n%s @ %s\n' $n $r
251 > hg debugdata $n $r
251 > hg debugdata $n $r
252 > done
252 > done
253 > done
253 > done
254
254
255 a1 @ 0
255 a1 @ 0
256 version https://git-lfs.github.com/spec/v1
256 version https://git-lfs.github.com/spec/v1
257 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
257 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
258 size 29
258 size 29
259 x-is-binary 0
259 x-is-binary 0
260
260
261 a1 @ 1
261 a1 @ 1
262 \x01 (esc)
262 \x01 (esc)
263 copy: a2
263 copy: a2
264 copyrev: 50470ad23cf937b1f4b9f80bfe54df38e65b50d9
264 copyrev: 50470ad23cf937b1f4b9f80bfe54df38e65b50d9
265 \x01 (esc)
265 \x01 (esc)
266 SMALL
266 SMALL
267
267
268 a1 @ 2
268 a1 @ 2
269 \x01 (esc)
269 \x01 (esc)
270 \x01 (esc)
270 \x01 (esc)
271 \x01 (esc)
271 \x01 (esc)
272 META
272 META
273
273
274 a2 @ 0
274 a2 @ 0
275 SMALL
275 SMALL
276
276
277 a2 @ 1
277 a2 @ 1
278 version https://git-lfs.github.com/spec/v1
278 version https://git-lfs.github.com/spec/v1
279 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
279 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
280 size 29
280 size 29
281 x-hg-copy a1
281 x-hg-copy a1
282 x-hg-copyrev be23af27908a582af43e5cda209a5a9b319de8d4
282 x-hg-copyrev be23af27908a582af43e5cda209a5a9b319de8d4
283 x-is-binary 0
283 x-is-binary 0
284
284
285 a2 @ 2
285 a2 @ 2
286 version https://git-lfs.github.com/spec/v1
286 version https://git-lfs.github.com/spec/v1
287 oid sha256:876dadc86a8542f9798048f2c47f51dbf8e4359aed883e8ec80c5db825f0d943
287 oid sha256:876dadc86a8542f9798048f2c47f51dbf8e4359aed883e8ec80c5db825f0d943
288 size 32
288 size 32
289 x-is-binary 0
289 x-is-binary 0
290
290
291 # Verify commit hashes include rename metadata
291 # Verify commit hashes include rename metadata
292
292
293 $ hg log -T '{rev}:{node|short} {desc}\n'
293 $ hg log -T '{rev}:{node|short} {desc}\n'
294 2:0fae949de7fa meta
294 2:0fae949de7fa meta
295 1:9cd6bdffdac0 b
295 1:9cd6bdffdac0 b
296 0:7f96794915f7 a
296 0:7f96794915f7 a
297
297
298 $ cd ..
298 $ cd ..
299
299
300 # Test bundle
300 # Test bundle
301
301
302 $ hg init repo9
302 $ hg init repo9
303 $ cd repo9
303 $ cd repo9
304 $ cat >> .hg/hgrc << EOF
304 $ cat >> .hg/hgrc << EOF
305 > [lfs]
305 > [lfs]
306 > threshold=10B
306 > threshold=10B
307 > [diff]
307 > [diff]
308 > git=1
308 > git=1
309 > EOF
309 > EOF
310
310
311 $ for i in 0 single two three 4; do
311 $ for i in 0 single two three 4; do
312 > echo 'THIS-IS-LFS-'$i > a
312 > echo 'THIS-IS-LFS-'$i > a
313 > hg commit -m a-$i -A a
313 > hg commit -m a-$i -A a
314 > done
314 > done
315
315
316 $ hg update 2 -q
316 $ hg update 2 -q
317 $ echo 'THIS-IS-LFS-2-CHILD' > a
317 $ echo 'THIS-IS-LFS-2-CHILD' > a
318 $ hg commit -m branching -q
318 $ hg commit -m branching -q
319
319
320 $ hg bundle --base 1 bundle.hg -v
320 $ hg bundle --base 1 bundle.hg -v
321 lfs: found 5ab7a3739a5feec94a562d070a14f36dba7cad17e5484a4a89eea8e5f3166888 in the local lfs store
321 lfs: found 5ab7a3739a5feec94a562d070a14f36dba7cad17e5484a4a89eea8e5f3166888 in the local lfs store
322 lfs: found a9c7d1cd6ce2b9bbdf46ed9a862845228717b921c089d0d42e3bcaed29eb612e in the local lfs store
322 lfs: found a9c7d1cd6ce2b9bbdf46ed9a862845228717b921c089d0d42e3bcaed29eb612e in the local lfs store
323 lfs: found f693890c49c409ec33673b71e53f297681f76c1166daf33b2ad7ebf8b1d3237e in the local lfs store
323 lfs: found f693890c49c409ec33673b71e53f297681f76c1166daf33b2ad7ebf8b1d3237e in the local lfs store
324 lfs: found fda198fea753eb66a252e9856915e1f5cddbe41723bd4b695ece2604ad3c9f75 in the local lfs store
324 lfs: found fda198fea753eb66a252e9856915e1f5cddbe41723bd4b695ece2604ad3c9f75 in the local lfs store
325 4 changesets found
325 4 changesets found
326 uncompressed size of bundle content:
326 uncompressed size of bundle content:
327 * (changelog) (glob)
327 * (changelog) (glob)
328 * (manifests) (glob)
328 * (manifests) (glob)
329 * a (glob)
329 * a (glob)
330 $ hg --config extensions.strip= strip -r 2 --no-backup --force -q
330 $ hg --config extensions.strip= strip -r 2 --no-backup --force -q
331 $ hg -R bundle.hg log -p -T '{rev} {desc}\n' a
331 $ hg -R bundle.hg log -p -T '{rev} {desc}\n' a
332 5 branching
332 5 branching
333 diff --git a/a b/a
333 diff --git a/a b/a
334 --- a/a
334 --- a/a
335 +++ b/a
335 +++ b/a
336 @@ -1,1 +1,1 @@
336 @@ -1,1 +1,1 @@
337 -THIS-IS-LFS-two
337 -THIS-IS-LFS-two
338 +THIS-IS-LFS-2-CHILD
338 +THIS-IS-LFS-2-CHILD
339
339
340 4 a-4
340 4 a-4
341 diff --git a/a b/a
341 diff --git a/a b/a
342 --- a/a
342 --- a/a
343 +++ b/a
343 +++ b/a
344 @@ -1,1 +1,1 @@
344 @@ -1,1 +1,1 @@
345 -THIS-IS-LFS-three
345 -THIS-IS-LFS-three
346 +THIS-IS-LFS-4
346 +THIS-IS-LFS-4
347
347
348 3 a-three
348 3 a-three
349 diff --git a/a b/a
349 diff --git a/a b/a
350 --- a/a
350 --- a/a
351 +++ b/a
351 +++ b/a
352 @@ -1,1 +1,1 @@
352 @@ -1,1 +1,1 @@
353 -THIS-IS-LFS-two
353 -THIS-IS-LFS-two
354 +THIS-IS-LFS-three
354 +THIS-IS-LFS-three
355
355
356 2 a-two
356 2 a-two
357 diff --git a/a b/a
357 diff --git a/a b/a
358 --- a/a
358 --- a/a
359 +++ b/a
359 +++ b/a
360 @@ -1,1 +1,1 @@
360 @@ -1,1 +1,1 @@
361 -THIS-IS-LFS-single
361 -THIS-IS-LFS-single
362 +THIS-IS-LFS-two
362 +THIS-IS-LFS-two
363
363
364 1 a-single
364 1 a-single
365 diff --git a/a b/a
365 diff --git a/a b/a
366 --- a/a
366 --- a/a
367 +++ b/a
367 +++ b/a
368 @@ -1,1 +1,1 @@
368 @@ -1,1 +1,1 @@
369 -THIS-IS-LFS-0
369 -THIS-IS-LFS-0
370 +THIS-IS-LFS-single
370 +THIS-IS-LFS-single
371
371
372 0 a-0
372 0 a-0
373 diff --git a/a b/a
373 diff --git a/a b/a
374 new file mode 100644
374 new file mode 100644
375 --- /dev/null
375 --- /dev/null
376 +++ b/a
376 +++ b/a
377 @@ -0,0 +1,1 @@
377 @@ -0,0 +1,1 @@
378 +THIS-IS-LFS-0
378 +THIS-IS-LFS-0
379
379
380 $ hg bundle -R bundle.hg --base 1 bundle-again.hg -q
380 $ hg bundle -R bundle.hg --base 1 bundle-again.hg -q
381 $ hg -R bundle-again.hg log -p -T '{rev} {desc}\n' a
381 $ hg -R bundle-again.hg log -p -T '{rev} {desc}\n' a
382 5 branching
382 5 branching
383 diff --git a/a b/a
383 diff --git a/a b/a
384 --- a/a
384 --- a/a
385 +++ b/a
385 +++ b/a
386 @@ -1,1 +1,1 @@
386 @@ -1,1 +1,1 @@
387 -THIS-IS-LFS-two
387 -THIS-IS-LFS-two
388 +THIS-IS-LFS-2-CHILD
388 +THIS-IS-LFS-2-CHILD
389
389
390 4 a-4
390 4 a-4
391 diff --git a/a b/a
391 diff --git a/a b/a
392 --- a/a
392 --- a/a
393 +++ b/a
393 +++ b/a
394 @@ -1,1 +1,1 @@
394 @@ -1,1 +1,1 @@
395 -THIS-IS-LFS-three
395 -THIS-IS-LFS-three
396 +THIS-IS-LFS-4
396 +THIS-IS-LFS-4
397
397
398 3 a-three
398 3 a-three
399 diff --git a/a b/a
399 diff --git a/a b/a
400 --- a/a
400 --- a/a
401 +++ b/a
401 +++ b/a
402 @@ -1,1 +1,1 @@
402 @@ -1,1 +1,1 @@
403 -THIS-IS-LFS-two
403 -THIS-IS-LFS-two
404 +THIS-IS-LFS-three
404 +THIS-IS-LFS-three
405
405
406 2 a-two
406 2 a-two
407 diff --git a/a b/a
407 diff --git a/a b/a
408 --- a/a
408 --- a/a
409 +++ b/a
409 +++ b/a
410 @@ -1,1 +1,1 @@
410 @@ -1,1 +1,1 @@
411 -THIS-IS-LFS-single
411 -THIS-IS-LFS-single
412 +THIS-IS-LFS-two
412 +THIS-IS-LFS-two
413
413
414 1 a-single
414 1 a-single
415 diff --git a/a b/a
415 diff --git a/a b/a
416 --- a/a
416 --- a/a
417 +++ b/a
417 +++ b/a
418 @@ -1,1 +1,1 @@
418 @@ -1,1 +1,1 @@
419 -THIS-IS-LFS-0
419 -THIS-IS-LFS-0
420 +THIS-IS-LFS-single
420 +THIS-IS-LFS-single
421
421
422 0 a-0
422 0 a-0
423 diff --git a/a b/a
423 diff --git a/a b/a
424 new file mode 100644
424 new file mode 100644
425 --- /dev/null
425 --- /dev/null
426 +++ b/a
426 +++ b/a
427 @@ -0,0 +1,1 @@
427 @@ -0,0 +1,1 @@
428 +THIS-IS-LFS-0
428 +THIS-IS-LFS-0
429
429
430 $ cd ..
430 $ cd ..
431
431
432 # Test isbinary
432 # Test isbinary
433
433
434 $ hg init repo10
434 $ hg init repo10
435 $ cd repo10
435 $ cd repo10
436 $ cat >> .hg/hgrc << EOF
436 $ cat >> .hg/hgrc << EOF
437 > [extensions]
437 > [extensions]
438 > lfs=
438 > lfs=
439 > [lfs]
439 > [lfs]
440 > threshold=1
440 > threshold=1
441 > EOF
441 > EOF
442 $ $PYTHON <<'EOF'
442 $ $PYTHON <<'EOF'
443 > def write(path, content):
443 > def write(path, content):
444 > with open(path, 'wb') as f:
444 > with open(path, 'wb') as f:
445 > f.write(content)
445 > f.write(content)
446 > write('a', b'\0\0')
446 > write('a', b'\0\0')
447 > write('b', b'\1\n')
447 > write('b', b'\1\n')
448 > write('c', b'\1\n\0')
448 > write('c', b'\1\n\0')
449 > write('d', b'xx')
449 > write('d', b'xx')
450 > EOF
450 > EOF
451 $ hg add a b c d
451 $ hg add a b c d
452 $ hg diff --stat
452 $ hg diff --stat
453 a | Bin
453 a | Bin
454 b | 1 +
454 b | 1 +
455 c | Bin
455 c | Bin
456 d | 1 +
456 d | 1 +
457 4 files changed, 2 insertions(+), 0 deletions(-)
457 4 files changed, 2 insertions(+), 0 deletions(-)
458 $ hg commit -m binarytest
458 $ hg commit -m binarytest
459 $ cat > $TESTTMP/dumpbinary.py << EOF
459 $ cat > $TESTTMP/dumpbinary.py << EOF
460 > def reposetup(ui, repo):
460 > def reposetup(ui, repo):
461 > for n in 'abcd':
461 > for n in 'abcd':
462 > ui.write(('%s: binary=%s\n') % (n, repo['.'][n].isbinary()))
462 > ui.write(('%s: binary=%s\n') % (n, repo['.'][n].isbinary()))
463 > EOF
463 > EOF
464 $ hg --config extensions.dumpbinary=$TESTTMP/dumpbinary.py id --trace
464 $ hg --config extensions.dumpbinary=$TESTTMP/dumpbinary.py id --trace
465 a: binary=True
465 a: binary=True
466 b: binary=False
466 b: binary=False
467 c: binary=True
467 c: binary=True
468 d: binary=False
468 d: binary=False
469 b55353847f02 tip
469 b55353847f02 tip
470
470
471 $ cd ..
471 $ cd ..
472
472
473 # Test fctx.cmp fastpath - diff without LFS blobs
473 # Test fctx.cmp fastpath - diff without LFS blobs
474
474
475 $ hg init repo11
475 $ hg init repo11
476 $ cd repo11
476 $ cd repo11
477 $ cat >> .hg/hgrc <<EOF
477 $ cat >> .hg/hgrc <<EOF
478 > [lfs]
478 > [lfs]
479 > threshold=1
479 > threshold=1
480 > EOF
480 > EOF
481 $ cat > ../patch.diff <<EOF
481 $ cat > ../patch.diff <<EOF
482 > # HG changeset patch
482 > # HG changeset patch
483 > 2
483 > 2
484 >
484 >
485 > diff --git a/a b/a
485 > diff --git a/a b/a
486 > old mode 100644
486 > old mode 100644
487 > new mode 100755
487 > new mode 100755
488 > EOF
488 > EOF
489
489
490 $ for i in 1 2 3; do
490 $ for i in 1 2 3; do
491 > cp ../repo10/a a
491 > cp ../repo10/a a
492 > if [ $i = 3 ]; then
492 > if [ $i = 3 ]; then
493 > # make a content-only change
493 > # make a content-only change
494 > hg import -q --bypass ../patch.diff
494 > hg import -q --bypass ../patch.diff
495 > hg update -q
495 > hg update -q
496 > rm ../patch.diff
496 > rm ../patch.diff
497 > else
497 > else
498 > echo $i >> a
498 > echo $i >> a
499 > hg commit -m $i -A a
499 > hg commit -m $i -A a
500 > fi
500 > fi
501 > done
501 > done
502 $ [ -d .hg/store/lfs/objects ]
502 $ [ -d .hg/store/lfs/objects ]
503
503
504 $ cd ..
504 $ cd ..
505
505
506 $ hg clone repo11 repo12 --noupdate
506 $ hg clone repo11 repo12 --noupdate
507 $ cd repo12
507 $ cd repo12
508 $ hg log --removed -p a -T '{desc}\n' --config diff.nobinary=1 --git
508 $ hg log --removed -p a -T '{desc}\n' --config diff.nobinary=1 --git
509 2
509 2
510 diff --git a/a b/a
510 diff --git a/a b/a
511 old mode 100644
511 old mode 100644
512 new mode 100755
512 new mode 100755
513
513
514 2
514 2
515 diff --git a/a b/a
515 diff --git a/a b/a
516 Binary file a has changed
516 Binary file a has changed
517
517
518 1
518 1
519 diff --git a/a b/a
519 diff --git a/a b/a
520 new file mode 100644
520 new file mode 100644
521 Binary file a has changed
521 Binary file a has changed
522
522
523 $ [ -d .hg/store/lfs/objects ]
523 $ [ -d .hg/store/lfs/objects ]
524 [1]
524 [1]
525
525
526 $ cd ..
526 $ cd ..
527
527
528 # Verify the repos
528 # Verify the repos
529
529
530 $ cat > $TESTTMP/dumpflog.py << EOF
530 $ cat > $TESTTMP/dumpflog.py << EOF
531 > # print raw revision sizes, flags, and hashes for certain files
531 > # print raw revision sizes, flags, and hashes for certain files
532 > import hashlib
532 > import hashlib
533 > from mercurial import revlog
533 > from mercurial import revlog
534 > from mercurial.node import short
534 > from mercurial.node import short
535 > def hash(rawtext):
535 > def hash(rawtext):
536 > h = hashlib.sha512()
536 > h = hashlib.sha512()
537 > h.update(rawtext)
537 > h.update(rawtext)
538 > return h.hexdigest()[:4]
538 > return h.hexdigest()[:4]
539 > def reposetup(ui, repo):
539 > def reposetup(ui, repo):
540 > # these 2 files are interesting
540 > # these 2 files are interesting
541 > for name in ['l', 's']:
541 > for name in ['l', 's']:
542 > fl = repo.file(name)
542 > fl = repo.file(name)
543 > if len(fl) == 0:
543 > if len(fl) == 0:
544 > continue
544 > continue
545 > sizes = [revlog.revlog.rawsize(fl, i) for i in fl]
545 > sizes = [revlog.revlog.rawsize(fl, i) for i in fl]
546 > texts = [fl.revision(i, raw=True) for i in fl]
546 > texts = [fl.revision(i, raw=True) for i in fl]
547 > flags = [int(fl.flags(i)) for i in fl]
547 > flags = [int(fl.flags(i)) for i in fl]
548 > hashes = [hash(t) for t in texts]
548 > hashes = [hash(t) for t in texts]
549 > print(' %s: rawsizes=%r flags=%r hashes=%r'
549 > print(' %s: rawsizes=%r flags=%r hashes=%r'
550 > % (name, sizes, flags, hashes))
550 > % (name, sizes, flags, hashes))
551 > EOF
551 > EOF
552
552
553 $ for i in client client2 server repo3 repo4 repo5 repo6 repo7 repo8 repo9 \
553 $ for i in client client2 server repo3 repo4 repo5 repo6 repo7 repo8 repo9 \
554 > repo10; do
554 > repo10; do
555 > echo 'repo:' $i
555 > echo 'repo:' $i
556 > hg --cwd $i verify --config extensions.dumpflog=$TESTTMP/dumpflog.py -q
556 > hg --cwd $i verify --config extensions.dumpflog=$TESTTMP/dumpflog.py -q
557 > done
557 > done
558 repo: client
558 repo: client
559 repo: client2
559 repo: client2
560 repo: server
560 repo: server
561 repo: repo3
561 repo: repo3
562 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
562 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
563 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
563 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
564 repo: repo4
564 repo: repo4
565 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
565 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
566 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
566 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
567 repo: repo5
567 repo: repo5
568 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
568 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
569 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
569 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
570 repo: repo6
570 repo: repo6
571 repo: repo7
571 repo: repo7
572 repo: repo8
572 repo: repo8
573 repo: repo9
573 repo: repo9
574 repo: repo10
574 repo: repo10
575
575
576 repo12 doesn't have any cached lfs files and its source never pushed its
576 repo12 doesn't have any cached lfs files and its source never pushed its
577 files. Therefore, the files don't exist in the remote store. Use the files in
577 files. Therefore, the files don't exist in the remote store. Use the files in
578 the user cache.
578 the user cache.
579
579
580 $ test -d $TESTTMP/repo12/.hg/store/lfs/objects
580 $ test -d $TESTTMP/repo12/.hg/store/lfs/objects
581 [1]
581 [1]
582
582
583 $ hg --config extensions.share= share repo12 repo13
583 $ hg --config extensions.share= share repo12 repo13
584 updating working directory
584 updating working directory
585 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
585 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
586 $ hg -R repo13 -q verify
586 $ hg -R repo13 -q verify
587
587
588 $ hg clone repo12 repo14
588 $ hg clone repo12 repo14
589 updating to branch default
589 updating to branch default
590 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
590 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
591 $ hg -R repo14 -q verify
591 $ hg -R repo14 -q verify
592
592
593 If the source repo doesn't have the blob (maybe it was pulled or cloned with
593 If the source repo doesn't have the blob (maybe it was pulled or cloned with
594 --noupdate), the blob is still accessible via the global cache to send to the
594 --noupdate), the blob is still accessible via the global cache to send to the
595 remote store.
595 remote store.
596
596
597 $ rm -rf $TESTTMP/repo14/.hg/store/lfs
597 $ rm -rf $TESTTMP/repo14/.hg/store/lfs
598 $ hg init repo15
598 $ hg init repo15
599 $ hg -R repo14 push repo15
599 $ hg -R repo14 push repo15
600 pushing to repo15
600 pushing to repo15
601 searching for changes
601 searching for changes
602 adding changesets
602 adding changesets
603 adding manifests
603 adding manifests
604 adding file changes
604 adding file changes
605 added 3 changesets with 2 changes to 1 files
605 added 3 changesets with 2 changes to 1 files
606 $ hg -R repo14 -q verify
606 $ hg -R repo14 -q verify
607
607
608 Test damaged file scenarios. (This also damages the usercache because of the
608 Test damaged file scenarios. (This also damages the usercache because of the
609 hardlinks.)
609 hardlinks.)
610
610
611 $ echo 'damage' >> repo5/.hg/store/lfs/objects/66/100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
611 $ echo 'damage' >> repo5/.hg/store/lfs/objects/66/100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
612
612
613 Repo with damaged lfs objects in any revision will fail verification.
613 Repo with damaged lfs objects in any revision will fail verification.
614
614
615 $ hg -R repo5 verify
615 $ hg -R repo5 verify
616 checking changesets
616 checking changesets
617 checking manifests
617 checking manifests
618 crosschecking files in changesets and manifests
618 crosschecking files in changesets and manifests
619 checking files
619 checking files
620 l@1: unpacking 46a2f24864bc: integrity check failed on data/l.i:0
620 l@1: unpacking 46a2f24864bc: integrity check failed on data/l.i:0
621 large@0: unpacking 2c531e0992ff: integrity check failed on data/large.i:0
621 large@0: unpacking 2c531e0992ff: integrity check failed on data/large.i:0
622 4 files, 5 changesets, 10 total revisions
622 4 files, 5 changesets, 10 total revisions
623 2 integrity errors encountered!
623 2 integrity errors encountered!
624 (first damaged changeset appears to be 0)
624 (first damaged changeset appears to be 0)
625 [1]
625 [1]
626
626
627 Updates work after cloning a damaged repo, if the damaged lfs objects aren't in
627 Updates work after cloning a damaged repo, if the damaged lfs objects aren't in
628 the update destination. Those objects won't be added to the new repo's store
628 the update destination. Those objects won't be added to the new repo's store
629 because they aren't accessed.
629 because they aren't accessed.
630
630
631 $ hg clone -v repo5 fromcorrupt
631 $ hg clone -v repo5 fromcorrupt
632 updating to branch default
632 updating to branch default
633 resolving manifests
633 resolving manifests
634 getting l
634 getting l
635 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the usercache
635 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the usercache
636 getting s
636 getting s
637 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
637 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
638 $ test -f fromcorrupt/.hg/store/lfs/objects/66/100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
638 $ test -f fromcorrupt/.hg/store/lfs/objects/66/100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
639 [1]
639 [1]
640
640
641 Verify will copy/link all lfs objects into the local store that aren't already
641 Verify will copy/link all lfs objects into the local store that aren't already
642 present. Bypass the corrupted usercache to show that verify works when fed by
642 present. Bypass the corrupted usercache to show that verify works when fed by
643 the (uncorrupted) remote store.
643 the (uncorrupted) remote store.
644
644
645 $ hg -R fromcorrupt --config lfs.usercache=emptycache verify -v
645 $ hg -R fromcorrupt --config lfs.usercache=emptycache verify -v
646 repository uses revlog format 1
646 repository uses revlog format 1
647 checking changesets
647 checking changesets
648 checking manifests
648 checking manifests
649 crosschecking files in changesets and manifests
649 crosschecking files in changesets and manifests
650 checking files
650 checking files
651 lfs: adding 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e to the usercache
651 lfs: adding 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e to the usercache
652 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the local lfs store
652 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the local lfs store
653 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
653 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
654 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the local lfs store
654 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the local lfs store
655 lfs: adding 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 to the usercache
655 lfs: adding 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 to the usercache
656 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
656 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
657 lfs: adding b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c to the usercache
657 lfs: adding b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c to the usercache
658 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
658 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
659 4 files, 5 changesets, 10 total revisions
659 4 files, 5 changesets, 10 total revisions
660
660
661 BUG: Verify will copy/link a corrupted file from the usercache into the local
661 Verify will not copy/link a corrupted file from the usercache into the local
662 store, and poison it. (The verify with a good remote now fails.)
662 store, and poison it. (The verify with a good remote now works.)
663
663
664 $ rm -r fromcorrupt/.hg/store/lfs/objects/66/100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
664 $ rm -r fromcorrupt/.hg/store/lfs/objects/66/100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
665 $ hg -R fromcorrupt verify -v
665 $ hg -R fromcorrupt verify -v
666 repository uses revlog format 1
666 repository uses revlog format 1
667 checking changesets
667 checking changesets
668 checking manifests
668 checking manifests
669 crosschecking files in changesets and manifests
669 crosschecking files in changesets and manifests
670 checking files
670 checking files
671 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the usercache
672 l@1: unpacking 46a2f24864bc: integrity check failed on data/l.i:0
671 l@1: unpacking 46a2f24864bc: integrity check failed on data/l.i:0
673 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
672 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
674 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the local lfs store
675 large@0: unpacking 2c531e0992ff: integrity check failed on data/large.i:0
673 large@0: unpacking 2c531e0992ff: integrity check failed on data/large.i:0
676 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
674 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
677 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
675 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
678 4 files, 5 changesets, 10 total revisions
676 4 files, 5 changesets, 10 total revisions
679 2 integrity errors encountered!
677 2 integrity errors encountered!
680 (first damaged changeset appears to be 0)
678 (first damaged changeset appears to be 0)
681 [1]
679 [1]
682 $ hg -R fromcorrupt --config lfs.usercache=emptycache verify -v
680 $ hg -R fromcorrupt --config lfs.usercache=emptycache verify -v
683 repository uses revlog format 1
681 repository uses revlog format 1
684 checking changesets
682 checking changesets
685 checking manifests
683 checking manifests
686 crosschecking files in changesets and manifests
684 crosschecking files in changesets and manifests
687 checking files
685 checking files
688 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the local lfs store
686 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the usercache
689 l@1: unpacking 46a2f24864bc: integrity check failed on data/l.i:0
690 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
687 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
691 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the local lfs store
688 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the local lfs store
692 large@0: unpacking 2c531e0992ff: integrity check failed on data/large.i:0
693 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
689 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
694 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
690 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
695 4 files, 5 changesets, 10 total revisions
691 4 files, 5 changesets, 10 total revisions
696 2 integrity errors encountered!
697 (first damaged changeset appears to be 0)
698 [1]
699
692
700 Damaging a file required by the update destination fails the update.
693 Damaging a file required by the update destination fails the update.
701
694
702 $ echo 'damage' >> $TESTTMP/dummy-remote/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
695 $ echo 'damage' >> $TESTTMP/dummy-remote/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
703 $ hg --config lfs.usercache=emptycache clone -v repo5 fromcorrupt2
696 $ hg --config lfs.usercache=emptycache clone -v repo5 fromcorrupt2
704 updating to branch default
697 updating to branch default
705 resolving manifests
698 resolving manifests
706 getting l
699 getting l
707 abort: detected corrupt lfs object: 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
700 abort: detected corrupt lfs object: 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
708 (run hg verify)
701 (run hg verify)
709 [255]
702 [255]
710
703
711 A corrupted lfs blob is not transferred from a file://remotestore to the
704 A corrupted lfs blob is not transferred from a file://remotestore to the
712 usercache or local store.
705 usercache or local store.
713
706
714 $ test -f emptycache/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
707 $ test -f emptycache/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
715 [1]
708 [1]
716 $ test -f fromcorrupt2/.hg/store/lfs/objects/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
709 $ test -f fromcorrupt2/.hg/store/lfs/objects/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
717 [1]
710 [1]
718
711
719 $ hg -R fromcorrupt2 verify
712 $ hg -R fromcorrupt2 verify
720 checking changesets
713 checking changesets
721 checking manifests
714 checking manifests
722 crosschecking files in changesets and manifests
715 crosschecking files in changesets and manifests
723 checking files
716 checking files
724 l@1: unpacking 46a2f24864bc: integrity check failed on data/l.i:0
717 l@1: unpacking 46a2f24864bc: integrity check failed on data/l.i:0
725 large@0: unpacking 2c531e0992ff: integrity check failed on data/large.i:0
718 large@0: unpacking 2c531e0992ff: integrity check failed on data/large.i:0
726 4 files, 5 changesets, 10 total revisions
719 4 files, 5 changesets, 10 total revisions
727 2 integrity errors encountered!
720 2 integrity errors encountered!
728 (first damaged changeset appears to be 0)
721 (first damaged changeset appears to be 0)
729 [1]
722 [1]
730
723
731 Corrupt local files are not sent upstream. (The alternate dummy remote
724 Corrupt local files are not sent upstream. (The alternate dummy remote
732 avoids the corrupt lfs object in the original remote.)
725 avoids the corrupt lfs object in the original remote.)
733
726
734 $ mkdir $TESTTMP/dummy-remote2
727 $ mkdir $TESTTMP/dummy-remote2
735 $ hg init dest
728 $ hg init dest
736 $ hg -R fromcorrupt2 --config lfs.url=file:///$TESTTMP/dummy-remote2 push -v dest
729 $ hg -R fromcorrupt2 --config lfs.url=file:///$TESTTMP/dummy-remote2 push -v dest
737 pushing to dest
730 pushing to dest
738 searching for changes
731 searching for changes
739 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
732 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
740 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
733 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
741 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
734 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
742 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the local lfs store
743 abort: detected corrupt lfs object: 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
735 abort: detected corrupt lfs object: 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
744 (run hg verify)
736 (run hg verify)
745 [255]
737 [255]
746
738
747 $ hg -R fromcorrupt2 --config lfs.url=file:///$TESTTMP/dummy-remote2 verify -v
739 $ hg -R fromcorrupt2 --config lfs.url=file:///$TESTTMP/dummy-remote2 verify -v
748 repository uses revlog format 1
740 repository uses revlog format 1
749 checking changesets
741 checking changesets
750 checking manifests
742 checking manifests
751 crosschecking files in changesets and manifests
743 crosschecking files in changesets and manifests
752 checking files
744 checking files
753 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the local lfs store
754 l@1: unpacking 46a2f24864bc: integrity check failed on data/l.i:0
745 l@1: unpacking 46a2f24864bc: integrity check failed on data/l.i:0
755 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
746 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
756 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the local lfs store
757 large@0: unpacking 2c531e0992ff: integrity check failed on data/large.i:0
747 large@0: unpacking 2c531e0992ff: integrity check failed on data/large.i:0
758 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
748 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
759 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
749 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
760 4 files, 5 changesets, 10 total revisions
750 4 files, 5 changesets, 10 total revisions
761 2 integrity errors encountered!
751 2 integrity errors encountered!
762 (first damaged changeset appears to be 0)
752 (first damaged changeset appears to be 0)
763 [1]
753 [1]
764
754
765 $ cat $TESTTMP/dummy-remote2/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b | $TESTDIR/f --sha256
755 $ cat $TESTTMP/dummy-remote2/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b | $TESTDIR/f --sha256
766 sha256=22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
756 sha256=22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
767 $ cat fromcorrupt2/.hg/store/lfs/objects/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b | $TESTDIR/f --sha256
757 $ cat fromcorrupt2/.hg/store/lfs/objects/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b | $TESTDIR/f --sha256
768 sha256=22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
758 sha256=22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
769 $ test -f $TESTTMP/dummy-remote2/66/100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
759 $ test -f $TESTTMP/dummy-remote2/66/100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
770 [1]
760 [1]
771
761
772 Accessing a corrupt file will complain
762 Accessing a corrupt file will complain
773
763
774 $ hg --cwd fromcorrupt2 cat -r 0 large
764 $ hg --cwd fromcorrupt2 cat -r 0 large
775 abort: integrity check failed on data/large.i:0!
765 abort: integrity check failed on data/large.i:0!
776 [255]
766 [255]
777
767
778 lfs -> normal -> lfs round trip conversions are possible. The threshold for the
768 lfs -> normal -> lfs round trip conversions are possible. The threshold for the
779 lfs destination is specified here because it was originally listed in the local
769 lfs destination is specified here because it was originally listed in the local
780 .hgrc, and the global one is too high to trigger lfs usage. For lfs -> normal,
770 .hgrc, and the global one is too high to trigger lfs usage. For lfs -> normal,
781 there's no 'lfs' destination repo requirement. For normal -> lfs, there is.
771 there's no 'lfs' destination repo requirement. For normal -> lfs, there is.
782
772
783 XXX: There's not a great way to ensure that the conversion to normal files
773 XXX: There's not a great way to ensure that the conversion to normal files
784 actually converts _everything_ to normal. The extension needs to be loaded for
774 actually converts _everything_ to normal. The extension needs to be loaded for
785 the source, but there's no way to disable it for the destination. The best that
775 the source, but there's no way to disable it for the destination. The best that
786 can be done is to raise the threshold so that lfs isn't used on the destination.
776 can be done is to raise the threshold so that lfs isn't used on the destination.
787 It doesn't like using '!' to unset the value on the command line.
777 It doesn't like using '!' to unset the value on the command line.
788
778
789 $ hg --config extensions.convert= --config lfs.threshold=1000M \
779 $ hg --config extensions.convert= --config lfs.threshold=1000M \
790 > convert repo8 convert_normal
780 > convert repo8 convert_normal
791 initializing destination convert_normal repository
781 initializing destination convert_normal repository
792 scanning source...
782 scanning source...
793 sorting...
783 sorting...
794 converting...
784 converting...
795 2 a
785 2 a
796 1 b
786 1 b
797 0 meta
787 0 meta
798 $ grep 'lfs' convert_normal/.hg/requires
788 $ grep 'lfs' convert_normal/.hg/requires
799 [1]
789 [1]
800 $ hg --cwd convert_normal debugdata a1 0
790 $ hg --cwd convert_normal debugdata a1 0
801 THIS-IS-LFS-BECAUSE-10-BYTES
791 THIS-IS-LFS-BECAUSE-10-BYTES
802
792
803 $ hg --config extensions.convert= --config lfs.threshold=10B \
793 $ hg --config extensions.convert= --config lfs.threshold=10B \
804 > convert convert_normal convert_lfs
794 > convert convert_normal convert_lfs
805 initializing destination convert_lfs repository
795 initializing destination convert_lfs repository
806 scanning source...
796 scanning source...
807 sorting...
797 sorting...
808 converting...
798 converting...
809 2 a
799 2 a
810 1 b
800 1 b
811 0 meta
801 0 meta
812 $ hg --cwd convert_lfs debugdata a1 0
802 $ hg --cwd convert_lfs debugdata a1 0
813 version https://git-lfs.github.com/spec/v1
803 version https://git-lfs.github.com/spec/v1
814 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
804 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
815 size 29
805 size 29
816 x-is-binary 0
806 x-is-binary 0
817 $ grep 'lfs' convert_lfs/.hg/requires
807 $ grep 'lfs' convert_lfs/.hg/requires
818 lfs
808 lfs
819
809
820 This convert is trickier, because it contains deleted files (via `hg mv`)
810 This convert is trickier, because it contains deleted files (via `hg mv`)
821
811
822 $ hg --config extensions.convert= --config lfs.threshold=1000M \
812 $ hg --config extensions.convert= --config lfs.threshold=1000M \
823 > convert repo3 convert_normal2
813 > convert repo3 convert_normal2
824 initializing destination convert_normal2 repository
814 initializing destination convert_normal2 repository
825 scanning source...
815 scanning source...
826 sorting...
816 sorting...
827 converting...
817 converting...
828 4 commit with lfs content
818 4 commit with lfs content
829 3 renames
819 3 renames
830 2 large to small, small to large
820 2 large to small, small to large
831 1 random modifications
821 1 random modifications
832 0 switch large and small again
822 0 switch large and small again
833 $ grep 'lfs' convert_normal2/.hg/requires
823 $ grep 'lfs' convert_normal2/.hg/requires
834 [1]
824 [1]
835 $ hg --cwd convert_normal2 debugdata large 0
825 $ hg --cwd convert_normal2 debugdata large 0
836 LONGER-THAN-TEN-BYTES-WILL-TRIGGER-LFS
826 LONGER-THAN-TEN-BYTES-WILL-TRIGGER-LFS
837
827
838 $ hg --config extensions.convert= --config lfs.threshold=10B \
828 $ hg --config extensions.convert= --config lfs.threshold=10B \
839 > convert convert_normal2 convert_lfs2
829 > convert convert_normal2 convert_lfs2
840 initializing destination convert_lfs2 repository
830 initializing destination convert_lfs2 repository
841 scanning source...
831 scanning source...
842 sorting...
832 sorting...
843 converting...
833 converting...
844 4 commit with lfs content
834 4 commit with lfs content
845 3 renames
835 3 renames
846 2 large to small, small to large
836 2 large to small, small to large
847 1 random modifications
837 1 random modifications
848 0 switch large and small again
838 0 switch large and small again
849 $ grep 'lfs' convert_lfs2/.hg/requires
839 $ grep 'lfs' convert_lfs2/.hg/requires
850 lfs
840 lfs
851 $ hg --cwd convert_lfs2 debugdata large 0
841 $ hg --cwd convert_lfs2 debugdata large 0
852 version https://git-lfs.github.com/spec/v1
842 version https://git-lfs.github.com/spec/v1
853 oid sha256:66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
843 oid sha256:66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
854 size 39
844 size 39
855 x-is-binary 0
845 x-is-binary 0
856
846
857 $ hg -R convert_lfs2 config --debug extensions | grep lfs
847 $ hg -R convert_lfs2 config --debug extensions | grep lfs
858 $TESTTMP/convert_lfs2/.hg/hgrc:*: extensions.lfs= (glob)
848 $TESTTMP/convert_lfs2/.hg/hgrc:*: extensions.lfs= (glob)
859
849
860 Committing deleted files works:
850 Committing deleted files works:
861
851
862 $ hg init $TESTTMP/repo-del
852 $ hg init $TESTTMP/repo-del
863 $ cd $TESTTMP/repo-del
853 $ cd $TESTTMP/repo-del
864 $ echo 1 > A
854 $ echo 1 > A
865 $ hg commit -m 'add A' -A A
855 $ hg commit -m 'add A' -A A
866 $ hg rm A
856 $ hg rm A
867 $ hg commit -m 'rm A'
857 $ hg commit -m 'rm A'
General Comments 0
You need to be logged in to leave comments. Login now