##// END OF EJS Templates
lfs: improve the client message when the server signals an object error...
Matt Harbison -
r37259:67db8484 default
parent child Browse files
Show More
@@ -1,504 +1,515 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 pycompat,
21 pycompat,
22 url as urlmod,
22 url as urlmod,
23 util,
23 util,
24 vfs as vfsmod,
24 vfs as vfsmod,
25 worker,
25 worker,
26 )
26 )
27
27
28 from ..largefiles import lfutil
28 from ..largefiles import lfutil
29
29
30 # 64 bytes for SHA256
30 # 64 bytes for SHA256
31 _lfsre = re.compile(br'\A[a-f0-9]{64}\Z')
31 _lfsre = re.compile(br'\A[a-f0-9]{64}\Z')
32
32
33 class lfsvfs(vfsmod.vfs):
33 class lfsvfs(vfsmod.vfs):
34 def join(self, path):
34 def join(self, path):
35 """split the path at first two characters, like: XX/XXXXX..."""
35 """split the path at first two characters, like: XX/XXXXX..."""
36 if not _lfsre.match(path):
36 if not _lfsre.match(path):
37 raise error.ProgrammingError('unexpected lfs path: %s' % path)
37 raise error.ProgrammingError('unexpected lfs path: %s' % path)
38 return super(lfsvfs, self).join(path[0:2], path[2:])
38 return super(lfsvfs, self).join(path[0:2], path[2:])
39
39
40 def walk(self, path=None, onerror=None):
40 def walk(self, path=None, onerror=None):
41 """Yield (dirpath, [], oids) tuple for blobs under path
41 """Yield (dirpath, [], oids) tuple for blobs under path
42
42
43 Oids only exist in the root of this vfs, so dirpath is always ''.
43 Oids only exist in the root of this vfs, so dirpath is always ''.
44 """
44 """
45 root = os.path.normpath(self.base)
45 root = os.path.normpath(self.base)
46 # when dirpath == root, dirpath[prefixlen:] becomes empty
46 # when dirpath == root, dirpath[prefixlen:] becomes empty
47 # because len(dirpath) < prefixlen.
47 # because len(dirpath) < prefixlen.
48 prefixlen = len(pathutil.normasprefix(root))
48 prefixlen = len(pathutil.normasprefix(root))
49 oids = []
49 oids = []
50
50
51 for dirpath, dirs, files in os.walk(self.reljoin(self.base, path or ''),
51 for dirpath, dirs, files in os.walk(self.reljoin(self.base, path or ''),
52 onerror=onerror):
52 onerror=onerror):
53 dirpath = dirpath[prefixlen:]
53 dirpath = dirpath[prefixlen:]
54
54
55 # Silently skip unexpected files and directories
55 # Silently skip unexpected files and directories
56 if len(dirpath) == 2:
56 if len(dirpath) == 2:
57 oids.extend([dirpath + f for f in files
57 oids.extend([dirpath + f for f in files
58 if _lfsre.match(dirpath + f)])
58 if _lfsre.match(dirpath + f)])
59
59
60 yield ('', [], oids)
60 yield ('', [], oids)
61
61
62 class filewithprogress(object):
62 class filewithprogress(object):
63 """a file-like object that supports __len__ and read.
63 """a file-like object that supports __len__ and read.
64
64
65 Useful to provide progress information for how many bytes are read.
65 Useful to provide progress information for how many bytes are read.
66 """
66 """
67
67
68 def __init__(self, fp, callback):
68 def __init__(self, fp, callback):
69 self._fp = fp
69 self._fp = fp
70 self._callback = callback # func(readsize)
70 self._callback = callback # func(readsize)
71 fp.seek(0, os.SEEK_END)
71 fp.seek(0, os.SEEK_END)
72 self._len = fp.tell()
72 self._len = fp.tell()
73 fp.seek(0)
73 fp.seek(0)
74
74
75 def __len__(self):
75 def __len__(self):
76 return self._len
76 return self._len
77
77
78 def read(self, size):
78 def read(self, size):
79 if self._fp is None:
79 if self._fp is None:
80 return b''
80 return b''
81 data = self._fp.read(size)
81 data = self._fp.read(size)
82 if data:
82 if data:
83 if self._callback:
83 if self._callback:
84 self._callback(len(data))
84 self._callback(len(data))
85 else:
85 else:
86 self._fp.close()
86 self._fp.close()
87 self._fp = None
87 self._fp = None
88 return data
88 return data
89
89
90 class local(object):
90 class local(object):
91 """Local blobstore for large file contents.
91 """Local blobstore for large file contents.
92
92
93 This blobstore is used both as a cache and as a staging area for large blobs
93 This blobstore is used both as a cache and as a staging area for large blobs
94 to be uploaded to the remote blobstore.
94 to be uploaded to the remote blobstore.
95 """
95 """
96
96
97 def __init__(self, repo):
97 def __init__(self, repo):
98 fullpath = repo.svfs.join('lfs/objects')
98 fullpath = repo.svfs.join('lfs/objects')
99 self.vfs = lfsvfs(fullpath)
99 self.vfs = lfsvfs(fullpath)
100 usercache = lfutil._usercachedir(repo.ui, 'lfs')
100 usercache = lfutil._usercachedir(repo.ui, 'lfs')
101 self.cachevfs = lfsvfs(usercache)
101 self.cachevfs = lfsvfs(usercache)
102 self.ui = repo.ui
102 self.ui = repo.ui
103
103
104 def open(self, oid):
104 def open(self, oid):
105 """Open a read-only file descriptor to the named blob, in either the
105 """Open a read-only file descriptor to the named blob, in either the
106 usercache or the local store."""
106 usercache or the local store."""
107 # The usercache is the most likely place to hold the file. Commit will
107 # The usercache is the most likely place to hold the file. Commit will
108 # write to both it and the local store, as will anything that downloads
108 # write to both it and the local store, as will anything that downloads
109 # the blobs. However, things like clone without an update won't
109 # the blobs. However, things like clone without an update won't
110 # populate the local store. For an init + push of a local clone,
110 # populate the local store. For an init + push of a local clone,
111 # the usercache is the only place it _could_ be. If not present, the
111 # the usercache is the only place it _could_ be. If not present, the
112 # missing file msg here will indicate the local repo, not the usercache.
112 # missing file msg here will indicate the local repo, not the usercache.
113 if self.cachevfs.exists(oid):
113 if self.cachevfs.exists(oid):
114 return self.cachevfs(oid, 'rb')
114 return self.cachevfs(oid, 'rb')
115
115
116 return self.vfs(oid, 'rb')
116 return self.vfs(oid, 'rb')
117
117
118 def download(self, oid, src):
118 def download(self, oid, src):
119 """Read the blob from the remote source in chunks, verify the content,
119 """Read the blob from the remote source in chunks, verify the content,
120 and write to this local blobstore."""
120 and write to this local blobstore."""
121 sha256 = hashlib.sha256()
121 sha256 = hashlib.sha256()
122
122
123 with self.vfs(oid, 'wb', atomictemp=True) as fp:
123 with self.vfs(oid, 'wb', atomictemp=True) as fp:
124 for chunk in util.filechunkiter(src, size=1048576):
124 for chunk in util.filechunkiter(src, size=1048576):
125 fp.write(chunk)
125 fp.write(chunk)
126 sha256.update(chunk)
126 sha256.update(chunk)
127
127
128 realoid = sha256.hexdigest()
128 realoid = sha256.hexdigest()
129 if realoid != oid:
129 if realoid != oid:
130 raise error.Abort(_('corrupt remote lfs object: %s') % oid)
130 raise error.Abort(_('corrupt remote lfs object: %s') % oid)
131
131
132 # XXX: should we verify the content of the cache, and hardlink back to
132 # XXX: should we verify the content of the cache, and hardlink back to
133 # the local store on success, but truncate, write and link on failure?
133 # the local store on success, but truncate, write and link on failure?
134 if not self.cachevfs.exists(oid):
134 if not self.cachevfs.exists(oid):
135 self.ui.note(_('lfs: adding %s to the usercache\n') % oid)
135 self.ui.note(_('lfs: adding %s to the usercache\n') % oid)
136 lfutil.link(self.vfs.join(oid), self.cachevfs.join(oid))
136 lfutil.link(self.vfs.join(oid), self.cachevfs.join(oid))
137
137
138 def write(self, oid, data):
138 def write(self, oid, data):
139 """Write blob to local blobstore.
139 """Write blob to local blobstore.
140
140
141 This should only be called from the filelog during a commit or similar.
141 This should only be called from the filelog during a commit or similar.
142 As such, there is no need to verify the data. Imports from a remote
142 As such, there is no need to verify the data. Imports from a remote
143 store must use ``download()`` instead."""
143 store must use ``download()`` instead."""
144 with self.vfs(oid, 'wb', atomictemp=True) as fp:
144 with self.vfs(oid, 'wb', atomictemp=True) as fp:
145 fp.write(data)
145 fp.write(data)
146
146
147 # XXX: should we verify the content of the cache, and hardlink back to
147 # XXX: should we verify the content of the cache, and hardlink back to
148 # the local store on success, but truncate, write and link on failure?
148 # the local store on success, but truncate, write and link on failure?
149 if not self.cachevfs.exists(oid):
149 if not self.cachevfs.exists(oid):
150 self.ui.note(_('lfs: adding %s to the usercache\n') % oid)
150 self.ui.note(_('lfs: adding %s to the usercache\n') % oid)
151 lfutil.link(self.vfs.join(oid), self.cachevfs.join(oid))
151 lfutil.link(self.vfs.join(oid), self.cachevfs.join(oid))
152
152
153 def read(self, oid, verify=True):
153 def read(self, oid, verify=True):
154 """Read blob from local blobstore."""
154 """Read blob from local blobstore."""
155 if not self.vfs.exists(oid):
155 if not self.vfs.exists(oid):
156 blob = self._read(self.cachevfs, oid, verify)
156 blob = self._read(self.cachevfs, oid, verify)
157
157
158 # Even if revlog will verify the content, it needs to be verified
158 # Even if revlog will verify the content, it needs to be verified
159 # now before making the hardlink to avoid propagating corrupt blobs.
159 # now before making the hardlink to avoid propagating corrupt blobs.
160 # Don't abort if corruption is detected, because `hg verify` will
160 # Don't abort if corruption is detected, because `hg verify` will
161 # give more useful info about the corruption- simply don't add the
161 # give more useful info about the corruption- simply don't add the
162 # hardlink.
162 # hardlink.
163 if verify or hashlib.sha256(blob).hexdigest() == oid:
163 if verify or hashlib.sha256(blob).hexdigest() == oid:
164 self.ui.note(_('lfs: found %s in the usercache\n') % oid)
164 self.ui.note(_('lfs: found %s in the usercache\n') % oid)
165 lfutil.link(self.cachevfs.join(oid), self.vfs.join(oid))
165 lfutil.link(self.cachevfs.join(oid), self.vfs.join(oid))
166 else:
166 else:
167 self.ui.note(_('lfs: found %s in the local lfs store\n') % oid)
167 self.ui.note(_('lfs: found %s in the local lfs store\n') % oid)
168 blob = self._read(self.vfs, oid, verify)
168 blob = self._read(self.vfs, oid, verify)
169 return blob
169 return blob
170
170
171 def _read(self, vfs, oid, verify):
171 def _read(self, vfs, oid, verify):
172 """Read blob (after verifying) from the given store"""
172 """Read blob (after verifying) from the given store"""
173 blob = vfs.read(oid)
173 blob = vfs.read(oid)
174 if verify:
174 if verify:
175 _verify(oid, blob)
175 _verify(oid, blob)
176 return blob
176 return blob
177
177
178 def verify(self, oid):
178 def verify(self, oid):
179 """Indicate whether or not the hash of the underlying file matches its
179 """Indicate whether or not the hash of the underlying file matches its
180 name."""
180 name."""
181 sha256 = hashlib.sha256()
181 sha256 = hashlib.sha256()
182
182
183 with self.open(oid) as fp:
183 with self.open(oid) as fp:
184 for chunk in util.filechunkiter(fp, size=1048576):
184 for chunk in util.filechunkiter(fp, size=1048576):
185 sha256.update(chunk)
185 sha256.update(chunk)
186
186
187 return oid == sha256.hexdigest()
187 return oid == sha256.hexdigest()
188
188
189 def has(self, oid):
189 def has(self, oid):
190 """Returns True if the local blobstore contains the requested blob,
190 """Returns True if the local blobstore contains the requested blob,
191 False otherwise."""
191 False otherwise."""
192 return self.cachevfs.exists(oid) or self.vfs.exists(oid)
192 return self.cachevfs.exists(oid) or self.vfs.exists(oid)
193
193
194 class _gitlfsremote(object):
194 class _gitlfsremote(object):
195
195
196 def __init__(self, repo, url):
196 def __init__(self, repo, url):
197 ui = repo.ui
197 ui = repo.ui
198 self.ui = ui
198 self.ui = ui
199 baseurl, authinfo = url.authinfo()
199 baseurl, authinfo = url.authinfo()
200 self.baseurl = baseurl.rstrip('/')
200 self.baseurl = baseurl.rstrip('/')
201 useragent = repo.ui.config('experimental', 'lfs.user-agent')
201 useragent = repo.ui.config('experimental', 'lfs.user-agent')
202 if not useragent:
202 if not useragent:
203 useragent = 'git-lfs/2.3.4 (Mercurial %s)' % util.version()
203 useragent = 'git-lfs/2.3.4 (Mercurial %s)' % util.version()
204 self.urlopener = urlmod.opener(ui, authinfo, useragent)
204 self.urlopener = urlmod.opener(ui, authinfo, useragent)
205 self.retry = ui.configint('lfs', 'retry')
205 self.retry = ui.configint('lfs', 'retry')
206
206
207 def writebatch(self, pointers, fromstore):
207 def writebatch(self, pointers, fromstore):
208 """Batch upload from local to remote blobstore."""
208 """Batch upload from local to remote blobstore."""
209 self._batch(_deduplicate(pointers), fromstore, 'upload')
209 self._batch(_deduplicate(pointers), fromstore, 'upload')
210
210
211 def readbatch(self, pointers, tostore):
211 def readbatch(self, pointers, tostore):
212 """Batch download from remote to local blostore."""
212 """Batch download from remote to local blostore."""
213 self._batch(_deduplicate(pointers), tostore, 'download')
213 self._batch(_deduplicate(pointers), tostore, 'download')
214
214
215 def _batchrequest(self, pointers, action):
215 def _batchrequest(self, pointers, action):
216 """Get metadata about objects pointed by pointers for given action
216 """Get metadata about objects pointed by pointers for given action
217
217
218 Return decoded JSON object like {'objects': [{'oid': '', 'size': 1}]}
218 Return decoded JSON object like {'objects': [{'oid': '', 'size': 1}]}
219 See https://github.com/git-lfs/git-lfs/blob/master/docs/api/batch.md
219 See https://github.com/git-lfs/git-lfs/blob/master/docs/api/batch.md
220 """
220 """
221 objects = [{'oid': p.oid(), 'size': p.size()} for p in pointers]
221 objects = [{'oid': p.oid(), 'size': p.size()} for p in pointers]
222 requestdata = json.dumps({
222 requestdata = json.dumps({
223 'objects': objects,
223 'objects': objects,
224 'operation': action,
224 'operation': action,
225 })
225 })
226 batchreq = util.urlreq.request('%s/objects/batch' % self.baseurl,
226 batchreq = util.urlreq.request('%s/objects/batch' % self.baseurl,
227 data=requestdata)
227 data=requestdata)
228 batchreq.add_header('Accept', 'application/vnd.git-lfs+json')
228 batchreq.add_header('Accept', 'application/vnd.git-lfs+json')
229 batchreq.add_header('Content-Type', 'application/vnd.git-lfs+json')
229 batchreq.add_header('Content-Type', 'application/vnd.git-lfs+json')
230 try:
230 try:
231 rsp = self.urlopener.open(batchreq)
231 rsp = self.urlopener.open(batchreq)
232 rawjson = rsp.read()
232 rawjson = rsp.read()
233 except util.urlerr.httperror as ex:
233 except util.urlerr.httperror as ex:
234 raise LfsRemoteError(_('LFS HTTP error: %s (action=%s)')
234 raise LfsRemoteError(_('LFS HTTP error: %s (action=%s)')
235 % (ex, action))
235 % (ex, action))
236 try:
236 try:
237 response = json.loads(rawjson)
237 response = json.loads(rawjson)
238 except ValueError:
238 except ValueError:
239 raise LfsRemoteError(_('LFS server returns invalid JSON: %s')
239 raise LfsRemoteError(_('LFS server returns invalid JSON: %s')
240 % rawjson)
240 % rawjson)
241
241
242 if self.ui.debugflag:
242 if self.ui.debugflag:
243 self.ui.debug('Status: %d\n' % rsp.status)
243 self.ui.debug('Status: %d\n' % rsp.status)
244 # lfs-test-server and hg serve return headers in different order
244 # lfs-test-server and hg serve return headers in different order
245 self.ui.debug('%s\n'
245 self.ui.debug('%s\n'
246 % '\n'.join(sorted(str(rsp.info()).splitlines())))
246 % '\n'.join(sorted(str(rsp.info()).splitlines())))
247
247
248 if 'objects' in response:
248 if 'objects' in response:
249 response['objects'] = sorted(response['objects'],
249 response['objects'] = sorted(response['objects'],
250 key=lambda p: p['oid'])
250 key=lambda p: p['oid'])
251 self.ui.debug('%s\n'
251 self.ui.debug('%s\n'
252 % json.dumps(response, indent=2,
252 % json.dumps(response, indent=2,
253 separators=('', ': '), sort_keys=True))
253 separators=('', ': '), sort_keys=True))
254
254
255 return response
255 return response
256
256
257 def _checkforservererror(self, pointers, responses, action):
257 def _checkforservererror(self, pointers, responses, action):
258 """Scans errors from objects
258 """Scans errors from objects
259
259
260 Raises LfsRemoteError if any objects have an error"""
260 Raises LfsRemoteError if any objects have an error"""
261 for response in responses:
261 for response in responses:
262 # The server should return 404 when objects cannot be found. Some
262 # The server should return 404 when objects cannot be found. Some
263 # server implementation (ex. lfs-test-server) does not set "error"
263 # server implementation (ex. lfs-test-server) does not set "error"
264 # but just removes "download" from "actions". Treat that case
264 # but just removes "download" from "actions". Treat that case
265 # as the same as 404 error.
265 # as the same as 404 error.
266 notfound = (response.get('error', {}).get('code') == 404
266 if 'error' not in response:
267 or (action == 'download'
267 if (action == 'download'
268 and action not in response.get('actions', [])))
268 and action not in response.get('actions', [])):
269 if notfound:
269 code = 404
270 ptrmap = {p.oid(): p for p in pointers}
271 p = ptrmap.get(response['oid'], None)
272 if p:
273 filename = getattr(p, 'filename', 'unknown')
274 raise LfsRemoteError(
275 _(('LFS server error. Remote object '
276 'for "%s" not found: %r')) % (filename, response))
277 else:
270 else:
278 raise LfsRemoteError(
271 continue
279 _('LFS server error. Unsolicited response for oid %s')
272 else:
280 % response['oid'])
273 # An error dict without a code doesn't make much sense, so
281 if 'error' in response:
274 # treat as a server error.
282 raise LfsRemoteError(_('LFS server error: %r') % response)
275 code = response.get('error').get('code', 500)
276
277 ptrmap = {p.oid(): p for p in pointers}
278 p = ptrmap.get(response['oid'], None)
279 if p:
280 filename = getattr(p, 'filename', 'unknown')
281 errors = {
282 404: 'The object does not exist',
283 410: 'The object was removed by the owner',
284 422: 'Validation error',
285 500: 'Internal server error',
286 }
287 msg = errors.get(code, 'status code %d' % code)
288 raise LfsRemoteError(_('LFS server error for "%s": %s')
289 % (filename, msg))
290 else:
291 raise LfsRemoteError(
292 _('LFS server error. Unsolicited response for oid %s')
293 % response['oid'])
283
294
284 def _extractobjects(self, response, pointers, action):
295 def _extractobjects(self, response, pointers, action):
285 """extract objects from response of the batch API
296 """extract objects from response of the batch API
286
297
287 response: parsed JSON object returned by batch API
298 response: parsed JSON object returned by batch API
288 return response['objects'] filtered by action
299 return response['objects'] filtered by action
289 raise if any object has an error
300 raise if any object has an error
290 """
301 """
291 # Scan errors from objects - fail early
302 # Scan errors from objects - fail early
292 objects = response.get('objects', [])
303 objects = response.get('objects', [])
293 self._checkforservererror(pointers, objects, action)
304 self._checkforservererror(pointers, objects, action)
294
305
295 # Filter objects with given action. Practically, this skips uploading
306 # Filter objects with given action. Practically, this skips uploading
296 # objects which exist in the server.
307 # objects which exist in the server.
297 filteredobjects = [o for o in objects if action in o.get('actions', [])]
308 filteredobjects = [o for o in objects if action in o.get('actions', [])]
298
309
299 return filteredobjects
310 return filteredobjects
300
311
301 def _basictransfer(self, obj, action, localstore):
312 def _basictransfer(self, obj, action, localstore):
302 """Download or upload a single object using basic transfer protocol
313 """Download or upload a single object using basic transfer protocol
303
314
304 obj: dict, an object description returned by batch API
315 obj: dict, an object description returned by batch API
305 action: string, one of ['upload', 'download']
316 action: string, one of ['upload', 'download']
306 localstore: blobstore.local
317 localstore: blobstore.local
307
318
308 See https://github.com/git-lfs/git-lfs/blob/master/docs/api/\
319 See https://github.com/git-lfs/git-lfs/blob/master/docs/api/\
309 basic-transfers.md
320 basic-transfers.md
310 """
321 """
311 oid = pycompat.bytestr(obj['oid'])
322 oid = pycompat.bytestr(obj['oid'])
312
323
313 href = pycompat.bytestr(obj['actions'][action].get('href'))
324 href = pycompat.bytestr(obj['actions'][action].get('href'))
314 headers = obj['actions'][action].get('header', {}).items()
325 headers = obj['actions'][action].get('header', {}).items()
315
326
316 request = util.urlreq.request(href)
327 request = util.urlreq.request(href)
317 if action == 'upload':
328 if action == 'upload':
318 # If uploading blobs, read data from local blobstore.
329 # If uploading blobs, read data from local blobstore.
319 if not localstore.verify(oid):
330 if not localstore.verify(oid):
320 raise error.Abort(_('detected corrupt lfs object: %s') % oid,
331 raise error.Abort(_('detected corrupt lfs object: %s') % oid,
321 hint=_('run hg verify'))
332 hint=_('run hg verify'))
322 request.data = filewithprogress(localstore.open(oid), None)
333 request.data = filewithprogress(localstore.open(oid), None)
323 request.get_method = lambda: 'PUT'
334 request.get_method = lambda: 'PUT'
324
335
325 for k, v in headers:
336 for k, v in headers:
326 request.add_header(k, v)
337 request.add_header(k, v)
327
338
328 response = b''
339 response = b''
329 try:
340 try:
330 req = self.urlopener.open(request)
341 req = self.urlopener.open(request)
331
342
332 if self.ui.debugflag:
343 if self.ui.debugflag:
333 self.ui.debug('Status: %d\n' % req.status)
344 self.ui.debug('Status: %d\n' % req.status)
334 # lfs-test-server and hg serve return headers in different order
345 # lfs-test-server and hg serve return headers in different order
335 self.ui.debug('%s\n'
346 self.ui.debug('%s\n'
336 % '\n'.join(sorted(str(req.info()).splitlines())))
347 % '\n'.join(sorted(str(req.info()).splitlines())))
337
348
338 if action == 'download':
349 if action == 'download':
339 # If downloading blobs, store downloaded data to local blobstore
350 # If downloading blobs, store downloaded data to local blobstore
340 localstore.download(oid, req)
351 localstore.download(oid, req)
341 else:
352 else:
342 while True:
353 while True:
343 data = req.read(1048576)
354 data = req.read(1048576)
344 if not data:
355 if not data:
345 break
356 break
346 response += data
357 response += data
347 if response:
358 if response:
348 self.ui.debug('lfs %s response: %s' % (action, response))
359 self.ui.debug('lfs %s response: %s' % (action, response))
349 except util.urlerr.httperror as ex:
360 except util.urlerr.httperror as ex:
350 if self.ui.debugflag:
361 if self.ui.debugflag:
351 self.ui.debug('%s: %s\n' % (oid, ex.read()))
362 self.ui.debug('%s: %s\n' % (oid, ex.read()))
352 raise LfsRemoteError(_('HTTP error: %s (oid=%s, action=%s)')
363 raise LfsRemoteError(_('HTTP error: %s (oid=%s, action=%s)')
353 % (ex, oid, action))
364 % (ex, oid, action))
354
365
355 def _batch(self, pointers, localstore, action):
366 def _batch(self, pointers, localstore, action):
356 if action not in ['upload', 'download']:
367 if action not in ['upload', 'download']:
357 raise error.ProgrammingError('invalid Git-LFS action: %s' % action)
368 raise error.ProgrammingError('invalid Git-LFS action: %s' % action)
358
369
359 response = self._batchrequest(pointers, action)
370 response = self._batchrequest(pointers, action)
360 objects = self._extractobjects(response, pointers, action)
371 objects = self._extractobjects(response, pointers, action)
361 total = sum(x.get('size', 0) for x in objects)
372 total = sum(x.get('size', 0) for x in objects)
362 sizes = {}
373 sizes = {}
363 for obj in objects:
374 for obj in objects:
364 sizes[obj.get('oid')] = obj.get('size', 0)
375 sizes[obj.get('oid')] = obj.get('size', 0)
365 topic = {'upload': _('lfs uploading'),
376 topic = {'upload': _('lfs uploading'),
366 'download': _('lfs downloading')}[action]
377 'download': _('lfs downloading')}[action]
367 if len(objects) > 1:
378 if len(objects) > 1:
368 self.ui.note(_('lfs: need to transfer %d objects (%s)\n')
379 self.ui.note(_('lfs: need to transfer %d objects (%s)\n')
369 % (len(objects), util.bytecount(total)))
380 % (len(objects), util.bytecount(total)))
370 self.ui.progress(topic, 0, total=total)
381 self.ui.progress(topic, 0, total=total)
371 def transfer(chunk):
382 def transfer(chunk):
372 for obj in chunk:
383 for obj in chunk:
373 objsize = obj.get('size', 0)
384 objsize = obj.get('size', 0)
374 if self.ui.verbose:
385 if self.ui.verbose:
375 if action == 'download':
386 if action == 'download':
376 msg = _('lfs: downloading %s (%s)\n')
387 msg = _('lfs: downloading %s (%s)\n')
377 elif action == 'upload':
388 elif action == 'upload':
378 msg = _('lfs: uploading %s (%s)\n')
389 msg = _('lfs: uploading %s (%s)\n')
379 self.ui.note(msg % (obj.get('oid'),
390 self.ui.note(msg % (obj.get('oid'),
380 util.bytecount(objsize)))
391 util.bytecount(objsize)))
381 retry = self.retry
392 retry = self.retry
382 while True:
393 while True:
383 try:
394 try:
384 self._basictransfer(obj, action, localstore)
395 self._basictransfer(obj, action, localstore)
385 yield 1, obj.get('oid')
396 yield 1, obj.get('oid')
386 break
397 break
387 except socket.error as ex:
398 except socket.error as ex:
388 if retry > 0:
399 if retry > 0:
389 self.ui.note(
400 self.ui.note(
390 _('lfs: failed: %r (remaining retry %d)\n')
401 _('lfs: failed: %r (remaining retry %d)\n')
391 % (ex, retry))
402 % (ex, retry))
392 retry -= 1
403 retry -= 1
393 continue
404 continue
394 raise
405 raise
395
406
396 # Until https multiplexing gets sorted out
407 # Until https multiplexing gets sorted out
397 if self.ui.configbool('experimental', 'lfs.worker-enable'):
408 if self.ui.configbool('experimental', 'lfs.worker-enable'):
398 oids = worker.worker(self.ui, 0.1, transfer, (),
409 oids = worker.worker(self.ui, 0.1, transfer, (),
399 sorted(objects, key=lambda o: o.get('oid')))
410 sorted(objects, key=lambda o: o.get('oid')))
400 else:
411 else:
401 oids = transfer(sorted(objects, key=lambda o: o.get('oid')))
412 oids = transfer(sorted(objects, key=lambda o: o.get('oid')))
402
413
403 processed = 0
414 processed = 0
404 blobs = 0
415 blobs = 0
405 for _one, oid in oids:
416 for _one, oid in oids:
406 processed += sizes[oid]
417 processed += sizes[oid]
407 blobs += 1
418 blobs += 1
408 self.ui.progress(topic, processed, total=total)
419 self.ui.progress(topic, processed, total=total)
409 self.ui.note(_('lfs: processed: %s\n') % oid)
420 self.ui.note(_('lfs: processed: %s\n') % oid)
410 self.ui.progress(topic, pos=None, total=total)
421 self.ui.progress(topic, pos=None, total=total)
411
422
412 if blobs > 0:
423 if blobs > 0:
413 if action == 'upload':
424 if action == 'upload':
414 self.ui.status(_('lfs: uploaded %d files (%s)\n')
425 self.ui.status(_('lfs: uploaded %d files (%s)\n')
415 % (blobs, util.bytecount(processed)))
426 % (blobs, util.bytecount(processed)))
416 # TODO: coalesce the download requests, and comment this in
427 # TODO: coalesce the download requests, and comment this in
417 #elif action == 'download':
428 #elif action == 'download':
418 # self.ui.status(_('lfs: downloaded %d files (%s)\n')
429 # self.ui.status(_('lfs: downloaded %d files (%s)\n')
419 # % (blobs, util.bytecount(processed)))
430 # % (blobs, util.bytecount(processed)))
420
431
421 def __del__(self):
432 def __del__(self):
422 # copied from mercurial/httppeer.py
433 # copied from mercurial/httppeer.py
423 urlopener = getattr(self, 'urlopener', None)
434 urlopener = getattr(self, 'urlopener', None)
424 if urlopener:
435 if urlopener:
425 for h in urlopener.handlers:
436 for h in urlopener.handlers:
426 h.close()
437 h.close()
427 getattr(h, "close_all", lambda : None)()
438 getattr(h, "close_all", lambda : None)()
428
439
429 class _dummyremote(object):
440 class _dummyremote(object):
430 """Dummy store storing blobs to temp directory."""
441 """Dummy store storing blobs to temp directory."""
431
442
432 def __init__(self, repo, url):
443 def __init__(self, repo, url):
433 fullpath = repo.vfs.join('lfs', url.path)
444 fullpath = repo.vfs.join('lfs', url.path)
434 self.vfs = lfsvfs(fullpath)
445 self.vfs = lfsvfs(fullpath)
435
446
436 def writebatch(self, pointers, fromstore):
447 def writebatch(self, pointers, fromstore):
437 for p in _deduplicate(pointers):
448 for p in _deduplicate(pointers):
438 content = fromstore.read(p.oid(), verify=True)
449 content = fromstore.read(p.oid(), verify=True)
439 with self.vfs(p.oid(), 'wb', atomictemp=True) as fp:
450 with self.vfs(p.oid(), 'wb', atomictemp=True) as fp:
440 fp.write(content)
451 fp.write(content)
441
452
442 def readbatch(self, pointers, tostore):
453 def readbatch(self, pointers, tostore):
443 for p in _deduplicate(pointers):
454 for p in _deduplicate(pointers):
444 with self.vfs(p.oid(), 'rb') as fp:
455 with self.vfs(p.oid(), 'rb') as fp:
445 tostore.download(p.oid(), fp)
456 tostore.download(p.oid(), fp)
446
457
447 class _nullremote(object):
458 class _nullremote(object):
448 """Null store storing blobs to /dev/null."""
459 """Null store storing blobs to /dev/null."""
449
460
450 def __init__(self, repo, url):
461 def __init__(self, repo, url):
451 pass
462 pass
452
463
453 def writebatch(self, pointers, fromstore):
464 def writebatch(self, pointers, fromstore):
454 pass
465 pass
455
466
456 def readbatch(self, pointers, tostore):
467 def readbatch(self, pointers, tostore):
457 pass
468 pass
458
469
459 class _promptremote(object):
470 class _promptremote(object):
460 """Prompt user to set lfs.url when accessed."""
471 """Prompt user to set lfs.url when accessed."""
461
472
462 def __init__(self, repo, url):
473 def __init__(self, repo, url):
463 pass
474 pass
464
475
465 def writebatch(self, pointers, fromstore, ui=None):
476 def writebatch(self, pointers, fromstore, ui=None):
466 self._prompt()
477 self._prompt()
467
478
468 def readbatch(self, pointers, tostore, ui=None):
479 def readbatch(self, pointers, tostore, ui=None):
469 self._prompt()
480 self._prompt()
470
481
471 def _prompt(self):
482 def _prompt(self):
472 raise error.Abort(_('lfs.url needs to be configured'))
483 raise error.Abort(_('lfs.url needs to be configured'))
473
484
474 _storemap = {
485 _storemap = {
475 'https': _gitlfsremote,
486 'https': _gitlfsremote,
476 'http': _gitlfsremote,
487 'http': _gitlfsremote,
477 'file': _dummyremote,
488 'file': _dummyremote,
478 'null': _nullremote,
489 'null': _nullremote,
479 None: _promptremote,
490 None: _promptremote,
480 }
491 }
481
492
482 def _deduplicate(pointers):
493 def _deduplicate(pointers):
483 """Remove any duplicate oids that exist in the list"""
494 """Remove any duplicate oids that exist in the list"""
484 reduced = util.sortdict()
495 reduced = util.sortdict()
485 for p in pointers:
496 for p in pointers:
486 reduced[p.oid()] = p
497 reduced[p.oid()] = p
487 return reduced.values()
498 return reduced.values()
488
499
489 def _verify(oid, content):
500 def _verify(oid, content):
490 realoid = hashlib.sha256(content).hexdigest()
501 realoid = hashlib.sha256(content).hexdigest()
491 if realoid != oid:
502 if realoid != oid:
492 raise error.Abort(_('detected corrupt lfs object: %s') % oid,
503 raise error.Abort(_('detected corrupt lfs object: %s') % oid,
493 hint=_('run hg verify'))
504 hint=_('run hg verify'))
494
505
495 def remote(repo):
506 def remote(repo):
496 """remotestore factory. return a store in _storemap depending on config"""
507 """remotestore factory. return a store in _storemap depending on config"""
497 url = util.url(repo.ui.config('lfs', 'url') or '')
508 url = util.url(repo.ui.config('lfs', 'url') or '')
498 scheme = url.scheme
509 scheme = url.scheme
499 if scheme not in _storemap:
510 if scheme not in _storemap:
500 raise error.Abort(_('lfs: unknown url scheme: %s') % scheme)
511 raise error.Abort(_('lfs: unknown url scheme: %s') % scheme)
501 return _storemap[scheme](repo, url)
512 return _storemap[scheme](repo, url)
502
513
503 class LfsRemoteError(error.RevlogError):
514 class LfsRemoteError(error.RevlogError):
504 pass
515 pass
@@ -1,924 +1,924 b''
1 #testcases git-server hg-server
1 #testcases git-server hg-server
2
2
3 #if git-server
3 #if git-server
4 #require lfs-test-server
4 #require lfs-test-server
5 #else
5 #else
6 #require serve
6 #require serve
7 #endif
7 #endif
8
8
9 #if git-server
9 #if git-server
10 $ LFS_LISTEN="tcp://:$HGPORT"
10 $ LFS_LISTEN="tcp://:$HGPORT"
11 $ LFS_HOST="localhost:$HGPORT"
11 $ LFS_HOST="localhost:$HGPORT"
12 $ LFS_PUBLIC=1
12 $ LFS_PUBLIC=1
13 $ export LFS_LISTEN LFS_HOST LFS_PUBLIC
13 $ export LFS_LISTEN LFS_HOST LFS_PUBLIC
14 #else
14 #else
15 $ LFS_HOST="localhost:$HGPORT/.git/info/lfs"
15 $ LFS_HOST="localhost:$HGPORT/.git/info/lfs"
16 #endif
16 #endif
17
17
18 #if no-windows git-server
18 #if no-windows git-server
19 $ lfs-test-server &> lfs-server.log &
19 $ lfs-test-server &> lfs-server.log &
20 $ echo $! >> $DAEMON_PIDS
20 $ echo $! >> $DAEMON_PIDS
21 #endif
21 #endif
22
22
23 #if windows git-server
23 #if windows git-server
24 $ cat >> $TESTTMP/spawn.py <<EOF
24 $ cat >> $TESTTMP/spawn.py <<EOF
25 > import os
25 > import os
26 > import subprocess
26 > import subprocess
27 > import sys
27 > import sys
28 >
28 >
29 > for path in os.environ["PATH"].split(os.pathsep):
29 > for path in os.environ["PATH"].split(os.pathsep):
30 > exe = os.path.join(path, 'lfs-test-server.exe')
30 > exe = os.path.join(path, 'lfs-test-server.exe')
31 > if os.path.exists(exe):
31 > if os.path.exists(exe):
32 > with open('lfs-server.log', 'wb') as out:
32 > with open('lfs-server.log', 'wb') as out:
33 > p = subprocess.Popen(exe, stdout=out, stderr=out)
33 > p = subprocess.Popen(exe, stdout=out, stderr=out)
34 > sys.stdout.write('%s\n' % p.pid)
34 > sys.stdout.write('%s\n' % p.pid)
35 > sys.exit(0)
35 > sys.exit(0)
36 > sys.exit(1)
36 > sys.exit(1)
37 > EOF
37 > EOF
38 $ $PYTHON $TESTTMP/spawn.py >> $DAEMON_PIDS
38 $ $PYTHON $TESTTMP/spawn.py >> $DAEMON_PIDS
39 #endif
39 #endif
40
40
41 $ cat >> $HGRCPATH <<EOF
41 $ cat >> $HGRCPATH <<EOF
42 > [extensions]
42 > [extensions]
43 > lfs=
43 > lfs=
44 > [lfs]
44 > [lfs]
45 > url=http://foo:bar@$LFS_HOST
45 > url=http://foo:bar@$LFS_HOST
46 > track=all()
46 > track=all()
47 > [web]
47 > [web]
48 > push_ssl = False
48 > push_ssl = False
49 > allow-push = *
49 > allow-push = *
50 > EOF
50 > EOF
51
51
52 Use a separate usercache, otherwise the server sees what the client commits, and
52 Use a separate usercache, otherwise the server sees what the client commits, and
53 never requests a transfer.
53 never requests a transfer.
54
54
55 #if hg-server
55 #if hg-server
56 $ hg init server
56 $ hg init server
57 $ hg --config "lfs.usercache=$TESTTMP/servercache" -R server serve -d \
57 $ hg --config "lfs.usercache=$TESTTMP/servercache" -R server serve -d \
58 > -p $HGPORT --pid-file=hg.pid -A $TESTTMP/access.log -E $TESTTMP/errors.log
58 > -p $HGPORT --pid-file=hg.pid -A $TESTTMP/access.log -E $TESTTMP/errors.log
59 $ cat hg.pid >> $DAEMON_PIDS
59 $ cat hg.pid >> $DAEMON_PIDS
60 #endif
60 #endif
61
61
62 $ hg init repo1
62 $ hg init repo1
63 $ cd repo1
63 $ cd repo1
64 $ echo THIS-IS-LFS > a
64 $ echo THIS-IS-LFS > a
65 $ hg commit -m a -A a
65 $ hg commit -m a -A a
66
66
67 A push can be serviced directly from the usercache if it isn't in the local
67 A push can be serviced directly from the usercache if it isn't in the local
68 store.
68 store.
69
69
70 $ hg init ../repo2
70 $ hg init ../repo2
71 $ mv .hg/store/lfs .hg/store/lfs_
71 $ mv .hg/store/lfs .hg/store/lfs_
72 $ hg push ../repo2 --debug
72 $ hg push ../repo2 --debug
73 http auth: user foo, password ***
73 http auth: user foo, password ***
74 pushing to ../repo2
74 pushing to ../repo2
75 http auth: user foo, password ***
75 http auth: user foo, password ***
76 query 1; heads
76 query 1; heads
77 searching for changes
77 searching for changes
78 1 total queries in *s (glob)
78 1 total queries in *s (glob)
79 listing keys for "phases"
79 listing keys for "phases"
80 checking for updated bookmarks
80 checking for updated bookmarks
81 listing keys for "bookmarks"
81 listing keys for "bookmarks"
82 lfs: computing set of blobs to upload
82 lfs: computing set of blobs to upload
83 Status: 200
83 Status: 200
84 Content-Length: 309 (git-server !)
84 Content-Length: 309 (git-server !)
85 Content-Length: 350 (hg-server !)
85 Content-Length: 350 (hg-server !)
86 Content-Type: application/vnd.git-lfs+json
86 Content-Type: application/vnd.git-lfs+json
87 Date: $HTTP_DATE$
87 Date: $HTTP_DATE$
88 Server: testing stub value (hg-server !)
88 Server: testing stub value (hg-server !)
89 {
89 {
90 "objects": [
90 "objects": [
91 {
91 {
92 "actions": {
92 "actions": {
93 "upload": {
93 "upload": {
94 "expires_at": "$ISO_8601_DATE_TIME$"
94 "expires_at": "$ISO_8601_DATE_TIME$"
95 "header": {
95 "header": {
96 "Accept": "application/vnd.git-lfs"
96 "Accept": "application/vnd.git-lfs"
97 }
97 }
98 "href": "http://localhost:$HGPORT/objects/31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b" (git-server !)
98 "href": "http://localhost:$HGPORT/objects/31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b" (git-server !)
99 "href": "http://localhost:$HGPORT/.hg/lfs/objects/31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b" (hg-server !)
99 "href": "http://localhost:$HGPORT/.hg/lfs/objects/31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b" (hg-server !)
100 }
100 }
101 }
101 }
102 "oid": "31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b"
102 "oid": "31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b"
103 "size": 12
103 "size": 12
104 }
104 }
105 ]
105 ]
106 "transfer": "basic" (hg-server !)
106 "transfer": "basic" (hg-server !)
107 }
107 }
108 lfs: uploading 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b (12 bytes)
108 lfs: uploading 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b (12 bytes)
109 Status: 200 (git-server !)
109 Status: 200 (git-server !)
110 Status: 201 (hg-server !)
110 Status: 201 (hg-server !)
111 Content-Length: 0
111 Content-Length: 0
112 Content-Type: text/plain; charset=utf-8
112 Content-Type: text/plain; charset=utf-8
113 Date: $HTTP_DATE$
113 Date: $HTTP_DATE$
114 Server: testing stub value (hg-server !)
114 Server: testing stub value (hg-server !)
115 lfs: processed: 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b
115 lfs: processed: 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b
116 lfs: uploaded 1 files (12 bytes)
116 lfs: uploaded 1 files (12 bytes)
117 1 changesets found
117 1 changesets found
118 list of changesets:
118 list of changesets:
119 99a7098854a3984a5c9eab0fc7a2906697b7cb5c
119 99a7098854a3984a5c9eab0fc7a2906697b7cb5c
120 bundle2-output-bundle: "HG20", 4 parts total
120 bundle2-output-bundle: "HG20", 4 parts total
121 bundle2-output-part: "replycaps" * bytes payload (glob)
121 bundle2-output-part: "replycaps" * bytes payload (glob)
122 bundle2-output-part: "check:heads" streamed payload
122 bundle2-output-part: "check:heads" streamed payload
123 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
123 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
124 bundle2-output-part: "phase-heads" 24 bytes payload
124 bundle2-output-part: "phase-heads" 24 bytes payload
125 bundle2-input-bundle: with-transaction
125 bundle2-input-bundle: with-transaction
126 bundle2-input-part: "replycaps" supported
126 bundle2-input-part: "replycaps" supported
127 bundle2-input-part: total payload size * (glob)
127 bundle2-input-part: total payload size * (glob)
128 bundle2-input-part: "check:heads" supported
128 bundle2-input-part: "check:heads" supported
129 bundle2-input-part: total payload size 20
129 bundle2-input-part: total payload size 20
130 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
130 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
131 adding changesets
131 adding changesets
132 add changeset 99a7098854a3
132 add changeset 99a7098854a3
133 adding manifests
133 adding manifests
134 adding file changes
134 adding file changes
135 adding a revisions
135 adding a revisions
136 added 1 changesets with 1 changes to 1 files
136 added 1 changesets with 1 changes to 1 files
137 calling hook pretxnchangegroup.lfs: hgext.lfs.checkrequireslfs
137 calling hook pretxnchangegroup.lfs: hgext.lfs.checkrequireslfs
138 bundle2-input-part: total payload size 617
138 bundle2-input-part: total payload size 617
139 bundle2-input-part: "phase-heads" supported
139 bundle2-input-part: "phase-heads" supported
140 bundle2-input-part: total payload size 24
140 bundle2-input-part: total payload size 24
141 bundle2-input-bundle: 3 parts total
141 bundle2-input-bundle: 3 parts total
142 updating the branch cache
142 updating the branch cache
143 bundle2-output-bundle: "HG20", 1 parts total
143 bundle2-output-bundle: "HG20", 1 parts total
144 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
144 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
145 bundle2-input-bundle: no-transaction
145 bundle2-input-bundle: no-transaction
146 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
146 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
147 bundle2-input-bundle: 0 parts total
147 bundle2-input-bundle: 0 parts total
148 listing keys for "phases"
148 listing keys for "phases"
149 $ mv .hg/store/lfs_ .hg/store/lfs
149 $ mv .hg/store/lfs_ .hg/store/lfs
150
150
151 Clear the cache to force a download
151 Clear the cache to force a download
152 $ rm -rf `hg config lfs.usercache`
152 $ rm -rf `hg config lfs.usercache`
153 $ cd ../repo2
153 $ cd ../repo2
154 $ hg update tip --debug
154 $ hg update tip --debug
155 http auth: user foo, password ***
155 http auth: user foo, password ***
156 resolving manifests
156 resolving manifests
157 branchmerge: False, force: False, partial: False
157 branchmerge: False, force: False, partial: False
158 ancestor: 000000000000, local: 000000000000+, remote: 99a7098854a3
158 ancestor: 000000000000, local: 000000000000+, remote: 99a7098854a3
159 Status: 200
159 Status: 200
160 Content-Length: 311 (git-server !)
160 Content-Length: 311 (git-server !)
161 Content-Length: 352 (hg-server !)
161 Content-Length: 352 (hg-server !)
162 Content-Type: application/vnd.git-lfs+json
162 Content-Type: application/vnd.git-lfs+json
163 Date: $HTTP_DATE$
163 Date: $HTTP_DATE$
164 Server: testing stub value (hg-server !)
164 Server: testing stub value (hg-server !)
165 {
165 {
166 "objects": [
166 "objects": [
167 {
167 {
168 "actions": {
168 "actions": {
169 "download": {
169 "download": {
170 "expires_at": "$ISO_8601_DATE_TIME$"
170 "expires_at": "$ISO_8601_DATE_TIME$"
171 "header": {
171 "header": {
172 "Accept": "application/vnd.git-lfs"
172 "Accept": "application/vnd.git-lfs"
173 }
173 }
174 "href": "http://localhost:$HGPORT/*/31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b" (glob)
174 "href": "http://localhost:$HGPORT/*/31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b" (glob)
175 }
175 }
176 }
176 }
177 "oid": "31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b"
177 "oid": "31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b"
178 "size": 12
178 "size": 12
179 }
179 }
180 ]
180 ]
181 "transfer": "basic" (hg-server !)
181 "transfer": "basic" (hg-server !)
182 }
182 }
183 lfs: downloading 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b (12 bytes)
183 lfs: downloading 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b (12 bytes)
184 Status: 200
184 Status: 200
185 Content-Length: 12
185 Content-Length: 12
186 Content-Type: text/plain; charset=utf-8 (git-server !)
186 Content-Type: text/plain; charset=utf-8 (git-server !)
187 Content-Type: application/octet-stream (hg-server !)
187 Content-Type: application/octet-stream (hg-server !)
188 Date: $HTTP_DATE$
188 Date: $HTTP_DATE$
189 Server: testing stub value (hg-server !)
189 Server: testing stub value (hg-server !)
190 lfs: adding 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b to the usercache
190 lfs: adding 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b to the usercache
191 lfs: processed: 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b
191 lfs: processed: 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b
192 a: remote created -> g
192 a: remote created -> g
193 getting a
193 getting a
194 lfs: found 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b in the local lfs store
194 lfs: found 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b in the local lfs store
195 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
195 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
196
196
197 When the server has some blobs already. `hg serve` doesn't offer to upload
197 When the server has some blobs already. `hg serve` doesn't offer to upload
198 blobs that it already knows about. Note that lfs-test-server is simply
198 blobs that it already knows about. Note that lfs-test-server is simply
199 toggling the action to 'download'. The Batch API spec says it should omit the
199 toggling the action to 'download'. The Batch API spec says it should omit the
200 actions property completely.
200 actions property completely.
201
201
202 $ hg mv a b
202 $ hg mv a b
203 $ echo ANOTHER-LARGE-FILE > c
203 $ echo ANOTHER-LARGE-FILE > c
204 $ echo ANOTHER-LARGE-FILE2 > d
204 $ echo ANOTHER-LARGE-FILE2 > d
205 $ hg commit -m b-and-c -A b c d
205 $ hg commit -m b-and-c -A b c d
206 $ hg push ../repo1 --debug
206 $ hg push ../repo1 --debug
207 http auth: user foo, password ***
207 http auth: user foo, password ***
208 pushing to ../repo1
208 pushing to ../repo1
209 http auth: user foo, password ***
209 http auth: user foo, password ***
210 query 1; heads
210 query 1; heads
211 searching for changes
211 searching for changes
212 all remote heads known locally
212 all remote heads known locally
213 listing keys for "phases"
213 listing keys for "phases"
214 checking for updated bookmarks
214 checking for updated bookmarks
215 listing keys for "bookmarks"
215 listing keys for "bookmarks"
216 listing keys for "bookmarks"
216 listing keys for "bookmarks"
217 lfs: computing set of blobs to upload
217 lfs: computing set of blobs to upload
218 Status: 200
218 Status: 200
219 Content-Length: 901 (git-server !)
219 Content-Length: 901 (git-server !)
220 Content-Length: 755 (hg-server !)
220 Content-Length: 755 (hg-server !)
221 Content-Type: application/vnd.git-lfs+json
221 Content-Type: application/vnd.git-lfs+json
222 Date: $HTTP_DATE$
222 Date: $HTTP_DATE$
223 Server: testing stub value (hg-server !)
223 Server: testing stub value (hg-server !)
224 {
224 {
225 "objects": [
225 "objects": [
226 {
226 {
227 "actions": { (git-server !)
227 "actions": { (git-server !)
228 "download": { (git-server !)
228 "download": { (git-server !)
229 "expires_at": "$ISO_8601_DATE_TIME$" (git-server !)
229 "expires_at": "$ISO_8601_DATE_TIME$" (git-server !)
230 "header": { (git-server !)
230 "header": { (git-server !)
231 "Accept": "application/vnd.git-lfs" (git-server !)
231 "Accept": "application/vnd.git-lfs" (git-server !)
232 } (git-server !)
232 } (git-server !)
233 "href": "http://localhost:$HGPORT/objects/31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b" (git-server !)
233 "href": "http://localhost:$HGPORT/objects/31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b" (git-server !)
234 } (git-server !)
234 } (git-server !)
235 } (git-server !)
235 } (git-server !)
236 "oid": "31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b"
236 "oid": "31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b"
237 "size": 12
237 "size": 12
238 }
238 }
239 {
239 {
240 "actions": {
240 "actions": {
241 "upload": {
241 "upload": {
242 "expires_at": "$ISO_8601_DATE_TIME$"
242 "expires_at": "$ISO_8601_DATE_TIME$"
243 "header": {
243 "header": {
244 "Accept": "application/vnd.git-lfs"
244 "Accept": "application/vnd.git-lfs"
245 }
245 }
246 "href": "http://localhost:$HGPORT/*/37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19" (glob)
246 "href": "http://localhost:$HGPORT/*/37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19" (glob)
247 }
247 }
248 }
248 }
249 "oid": "37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19"
249 "oid": "37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19"
250 "size": 20
250 "size": 20
251 }
251 }
252 {
252 {
253 "actions": {
253 "actions": {
254 "upload": {
254 "upload": {
255 "expires_at": "$ISO_8601_DATE_TIME$"
255 "expires_at": "$ISO_8601_DATE_TIME$"
256 "header": {
256 "header": {
257 "Accept": "application/vnd.git-lfs"
257 "Accept": "application/vnd.git-lfs"
258 }
258 }
259 "href": "http://localhost:$HGPORT/*/d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998" (glob)
259 "href": "http://localhost:$HGPORT/*/d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998" (glob)
260 }
260 }
261 }
261 }
262 "oid": "d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998"
262 "oid": "d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998"
263 "size": 19
263 "size": 19
264 }
264 }
265 ]
265 ]
266 "transfer": "basic" (hg-server !)
266 "transfer": "basic" (hg-server !)
267 }
267 }
268 lfs: need to transfer 2 objects (39 bytes)
268 lfs: need to transfer 2 objects (39 bytes)
269 lfs: uploading 37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 (20 bytes)
269 lfs: uploading 37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 (20 bytes)
270 Status: 200 (git-server !)
270 Status: 200 (git-server !)
271 Status: 201 (hg-server !)
271 Status: 201 (hg-server !)
272 Content-Length: 0
272 Content-Length: 0
273 Content-Type: text/plain; charset=utf-8
273 Content-Type: text/plain; charset=utf-8
274 Date: $HTTP_DATE$
274 Date: $HTTP_DATE$
275 Server: testing stub value (hg-server !)
275 Server: testing stub value (hg-server !)
276 lfs: processed: 37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19
276 lfs: processed: 37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19
277 lfs: uploading d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 (19 bytes)
277 lfs: uploading d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 (19 bytes)
278 Status: 200 (git-server !)
278 Status: 200 (git-server !)
279 Status: 201 (hg-server !)
279 Status: 201 (hg-server !)
280 Content-Length: 0
280 Content-Length: 0
281 Content-Type: text/plain; charset=utf-8
281 Content-Type: text/plain; charset=utf-8
282 Date: $HTTP_DATE$
282 Date: $HTTP_DATE$
283 Server: testing stub value (hg-server !)
283 Server: testing stub value (hg-server !)
284 lfs: processed: d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
284 lfs: processed: d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
285 lfs: uploaded 2 files (39 bytes)
285 lfs: uploaded 2 files (39 bytes)
286 1 changesets found
286 1 changesets found
287 list of changesets:
287 list of changesets:
288 dfca2c9e2ef24996aa61ba2abd99277d884b3d63
288 dfca2c9e2ef24996aa61ba2abd99277d884b3d63
289 bundle2-output-bundle: "HG20", 5 parts total
289 bundle2-output-bundle: "HG20", 5 parts total
290 bundle2-output-part: "replycaps" * bytes payload (glob)
290 bundle2-output-part: "replycaps" * bytes payload (glob)
291 bundle2-output-part: "check:phases" 24 bytes payload
291 bundle2-output-part: "check:phases" 24 bytes payload
292 bundle2-output-part: "check:heads" streamed payload
292 bundle2-output-part: "check:heads" streamed payload
293 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
293 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
294 bundle2-output-part: "phase-heads" 24 bytes payload
294 bundle2-output-part: "phase-heads" 24 bytes payload
295 bundle2-input-bundle: with-transaction
295 bundle2-input-bundle: with-transaction
296 bundle2-input-part: "replycaps" supported
296 bundle2-input-part: "replycaps" supported
297 bundle2-input-part: total payload size * (glob)
297 bundle2-input-part: total payload size * (glob)
298 bundle2-input-part: "check:phases" supported
298 bundle2-input-part: "check:phases" supported
299 bundle2-input-part: total payload size 24
299 bundle2-input-part: total payload size 24
300 bundle2-input-part: "check:heads" supported
300 bundle2-input-part: "check:heads" supported
301 bundle2-input-part: total payload size 20
301 bundle2-input-part: total payload size 20
302 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
302 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
303 adding changesets
303 adding changesets
304 add changeset dfca2c9e2ef2
304 add changeset dfca2c9e2ef2
305 adding manifests
305 adding manifests
306 adding file changes
306 adding file changes
307 adding b revisions
307 adding b revisions
308 adding c revisions
308 adding c revisions
309 adding d revisions
309 adding d revisions
310 added 1 changesets with 3 changes to 3 files
310 added 1 changesets with 3 changes to 3 files
311 bundle2-input-part: total payload size 1315
311 bundle2-input-part: total payload size 1315
312 bundle2-input-part: "phase-heads" supported
312 bundle2-input-part: "phase-heads" supported
313 bundle2-input-part: total payload size 24
313 bundle2-input-part: total payload size 24
314 bundle2-input-bundle: 4 parts total
314 bundle2-input-bundle: 4 parts total
315 updating the branch cache
315 updating the branch cache
316 bundle2-output-bundle: "HG20", 1 parts total
316 bundle2-output-bundle: "HG20", 1 parts total
317 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
317 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
318 bundle2-input-bundle: no-transaction
318 bundle2-input-bundle: no-transaction
319 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
319 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
320 bundle2-input-bundle: 0 parts total
320 bundle2-input-bundle: 0 parts total
321 listing keys for "phases"
321 listing keys for "phases"
322
322
323 Clear the cache to force a download
323 Clear the cache to force a download
324 $ rm -rf `hg config lfs.usercache`
324 $ rm -rf `hg config lfs.usercache`
325 $ hg --repo ../repo1 update tip --debug
325 $ hg --repo ../repo1 update tip --debug
326 http auth: user foo, password ***
326 http auth: user foo, password ***
327 resolving manifests
327 resolving manifests
328 branchmerge: False, force: False, partial: False
328 branchmerge: False, force: False, partial: False
329 ancestor: 99a7098854a3, local: 99a7098854a3+, remote: dfca2c9e2ef2
329 ancestor: 99a7098854a3, local: 99a7098854a3+, remote: dfca2c9e2ef2
330 Status: 200
330 Status: 200
331 Content-Length: 608 (git-server !)
331 Content-Length: 608 (git-server !)
332 Content-Length: 670 (hg-server !)
332 Content-Length: 670 (hg-server !)
333 Content-Type: application/vnd.git-lfs+json
333 Content-Type: application/vnd.git-lfs+json
334 Date: $HTTP_DATE$
334 Date: $HTTP_DATE$
335 Server: testing stub value (hg-server !)
335 Server: testing stub value (hg-server !)
336 {
336 {
337 "objects": [
337 "objects": [
338 {
338 {
339 "actions": {
339 "actions": {
340 "download": {
340 "download": {
341 "expires_at": "$ISO_8601_DATE_TIME$"
341 "expires_at": "$ISO_8601_DATE_TIME$"
342 "header": {
342 "header": {
343 "Accept": "application/vnd.git-lfs"
343 "Accept": "application/vnd.git-lfs"
344 }
344 }
345 "href": "http://localhost:$HGPORT/*/37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19" (glob)
345 "href": "http://localhost:$HGPORT/*/37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19" (glob)
346 }
346 }
347 }
347 }
348 "oid": "37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19"
348 "oid": "37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19"
349 "size": 20
349 "size": 20
350 }
350 }
351 {
351 {
352 "actions": {
352 "actions": {
353 "download": {
353 "download": {
354 "expires_at": "$ISO_8601_DATE_TIME$"
354 "expires_at": "$ISO_8601_DATE_TIME$"
355 "header": {
355 "header": {
356 "Accept": "application/vnd.git-lfs"
356 "Accept": "application/vnd.git-lfs"
357 }
357 }
358 "href": "http://localhost:$HGPORT/*/d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998" (glob)
358 "href": "http://localhost:$HGPORT/*/d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998" (glob)
359 }
359 }
360 }
360 }
361 "oid": "d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998"
361 "oid": "d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998"
362 "size": 19
362 "size": 19
363 }
363 }
364 ]
364 ]
365 "transfer": "basic" (hg-server !)
365 "transfer": "basic" (hg-server !)
366 }
366 }
367 lfs: need to transfer 2 objects (39 bytes)
367 lfs: need to transfer 2 objects (39 bytes)
368 lfs: downloading 37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 (20 bytes)
368 lfs: downloading 37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 (20 bytes)
369 Status: 200
369 Status: 200
370 Content-Length: 20
370 Content-Length: 20
371 Content-Type: text/plain; charset=utf-8 (git-server !)
371 Content-Type: text/plain; charset=utf-8 (git-server !)
372 Content-Type: application/octet-stream (hg-server !)
372 Content-Type: application/octet-stream (hg-server !)
373 Date: $HTTP_DATE$
373 Date: $HTTP_DATE$
374 Server: testing stub value (hg-server !)
374 Server: testing stub value (hg-server !)
375 lfs: adding 37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 to the usercache
375 lfs: adding 37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 to the usercache
376 lfs: processed: 37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19
376 lfs: processed: 37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19
377 lfs: downloading d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 (19 bytes)
377 lfs: downloading d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 (19 bytes)
378 Status: 200
378 Status: 200
379 Content-Length: 19
379 Content-Length: 19
380 Content-Type: text/plain; charset=utf-8 (git-server !)
380 Content-Type: text/plain; charset=utf-8 (git-server !)
381 Content-Type: application/octet-stream (hg-server !)
381 Content-Type: application/octet-stream (hg-server !)
382 Date: $HTTP_DATE$
382 Date: $HTTP_DATE$
383 Server: testing stub value (hg-server !)
383 Server: testing stub value (hg-server !)
384 lfs: adding d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 to the usercache
384 lfs: adding d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 to the usercache
385 lfs: processed: d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
385 lfs: processed: d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
386 b: remote created -> g
386 b: remote created -> g
387 getting b
387 getting b
388 lfs: found 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b in the local lfs store
388 lfs: found 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b in the local lfs store
389 c: remote created -> g
389 c: remote created -> g
390 getting c
390 getting c
391 lfs: found d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 in the local lfs store
391 lfs: found d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 in the local lfs store
392 d: remote created -> g
392 d: remote created -> g
393 getting d
393 getting d
394 lfs: found 37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 in the local lfs store
394 lfs: found 37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 in the local lfs store
395 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
395 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
396
396
397 Test a corrupt file download, but clear the cache first to force a download.
397 Test a corrupt file download, but clear the cache first to force a download.
398 `hg serve` indicates a corrupt file without transferring it, unlike
398 `hg serve` indicates a corrupt file without transferring it, unlike
399 lfs-test-server.
399 lfs-test-server.
400
400
401 $ rm -rf `hg config lfs.usercache`
401 $ rm -rf `hg config lfs.usercache`
402 #if git-server
402 #if git-server
403 $ cp $TESTTMP/lfs-content/d1/1e/1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 blob
403 $ cp $TESTTMP/lfs-content/d1/1e/1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 blob
404 $ echo 'damage' > $TESTTMP/lfs-content/d1/1e/1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
404 $ echo 'damage' > $TESTTMP/lfs-content/d1/1e/1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
405 #else
405 #else
406 $ cp $TESTTMP/server/.hg/store/lfs/objects/d1/1e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 blob
406 $ cp $TESTTMP/server/.hg/store/lfs/objects/d1/1e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 blob
407 $ echo 'damage' > $TESTTMP/server/.hg/store/lfs/objects/d1/1e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
407 $ echo 'damage' > $TESTTMP/server/.hg/store/lfs/objects/d1/1e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
408 #endif
408 #endif
409 $ rm ../repo1/.hg/store/lfs/objects/d1/1e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
409 $ rm ../repo1/.hg/store/lfs/objects/d1/1e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
410 $ rm ../repo1/*
410 $ rm ../repo1/*
411
411
412 TODO: give the proper error indication from `hg serve`
412 TODO: give the proper error indication from `hg serve`
413
413
414 $ hg --repo ../repo1 update -C tip --debug
414 $ hg --repo ../repo1 update -C tip --debug
415 http auth: user foo, password ***
415 http auth: user foo, password ***
416 resolving manifests
416 resolving manifests
417 branchmerge: False, force: True, partial: False
417 branchmerge: False, force: True, partial: False
418 ancestor: dfca2c9e2ef2+, local: dfca2c9e2ef2+, remote: dfca2c9e2ef2
418 ancestor: dfca2c9e2ef2+, local: dfca2c9e2ef2+, remote: dfca2c9e2ef2
419 Status: 200
419 Status: 200
420 Content-Length: 311 (git-server !)
420 Content-Length: 311 (git-server !)
421 Content-Length: 183 (hg-server !)
421 Content-Length: 183 (hg-server !)
422 Content-Type: application/vnd.git-lfs+json
422 Content-Type: application/vnd.git-lfs+json
423 Date: $HTTP_DATE$
423 Date: $HTTP_DATE$
424 Server: testing stub value (hg-server !)
424 Server: testing stub value (hg-server !)
425 {
425 {
426 "objects": [
426 "objects": [
427 {
427 {
428 "actions": { (git-server !)
428 "actions": { (git-server !)
429 "download": { (git-server !)
429 "download": { (git-server !)
430 "expires_at": "$ISO_8601_DATE_TIME$" (git-server !)
430 "expires_at": "$ISO_8601_DATE_TIME$" (git-server !)
431 "header": { (git-server !)
431 "header": { (git-server !)
432 "Accept": "application/vnd.git-lfs" (git-server !)
432 "Accept": "application/vnd.git-lfs" (git-server !)
433 } (git-server !)
433 } (git-server !)
434 "href": "http://localhost:$HGPORT/objects/d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998" (git-server !)
434 "href": "http://localhost:$HGPORT/objects/d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998" (git-server !)
435 } (git-server !)
435 } (git-server !)
436 "error": { (hg-server !)
436 "error": { (hg-server !)
437 "code": 422 (hg-server !)
437 "code": 422 (hg-server !)
438 "message": "The object is corrupt" (hg-server !)
438 "message": "The object is corrupt" (hg-server !)
439 }
439 }
440 "oid": "d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998"
440 "oid": "d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998"
441 "size": 19
441 "size": 19
442 }
442 }
443 ]
443 ]
444 "transfer": "basic" (hg-server !)
444 "transfer": "basic" (hg-server !)
445 }
445 }
446 lfs: downloading d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 (19 bytes) (git-server !)
446 lfs: downloading d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 (19 bytes) (git-server !)
447 Status: 200 (git-server !)
447 Status: 200 (git-server !)
448 Content-Length: 7 (git-server !)
448 Content-Length: 7 (git-server !)
449 Content-Type: text/plain; charset=utf-8 (git-server !)
449 Content-Type: text/plain; charset=utf-8 (git-server !)
450 Date: $HTTP_DATE$ (git-server !)
450 Date: $HTTP_DATE$ (git-server !)
451 abort: corrupt remote lfs object: d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 (git-server !)
451 abort: corrupt remote lfs object: d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 (git-server !)
452 abort: LFS server error. Remote object for "c" not found: *! (glob) (hg-server !)
452 abort: LFS server error for "c": Validation error! (hg-server !)
453 [255]
453 [255]
454
454
455 The corrupted blob is not added to the usercache or local store
455 The corrupted blob is not added to the usercache or local store
456
456
457 $ test -f ../repo1/.hg/store/lfs/objects/d1/1e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
457 $ test -f ../repo1/.hg/store/lfs/objects/d1/1e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
458 [1]
458 [1]
459 $ test -f `hg config lfs.usercache`/d1/1e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
459 $ test -f `hg config lfs.usercache`/d1/1e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
460 [1]
460 [1]
461 #if git-server
461 #if git-server
462 $ cp blob $TESTTMP/lfs-content/d1/1e/1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
462 $ cp blob $TESTTMP/lfs-content/d1/1e/1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
463 #else
463 #else
464 $ cp blob $TESTTMP/server/.hg/store/lfs/objects/d1/1e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
464 $ cp blob $TESTTMP/server/.hg/store/lfs/objects/d1/1e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
465 #endif
465 #endif
466
466
467 Test a corrupted file upload
467 Test a corrupted file upload
468
468
469 $ echo 'another lfs blob' > b
469 $ echo 'another lfs blob' > b
470 $ hg ci -m 'another blob'
470 $ hg ci -m 'another blob'
471 $ echo 'damage' > .hg/store/lfs/objects/e6/59058e26b07b39d2a9c7145b3f99b41f797b6621c8076600e9cb7ee88291f0
471 $ echo 'damage' > .hg/store/lfs/objects/e6/59058e26b07b39d2a9c7145b3f99b41f797b6621c8076600e9cb7ee88291f0
472 $ hg push --debug ../repo1
472 $ hg push --debug ../repo1
473 http auth: user foo, password ***
473 http auth: user foo, password ***
474 pushing to ../repo1
474 pushing to ../repo1
475 http auth: user foo, password ***
475 http auth: user foo, password ***
476 query 1; heads
476 query 1; heads
477 searching for changes
477 searching for changes
478 all remote heads known locally
478 all remote heads known locally
479 listing keys for "phases"
479 listing keys for "phases"
480 checking for updated bookmarks
480 checking for updated bookmarks
481 listing keys for "bookmarks"
481 listing keys for "bookmarks"
482 listing keys for "bookmarks"
482 listing keys for "bookmarks"
483 lfs: computing set of blobs to upload
483 lfs: computing set of blobs to upload
484 Status: 200
484 Status: 200
485 Content-Length: 309 (git-server !)
485 Content-Length: 309 (git-server !)
486 Content-Length: 350 (hg-server !)
486 Content-Length: 350 (hg-server !)
487 Content-Type: application/vnd.git-lfs+json
487 Content-Type: application/vnd.git-lfs+json
488 Date: $HTTP_DATE$
488 Date: $HTTP_DATE$
489 Server: testing stub value (hg-server !)
489 Server: testing stub value (hg-server !)
490 {
490 {
491 "objects": [
491 "objects": [
492 {
492 {
493 "actions": {
493 "actions": {
494 "upload": {
494 "upload": {
495 "expires_at": "$ISO_8601_DATE_TIME$"
495 "expires_at": "$ISO_8601_DATE_TIME$"
496 "header": {
496 "header": {
497 "Accept": "application/vnd.git-lfs"
497 "Accept": "application/vnd.git-lfs"
498 }
498 }
499 "href": "http://localhost:$HGPORT/*/e659058e26b07b39d2a9c7145b3f99b41f797b6621c8076600e9cb7ee88291f0" (glob)
499 "href": "http://localhost:$HGPORT/*/e659058e26b07b39d2a9c7145b3f99b41f797b6621c8076600e9cb7ee88291f0" (glob)
500 }
500 }
501 }
501 }
502 "oid": "e659058e26b07b39d2a9c7145b3f99b41f797b6621c8076600e9cb7ee88291f0"
502 "oid": "e659058e26b07b39d2a9c7145b3f99b41f797b6621c8076600e9cb7ee88291f0"
503 "size": 17
503 "size": 17
504 }
504 }
505 ]
505 ]
506 "transfer": "basic" (hg-server !)
506 "transfer": "basic" (hg-server !)
507 }
507 }
508 lfs: uploading e659058e26b07b39d2a9c7145b3f99b41f797b6621c8076600e9cb7ee88291f0 (17 bytes)
508 lfs: uploading e659058e26b07b39d2a9c7145b3f99b41f797b6621c8076600e9cb7ee88291f0 (17 bytes)
509 abort: detected corrupt lfs object: e659058e26b07b39d2a9c7145b3f99b41f797b6621c8076600e9cb7ee88291f0
509 abort: detected corrupt lfs object: e659058e26b07b39d2a9c7145b3f99b41f797b6621c8076600e9cb7ee88291f0
510 (run hg verify)
510 (run hg verify)
511 [255]
511 [255]
512
512
513 Archive will prefetch blobs in a group
513 Archive will prefetch blobs in a group
514
514
515 $ rm -rf .hg/store/lfs `hg config lfs.usercache`
515 $ rm -rf .hg/store/lfs `hg config lfs.usercache`
516 $ hg archive --debug -r 1 ../archive
516 $ hg archive --debug -r 1 ../archive
517 http auth: user foo, password ***
517 http auth: user foo, password ***
518 Status: 200
518 Status: 200
519 Content-Length: 905 (git-server !)
519 Content-Length: 905 (git-server !)
520 Content-Length: 988 (hg-server !)
520 Content-Length: 988 (hg-server !)
521 Content-Type: application/vnd.git-lfs+json
521 Content-Type: application/vnd.git-lfs+json
522 Date: $HTTP_DATE$
522 Date: $HTTP_DATE$
523 Server: testing stub value (hg-server !)
523 Server: testing stub value (hg-server !)
524 {
524 {
525 "objects": [
525 "objects": [
526 {
526 {
527 "actions": {
527 "actions": {
528 "download": {
528 "download": {
529 "expires_at": "$ISO_8601_DATE_TIME$"
529 "expires_at": "$ISO_8601_DATE_TIME$"
530 "header": {
530 "header": {
531 "Accept": "application/vnd.git-lfs"
531 "Accept": "application/vnd.git-lfs"
532 }
532 }
533 "href": "http://localhost:$HGPORT/*/31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b" (glob)
533 "href": "http://localhost:$HGPORT/*/31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b" (glob)
534 }
534 }
535 }
535 }
536 "oid": "31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b"
536 "oid": "31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b"
537 "size": 12
537 "size": 12
538 }
538 }
539 {
539 {
540 "actions": {
540 "actions": {
541 "download": {
541 "download": {
542 "expires_at": "$ISO_8601_DATE_TIME$"
542 "expires_at": "$ISO_8601_DATE_TIME$"
543 "header": {
543 "header": {
544 "Accept": "application/vnd.git-lfs"
544 "Accept": "application/vnd.git-lfs"
545 }
545 }
546 "href": "http://localhost:$HGPORT/*/37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19" (glob)
546 "href": "http://localhost:$HGPORT/*/37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19" (glob)
547 }
547 }
548 }
548 }
549 "oid": "37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19"
549 "oid": "37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19"
550 "size": 20
550 "size": 20
551 }
551 }
552 {
552 {
553 "actions": {
553 "actions": {
554 "download": {
554 "download": {
555 "expires_at": "$ISO_8601_DATE_TIME$"
555 "expires_at": "$ISO_8601_DATE_TIME$"
556 "header": {
556 "header": {
557 "Accept": "application/vnd.git-lfs"
557 "Accept": "application/vnd.git-lfs"
558 }
558 }
559 "href": "http://localhost:$HGPORT/*/d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998" (glob)
559 "href": "http://localhost:$HGPORT/*/d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998" (glob)
560 }
560 }
561 }
561 }
562 "oid": "d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998"
562 "oid": "d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998"
563 "size": 19
563 "size": 19
564 }
564 }
565 ]
565 ]
566 "transfer": "basic" (hg-server !)
566 "transfer": "basic" (hg-server !)
567 }
567 }
568 lfs: need to transfer 3 objects (51 bytes)
568 lfs: need to transfer 3 objects (51 bytes)
569 lfs: downloading 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b (12 bytes)
569 lfs: downloading 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b (12 bytes)
570 Status: 200
570 Status: 200
571 Content-Length: 12
571 Content-Length: 12
572 Content-Type: text/plain; charset=utf-8 (git-server !)
572 Content-Type: text/plain; charset=utf-8 (git-server !)
573 Content-Type: application/octet-stream (hg-server !)
573 Content-Type: application/octet-stream (hg-server !)
574 Date: $HTTP_DATE$
574 Date: $HTTP_DATE$
575 Server: testing stub value (hg-server !)
575 Server: testing stub value (hg-server !)
576 lfs: adding 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b to the usercache
576 lfs: adding 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b to the usercache
577 lfs: processed: 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b
577 lfs: processed: 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b
578 lfs: downloading 37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 (20 bytes)
578 lfs: downloading 37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 (20 bytes)
579 Status: 200
579 Status: 200
580 Content-Length: 20
580 Content-Length: 20
581 Content-Type: text/plain; charset=utf-8 (git-server !)
581 Content-Type: text/plain; charset=utf-8 (git-server !)
582 Content-Type: application/octet-stream (hg-server !)
582 Content-Type: application/octet-stream (hg-server !)
583 Date: $HTTP_DATE$
583 Date: $HTTP_DATE$
584 Server: testing stub value (hg-server !)
584 Server: testing stub value (hg-server !)
585 lfs: adding 37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 to the usercache
585 lfs: adding 37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 to the usercache
586 lfs: processed: 37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19
586 lfs: processed: 37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19
587 lfs: downloading d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 (19 bytes)
587 lfs: downloading d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 (19 bytes)
588 Status: 200
588 Status: 200
589 Content-Length: 19
589 Content-Length: 19
590 Content-Type: text/plain; charset=utf-8 (git-server !)
590 Content-Type: text/plain; charset=utf-8 (git-server !)
591 Content-Type: application/octet-stream (hg-server !)
591 Content-Type: application/octet-stream (hg-server !)
592 Date: $HTTP_DATE$
592 Date: $HTTP_DATE$
593 Server: testing stub value (hg-server !)
593 Server: testing stub value (hg-server !)
594 lfs: adding d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 to the usercache
594 lfs: adding d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 to the usercache
595 lfs: processed: d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
595 lfs: processed: d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
596 lfs: found 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b in the local lfs store
596 lfs: found 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b in the local lfs store
597 lfs: found 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b in the local lfs store
597 lfs: found 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b in the local lfs store
598 lfs: found d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 in the local lfs store
598 lfs: found d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 in the local lfs store
599 lfs: found 37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 in the local lfs store
599 lfs: found 37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 in the local lfs store
600 $ find ../archive | sort
600 $ find ../archive | sort
601 ../archive
601 ../archive
602 ../archive/.hg_archival.txt
602 ../archive/.hg_archival.txt
603 ../archive/a
603 ../archive/a
604 ../archive/b
604 ../archive/b
605 ../archive/c
605 ../archive/c
606 ../archive/d
606 ../archive/d
607
607
608 Cat will prefetch blobs in a group
608 Cat will prefetch blobs in a group
609
609
610 $ rm -rf .hg/store/lfs `hg config lfs.usercache`
610 $ rm -rf .hg/store/lfs `hg config lfs.usercache`
611 $ hg cat --debug -r 1 a b c
611 $ hg cat --debug -r 1 a b c
612 http auth: user foo, password ***
612 http auth: user foo, password ***
613 Status: 200
613 Status: 200
614 Content-Length: 608 (git-server !)
614 Content-Length: 608 (git-server !)
615 Content-Length: 670 (hg-server !)
615 Content-Length: 670 (hg-server !)
616 Content-Type: application/vnd.git-lfs+json
616 Content-Type: application/vnd.git-lfs+json
617 Date: $HTTP_DATE$
617 Date: $HTTP_DATE$
618 Server: testing stub value (hg-server !)
618 Server: testing stub value (hg-server !)
619 {
619 {
620 "objects": [
620 "objects": [
621 {
621 {
622 "actions": {
622 "actions": {
623 "download": {
623 "download": {
624 "expires_at": "$ISO_8601_DATE_TIME$"
624 "expires_at": "$ISO_8601_DATE_TIME$"
625 "header": {
625 "header": {
626 "Accept": "application/vnd.git-lfs"
626 "Accept": "application/vnd.git-lfs"
627 }
627 }
628 "href": "http://localhost:$HGPORT/*/31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b" (glob)
628 "href": "http://localhost:$HGPORT/*/31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b" (glob)
629 }
629 }
630 }
630 }
631 "oid": "31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b"
631 "oid": "31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b"
632 "size": 12
632 "size": 12
633 }
633 }
634 {
634 {
635 "actions": {
635 "actions": {
636 "download": {
636 "download": {
637 "expires_at": "$ISO_8601_DATE_TIME$"
637 "expires_at": "$ISO_8601_DATE_TIME$"
638 "header": {
638 "header": {
639 "Accept": "application/vnd.git-lfs"
639 "Accept": "application/vnd.git-lfs"
640 }
640 }
641 "href": "http://localhost:$HGPORT/*/d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998" (glob)
641 "href": "http://localhost:$HGPORT/*/d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998" (glob)
642 }
642 }
643 }
643 }
644 "oid": "d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998"
644 "oid": "d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998"
645 "size": 19
645 "size": 19
646 }
646 }
647 ]
647 ]
648 "transfer": "basic" (hg-server !)
648 "transfer": "basic" (hg-server !)
649 }
649 }
650 lfs: need to transfer 2 objects (31 bytes)
650 lfs: need to transfer 2 objects (31 bytes)
651 lfs: downloading 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b (12 bytes)
651 lfs: downloading 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b (12 bytes)
652 Status: 200
652 Status: 200
653 Content-Length: 12
653 Content-Length: 12
654 Content-Type: text/plain; charset=utf-8 (git-server !)
654 Content-Type: text/plain; charset=utf-8 (git-server !)
655 Content-Type: application/octet-stream (hg-server !)
655 Content-Type: application/octet-stream (hg-server !)
656 Date: $HTTP_DATE$
656 Date: $HTTP_DATE$
657 Server: testing stub value (hg-server !)
657 Server: testing stub value (hg-server !)
658 lfs: adding 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b to the usercache
658 lfs: adding 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b to the usercache
659 lfs: processed: 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b
659 lfs: processed: 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b
660 lfs: downloading d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 (19 bytes)
660 lfs: downloading d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 (19 bytes)
661 Status: 200
661 Status: 200
662 Content-Length: 19
662 Content-Length: 19
663 Content-Type: text/plain; charset=utf-8 (git-server !)
663 Content-Type: text/plain; charset=utf-8 (git-server !)
664 Content-Type: application/octet-stream (hg-server !)
664 Content-Type: application/octet-stream (hg-server !)
665 Date: $HTTP_DATE$
665 Date: $HTTP_DATE$
666 Server: testing stub value (hg-server !)
666 Server: testing stub value (hg-server !)
667 lfs: adding d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 to the usercache
667 lfs: adding d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 to the usercache
668 lfs: processed: d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
668 lfs: processed: d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
669 lfs: found 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b in the local lfs store
669 lfs: found 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b in the local lfs store
670 THIS-IS-LFS
670 THIS-IS-LFS
671 lfs: found 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b in the local lfs store
671 lfs: found 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b in the local lfs store
672 THIS-IS-LFS
672 THIS-IS-LFS
673 lfs: found d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 in the local lfs store
673 lfs: found d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 in the local lfs store
674 ANOTHER-LARGE-FILE
674 ANOTHER-LARGE-FILE
675
675
676 Revert will prefetch blobs in a group
676 Revert will prefetch blobs in a group
677
677
678 $ rm -rf .hg/store/lfs
678 $ rm -rf .hg/store/lfs
679 $ rm -rf `hg config lfs.usercache`
679 $ rm -rf `hg config lfs.usercache`
680 $ rm *
680 $ rm *
681 $ hg revert --all -r 1 --debug
681 $ hg revert --all -r 1 --debug
682 http auth: user foo, password ***
682 http auth: user foo, password ***
683 adding a
683 adding a
684 reverting b
684 reverting b
685 reverting c
685 reverting c
686 reverting d
686 reverting d
687 Status: 200
687 Status: 200
688 Content-Length: 905 (git-server !)
688 Content-Length: 905 (git-server !)
689 Content-Length: 988 (hg-server !)
689 Content-Length: 988 (hg-server !)
690 Content-Type: application/vnd.git-lfs+json
690 Content-Type: application/vnd.git-lfs+json
691 Date: $HTTP_DATE$
691 Date: $HTTP_DATE$
692 Server: testing stub value (hg-server !)
692 Server: testing stub value (hg-server !)
693 {
693 {
694 "objects": [
694 "objects": [
695 {
695 {
696 "actions": {
696 "actions": {
697 "download": {
697 "download": {
698 "expires_at": "$ISO_8601_DATE_TIME$"
698 "expires_at": "$ISO_8601_DATE_TIME$"
699 "header": {
699 "header": {
700 "Accept": "application/vnd.git-lfs"
700 "Accept": "application/vnd.git-lfs"
701 }
701 }
702 "href": "http://localhost:$HGPORT/*/31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b" (glob)
702 "href": "http://localhost:$HGPORT/*/31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b" (glob)
703 }
703 }
704 }
704 }
705 "oid": "31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b"
705 "oid": "31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b"
706 "size": 12
706 "size": 12
707 }
707 }
708 {
708 {
709 "actions": {
709 "actions": {
710 "download": {
710 "download": {
711 "expires_at": "$ISO_8601_DATE_TIME$"
711 "expires_at": "$ISO_8601_DATE_TIME$"
712 "header": {
712 "header": {
713 "Accept": "application/vnd.git-lfs"
713 "Accept": "application/vnd.git-lfs"
714 }
714 }
715 "href": "http://localhost:$HGPORT/*/37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19" (glob)
715 "href": "http://localhost:$HGPORT/*/37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19" (glob)
716 }
716 }
717 }
717 }
718 "oid": "37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19"
718 "oid": "37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19"
719 "size": 20
719 "size": 20
720 }
720 }
721 {
721 {
722 "actions": {
722 "actions": {
723 "download": {
723 "download": {
724 "expires_at": "$ISO_8601_DATE_TIME$"
724 "expires_at": "$ISO_8601_DATE_TIME$"
725 "header": {
725 "header": {
726 "Accept": "application/vnd.git-lfs"
726 "Accept": "application/vnd.git-lfs"
727 }
727 }
728 "href": "http://localhost:$HGPORT/*/d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998" (glob)
728 "href": "http://localhost:$HGPORT/*/d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998" (glob)
729 }
729 }
730 }
730 }
731 "oid": "d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998"
731 "oid": "d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998"
732 "size": 19
732 "size": 19
733 }
733 }
734 ]
734 ]
735 "transfer": "basic" (hg-server !)
735 "transfer": "basic" (hg-server !)
736 }
736 }
737 lfs: need to transfer 3 objects (51 bytes)
737 lfs: need to transfer 3 objects (51 bytes)
738 lfs: downloading 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b (12 bytes)
738 lfs: downloading 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b (12 bytes)
739 Status: 200
739 Status: 200
740 Content-Length: 12
740 Content-Length: 12
741 Content-Type: text/plain; charset=utf-8 (git-server !)
741 Content-Type: text/plain; charset=utf-8 (git-server !)
742 Content-Type: application/octet-stream (hg-server !)
742 Content-Type: application/octet-stream (hg-server !)
743 Date: $HTTP_DATE$
743 Date: $HTTP_DATE$
744 Server: testing stub value (hg-server !)
744 Server: testing stub value (hg-server !)
745 lfs: adding 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b to the usercache
745 lfs: adding 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b to the usercache
746 lfs: processed: 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b
746 lfs: processed: 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b
747 lfs: downloading 37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 (20 bytes)
747 lfs: downloading 37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 (20 bytes)
748 Status: 200
748 Status: 200
749 Content-Length: 20
749 Content-Length: 20
750 Content-Type: text/plain; charset=utf-8 (git-server !)
750 Content-Type: text/plain; charset=utf-8 (git-server !)
751 Content-Type: application/octet-stream (hg-server !)
751 Content-Type: application/octet-stream (hg-server !)
752 Date: $HTTP_DATE$
752 Date: $HTTP_DATE$
753 Server: testing stub value (hg-server !)
753 Server: testing stub value (hg-server !)
754 lfs: adding 37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 to the usercache
754 lfs: adding 37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 to the usercache
755 lfs: processed: 37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19
755 lfs: processed: 37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19
756 lfs: downloading d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 (19 bytes)
756 lfs: downloading d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 (19 bytes)
757 Status: 200
757 Status: 200
758 Content-Length: 19
758 Content-Length: 19
759 Content-Type: text/plain; charset=utf-8 (git-server !)
759 Content-Type: text/plain; charset=utf-8 (git-server !)
760 Content-Type: application/octet-stream (hg-server !)
760 Content-Type: application/octet-stream (hg-server !)
761 Date: $HTTP_DATE$
761 Date: $HTTP_DATE$
762 Server: testing stub value (hg-server !)
762 Server: testing stub value (hg-server !)
763 lfs: adding d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 to the usercache
763 lfs: adding d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 to the usercache
764 lfs: processed: d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
764 lfs: processed: d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
765 lfs: found 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b in the local lfs store
765 lfs: found 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b in the local lfs store
766 lfs: found d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 in the local lfs store
766 lfs: found d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 in the local lfs store
767 lfs: found 37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 in the local lfs store
767 lfs: found 37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 in the local lfs store
768 lfs: found 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b in the local lfs store
768 lfs: found 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b in the local lfs store
769
769
770 Check error message when the remote missed a blob:
770 Check error message when the remote missed a blob:
771
771
772 $ echo FFFFF > b
772 $ echo FFFFF > b
773 $ hg commit -m b -A b
773 $ hg commit -m b -A b
774 $ echo FFFFF >> b
774 $ echo FFFFF >> b
775 $ hg commit -m b b
775 $ hg commit -m b b
776 $ rm -rf .hg/store/lfs
776 $ rm -rf .hg/store/lfs
777 $ rm -rf `hg config lfs.usercache`
777 $ rm -rf `hg config lfs.usercache`
778 $ hg update -C '.^' --debug
778 $ hg update -C '.^' --debug
779 http auth: user foo, password ***
779 http auth: user foo, password ***
780 resolving manifests
780 resolving manifests
781 branchmerge: False, force: True, partial: False
781 branchmerge: False, force: True, partial: False
782 ancestor: 62fdbaf221c6+, local: 62fdbaf221c6+, remote: ef0564edf47e
782 ancestor: 62fdbaf221c6+, local: 62fdbaf221c6+, remote: ef0564edf47e
783 Status: 200
783 Status: 200
784 Content-Length: 308 (git-server !)
784 Content-Length: 308 (git-server !)
785 Content-Length: 186 (hg-server !)
785 Content-Length: 186 (hg-server !)
786 Content-Type: application/vnd.git-lfs+json
786 Content-Type: application/vnd.git-lfs+json
787 Date: $HTTP_DATE$
787 Date: $HTTP_DATE$
788 Server: testing stub value (hg-server !)
788 Server: testing stub value (hg-server !)
789 {
789 {
790 "objects": [
790 "objects": [
791 {
791 {
792 "actions": { (git-server !)
792 "actions": { (git-server !)
793 "upload": { (git-server !)
793 "upload": { (git-server !)
794 "expires_at": "$ISO_8601_DATE_TIME$" (git-server !)
794 "expires_at": "$ISO_8601_DATE_TIME$" (git-server !)
795 "header": { (git-server !)
795 "header": { (git-server !)
796 "Accept": "application/vnd.git-lfs" (git-server !)
796 "Accept": "application/vnd.git-lfs" (git-server !)
797 } (git-server !)
797 } (git-server !)
798 "href": "http://localhost:$HGPORT/objects/8e6ea5f6c066b44a0efa43bcce86aea73f17e6e23f0663df0251e7524e140a13" (git-server !)
798 "href": "http://localhost:$HGPORT/objects/8e6ea5f6c066b44a0efa43bcce86aea73f17e6e23f0663df0251e7524e140a13" (git-server !)
799 } (git-server !)
799 } (git-server !)
800 "error": { (hg-server !)
800 "error": { (hg-server !)
801 "code": 404 (hg-server !)
801 "code": 404 (hg-server !)
802 "message": "The object does not exist" (hg-server !)
802 "message": "The object does not exist" (hg-server !)
803 }
803 }
804 "oid": "8e6ea5f6c066b44a0efa43bcce86aea73f17e6e23f0663df0251e7524e140a13"
804 "oid": "8e6ea5f6c066b44a0efa43bcce86aea73f17e6e23f0663df0251e7524e140a13"
805 "size": 6
805 "size": 6
806 }
806 }
807 ]
807 ]
808 "transfer": "basic" (hg-server !)
808 "transfer": "basic" (hg-server !)
809 }
809 }
810 abort: LFS server error. Remote object for "b" not found:(.*)! (re)
810 abort: LFS server error for "b": The object does not exist!
811 [255]
811 [255]
812
812
813 Check error message when object does not exist:
813 Check error message when object does not exist:
814
814
815 $ cd $TESTTMP
815 $ cd $TESTTMP
816 $ hg init test && cd test
816 $ hg init test && cd test
817 $ echo "[extensions]" >> .hg/hgrc
817 $ echo "[extensions]" >> .hg/hgrc
818 $ echo "lfs=" >> .hg/hgrc
818 $ echo "lfs=" >> .hg/hgrc
819 $ echo "[lfs]" >> .hg/hgrc
819 $ echo "[lfs]" >> .hg/hgrc
820 $ echo "threshold=1" >> .hg/hgrc
820 $ echo "threshold=1" >> .hg/hgrc
821 $ echo a > a
821 $ echo a > a
822 $ hg add a
822 $ hg add a
823 $ hg commit -m 'test'
823 $ hg commit -m 'test'
824 $ echo aaaaa > a
824 $ echo aaaaa > a
825 $ hg commit -m 'largefile'
825 $ hg commit -m 'largefile'
826 $ hg debugdata .hg/store/data/a.i 1 # verify this is no the file content but includes "oid", the LFS "pointer".
826 $ hg debugdata .hg/store/data/a.i 1 # verify this is no the file content but includes "oid", the LFS "pointer".
827 version https://git-lfs.github.com/spec/v1
827 version https://git-lfs.github.com/spec/v1
828 oid sha256:bdc26931acfb734b142a8d675f205becf27560dc461f501822de13274fe6fc8a
828 oid sha256:bdc26931acfb734b142a8d675f205becf27560dc461f501822de13274fe6fc8a
829 size 6
829 size 6
830 x-is-binary 0
830 x-is-binary 0
831 $ cd ..
831 $ cd ..
832 $ rm -rf `hg config lfs.usercache`
832 $ rm -rf `hg config lfs.usercache`
833
833
834 (Restart the server in a different location so it no longer has the content)
834 (Restart the server in a different location so it no longer has the content)
835
835
836 $ $PYTHON $RUNTESTDIR/killdaemons.py $DAEMON_PIDS
836 $ $PYTHON $RUNTESTDIR/killdaemons.py $DAEMON_PIDS
837
837
838 #if hg-server
838 #if hg-server
839 $ cat $TESTTMP/access.log $TESTTMP/errors.log
839 $ cat $TESTTMP/access.log $TESTTMP/errors.log
840 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
840 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
841 $LOCALIP - - [$LOGDATE$] "PUT /.hg/lfs/objects/31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b HTTP/1.1" 201 - (glob)
841 $LOCALIP - - [$LOGDATE$] "PUT /.hg/lfs/objects/31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b HTTP/1.1" 201 - (glob)
842 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
842 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
843 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b HTTP/1.1" 200 - (glob)
843 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b HTTP/1.1" 200 - (glob)
844 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
844 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
845 $LOCALIP - - [$LOGDATE$] "PUT /.hg/lfs/objects/37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 HTTP/1.1" 201 - (glob)
845 $LOCALIP - - [$LOGDATE$] "PUT /.hg/lfs/objects/37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 HTTP/1.1" 201 - (glob)
846 $LOCALIP - - [$LOGDATE$] "PUT /.hg/lfs/objects/d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 HTTP/1.1" 201 - (glob)
846 $LOCALIP - - [$LOGDATE$] "PUT /.hg/lfs/objects/d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 HTTP/1.1" 201 - (glob)
847 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
847 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
848 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 HTTP/1.1" 200 - (glob)
848 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 HTTP/1.1" 200 - (glob)
849 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 HTTP/1.1" 200 - (glob)
849 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 HTTP/1.1" 200 - (glob)
850 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
850 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
851 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
851 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
852 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
852 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
853 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b HTTP/1.1" 200 - (glob)
853 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b HTTP/1.1" 200 - (glob)
854 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 HTTP/1.1" 200 - (glob)
854 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 HTTP/1.1" 200 - (glob)
855 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 HTTP/1.1" 200 - (glob)
855 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 HTTP/1.1" 200 - (glob)
856 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
856 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
857 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b HTTP/1.1" 200 - (glob)
857 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b HTTP/1.1" 200 - (glob)
858 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 HTTP/1.1" 200 - (glob)
858 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 HTTP/1.1" 200 - (glob)
859 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
859 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
860 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b HTTP/1.1" 200 - (glob)
860 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b HTTP/1.1" 200 - (glob)
861 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 HTTP/1.1" 200 - (glob)
861 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 HTTP/1.1" 200 - (glob)
862 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 HTTP/1.1" 200 - (glob)
862 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 HTTP/1.1" 200 - (glob)
863 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
863 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
864 #endif
864 #endif
865
865
866 $ rm $DAEMON_PIDS
866 $ rm $DAEMON_PIDS
867 $ mkdir $TESTTMP/lfs-server2
867 $ mkdir $TESTTMP/lfs-server2
868 $ cd $TESTTMP/lfs-server2
868 $ cd $TESTTMP/lfs-server2
869 #if no-windows git-server
869 #if no-windows git-server
870 $ lfs-test-server &> lfs-server.log &
870 $ lfs-test-server &> lfs-server.log &
871 $ echo $! >> $DAEMON_PIDS
871 $ echo $! >> $DAEMON_PIDS
872 #endif
872 #endif
873
873
874 #if windows git-server
874 #if windows git-server
875 $ $PYTHON $TESTTMP/spawn.py >> $DAEMON_PIDS
875 $ $PYTHON $TESTTMP/spawn.py >> $DAEMON_PIDS
876 #endif
876 #endif
877
877
878 #if hg-server
878 #if hg-server
879 $ hg init server2
879 $ hg init server2
880 $ hg --config "lfs.usercache=$TESTTMP/servercache2" -R server2 serve -d \
880 $ hg --config "lfs.usercache=$TESTTMP/servercache2" -R server2 serve -d \
881 > -p $HGPORT --pid-file=hg.pid -A $TESTTMP/access.log -E $TESTTMP/errors.log
881 > -p $HGPORT --pid-file=hg.pid -A $TESTTMP/access.log -E $TESTTMP/errors.log
882 $ cat hg.pid >> $DAEMON_PIDS
882 $ cat hg.pid >> $DAEMON_PIDS
883 #endif
883 #endif
884
884
885 $ cd $TESTTMP
885 $ cd $TESTTMP
886 $ hg --debug clone test test2
886 $ hg --debug clone test test2
887 http auth: user foo, password ***
887 http auth: user foo, password ***
888 linked 6 files
888 linked 6 files
889 http auth: user foo, password ***
889 http auth: user foo, password ***
890 updating to branch default
890 updating to branch default
891 resolving manifests
891 resolving manifests
892 branchmerge: False, force: False, partial: False
892 branchmerge: False, force: False, partial: False
893 ancestor: 000000000000, local: 000000000000+, remote: d2a338f184a8
893 ancestor: 000000000000, local: 000000000000+, remote: d2a338f184a8
894 Status: 200
894 Status: 200
895 Content-Length: 308 (git-server !)
895 Content-Length: 308 (git-server !)
896 Content-Length: 186 (hg-server !)
896 Content-Length: 186 (hg-server !)
897 Content-Type: application/vnd.git-lfs+json
897 Content-Type: application/vnd.git-lfs+json
898 Date: $HTTP_DATE$
898 Date: $HTTP_DATE$
899 Server: testing stub value (hg-server !)
899 Server: testing stub value (hg-server !)
900 {
900 {
901 "objects": [
901 "objects": [
902 {
902 {
903 "actions": { (git-server !)
903 "actions": { (git-server !)
904 "upload": { (git-server !)
904 "upload": { (git-server !)
905 "expires_at": "$ISO_8601_DATE_TIME$" (git-server !)
905 "expires_at": "$ISO_8601_DATE_TIME$" (git-server !)
906 "header": { (git-server !)
906 "header": { (git-server !)
907 "Accept": "application/vnd.git-lfs" (git-server !)
907 "Accept": "application/vnd.git-lfs" (git-server !)
908 } (git-server !)
908 } (git-server !)
909 "href": "http://localhost:$HGPORT/objects/bdc26931acfb734b142a8d675f205becf27560dc461f501822de13274fe6fc8a" (git-server !)
909 "href": "http://localhost:$HGPORT/objects/bdc26931acfb734b142a8d675f205becf27560dc461f501822de13274fe6fc8a" (git-server !)
910 } (git-server !)
910 } (git-server !)
911 "error": { (hg-server !)
911 "error": { (hg-server !)
912 "code": 404 (hg-server !)
912 "code": 404 (hg-server !)
913 "message": "The object does not exist" (hg-server !)
913 "message": "The object does not exist" (hg-server !)
914 }
914 }
915 "oid": "bdc26931acfb734b142a8d675f205becf27560dc461f501822de13274fe6fc8a"
915 "oid": "bdc26931acfb734b142a8d675f205becf27560dc461f501822de13274fe6fc8a"
916 "size": 6
916 "size": 6
917 }
917 }
918 ]
918 ]
919 "transfer": "basic" (hg-server !)
919 "transfer": "basic" (hg-server !)
920 }
920 }
921 abort: LFS server error. Remote object for "a" not found:(.*)! (re)
921 abort: LFS server error for "a": The object does not exist!
922 [255]
922 [255]
923
923
924 $ $PYTHON $RUNTESTDIR/killdaemons.py $DAEMON_PIDS
924 $ $PYTHON $RUNTESTDIR/killdaemons.py $DAEMON_PIDS
General Comments 0
You need to be logged in to leave comments. Login now