##// END OF EJS Templates
lfs: remove callstatus property from 'lfs()' fileset...
Yuya Nishihara -
r38836:7790cd70 default
parent child Browse files
Show More
@@ -1,401 +1,401 b''
1 # lfs - hash-preserving large file support using Git-LFS protocol
1 # lfs - hash-preserving large file support using Git-LFS protocol
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 """lfs - large file support (EXPERIMENTAL)
8 """lfs - large file support (EXPERIMENTAL)
9
9
10 This extension allows large files to be tracked outside of the normal
10 This extension allows large files to be tracked outside of the normal
11 repository storage and stored on a centralized server, similar to the
11 repository storage and stored on a centralized server, similar to the
12 ``largefiles`` extension. The ``git-lfs`` protocol is used when
12 ``largefiles`` extension. The ``git-lfs`` protocol is used when
13 communicating with the server, so existing git infrastructure can be
13 communicating with the server, so existing git infrastructure can be
14 harnessed. Even though the files are stored outside of the repository,
14 harnessed. Even though the files are stored outside of the repository,
15 they are still integrity checked in the same manner as normal files.
15 they are still integrity checked in the same manner as normal files.
16
16
17 The files stored outside of the repository are downloaded on demand,
17 The files stored outside of the repository are downloaded on demand,
18 which reduces the time to clone, and possibly the local disk usage.
18 which reduces the time to clone, and possibly the local disk usage.
19 This changes fundamental workflows in a DVCS, so careful thought
19 This changes fundamental workflows in a DVCS, so careful thought
20 should be given before deploying it. :hg:`convert` can be used to
20 should be given before deploying it. :hg:`convert` can be used to
21 convert LFS repositories to normal repositories that no longer
21 convert LFS repositories to normal repositories that no longer
22 require this extension, and do so without changing the commit hashes.
22 require this extension, and do so without changing the commit hashes.
23 This allows the extension to be disabled if the centralized workflow
23 This allows the extension to be disabled if the centralized workflow
24 becomes burdensome. However, the pre and post convert clones will
24 becomes burdensome. However, the pre and post convert clones will
25 not be able to communicate with each other unless the extension is
25 not be able to communicate with each other unless the extension is
26 enabled on both.
26 enabled on both.
27
27
28 To start a new repository, or to add LFS files to an existing one, just
28 To start a new repository, or to add LFS files to an existing one, just
29 create an ``.hglfs`` file as described below in the root directory of
29 create an ``.hglfs`` file as described below in the root directory of
30 the repository. Typically, this file should be put under version
30 the repository. Typically, this file should be put under version
31 control, so that the settings will propagate to other repositories with
31 control, so that the settings will propagate to other repositories with
32 push and pull. During any commit, Mercurial will consult this file to
32 push and pull. During any commit, Mercurial will consult this file to
33 determine if an added or modified file should be stored externally. The
33 determine if an added or modified file should be stored externally. The
34 type of storage depends on the characteristics of the file at each
34 type of storage depends on the characteristics of the file at each
35 commit. A file that is near a size threshold may switch back and forth
35 commit. A file that is near a size threshold may switch back and forth
36 between LFS and normal storage, as needed.
36 between LFS and normal storage, as needed.
37
37
38 Alternately, both normal repositories and largefile controlled
38 Alternately, both normal repositories and largefile controlled
39 repositories can be converted to LFS by using :hg:`convert` and the
39 repositories can be converted to LFS by using :hg:`convert` and the
40 ``lfs.track`` config option described below. The ``.hglfs`` file
40 ``lfs.track`` config option described below. The ``.hglfs`` file
41 should then be created and added, to control subsequent LFS selection.
41 should then be created and added, to control subsequent LFS selection.
42 The hashes are also unchanged in this case. The LFS and non-LFS
42 The hashes are also unchanged in this case. The LFS and non-LFS
43 repositories can be distinguished because the LFS repository will
43 repositories can be distinguished because the LFS repository will
44 abort any command if this extension is disabled.
44 abort any command if this extension is disabled.
45
45
46 Committed LFS files are held locally, until the repository is pushed.
46 Committed LFS files are held locally, until the repository is pushed.
47 Prior to pushing the normal repository data, the LFS files that are
47 Prior to pushing the normal repository data, the LFS files that are
48 tracked by the outgoing commits are automatically uploaded to the
48 tracked by the outgoing commits are automatically uploaded to the
49 configured central server. No LFS files are transferred on
49 configured central server. No LFS files are transferred on
50 :hg:`pull` or :hg:`clone`. Instead, the files are downloaded on
50 :hg:`pull` or :hg:`clone`. Instead, the files are downloaded on
51 demand as they need to be read, if a cached copy cannot be found
51 demand as they need to be read, if a cached copy cannot be found
52 locally. Both committing and downloading an LFS file will link the
52 locally. Both committing and downloading an LFS file will link the
53 file to a usercache, to speed up future access. See the `usercache`
53 file to a usercache, to speed up future access. See the `usercache`
54 config setting described below.
54 config setting described below.
55
55
56 .hglfs::
56 .hglfs::
57
57
58 The extension reads its configuration from a versioned ``.hglfs``
58 The extension reads its configuration from a versioned ``.hglfs``
59 configuration file found in the root of the working directory. The
59 configuration file found in the root of the working directory. The
60 ``.hglfs`` file uses the same syntax as all other Mercurial
60 ``.hglfs`` file uses the same syntax as all other Mercurial
61 configuration files. It uses a single section, ``[track]``.
61 configuration files. It uses a single section, ``[track]``.
62
62
63 The ``[track]`` section specifies which files are stored as LFS (or
63 The ``[track]`` section specifies which files are stored as LFS (or
64 not). Each line is keyed by a file pattern, with a predicate value.
64 not). Each line is keyed by a file pattern, with a predicate value.
65 The first file pattern match is used, so put more specific patterns
65 The first file pattern match is used, so put more specific patterns
66 first. The available predicates are ``all()``, ``none()``, and
66 first. The available predicates are ``all()``, ``none()``, and
67 ``size()``. See "hg help filesets.size" for the latter.
67 ``size()``. See "hg help filesets.size" for the latter.
68
68
69 Example versioned ``.hglfs`` file::
69 Example versioned ``.hglfs`` file::
70
70
71 [track]
71 [track]
72 # No Makefile or python file, anywhere, will be LFS
72 # No Makefile or python file, anywhere, will be LFS
73 **Makefile = none()
73 **Makefile = none()
74 **.py = none()
74 **.py = none()
75
75
76 **.zip = all()
76 **.zip = all()
77 **.exe = size(">1MB")
77 **.exe = size(">1MB")
78
78
79 # Catchall for everything not matched above
79 # Catchall for everything not matched above
80 ** = size(">10MB")
80 ** = size(">10MB")
81
81
82 Configs::
82 Configs::
83
83
84 [lfs]
84 [lfs]
85 # Remote endpoint. Multiple protocols are supported:
85 # Remote endpoint. Multiple protocols are supported:
86 # - http(s)://user:pass@example.com/path
86 # - http(s)://user:pass@example.com/path
87 # git-lfs endpoint
87 # git-lfs endpoint
88 # - file:///tmp/path
88 # - file:///tmp/path
89 # local filesystem, usually for testing
89 # local filesystem, usually for testing
90 # if unset, lfs will assume the remote repository also handles blob storage
90 # if unset, lfs will assume the remote repository also handles blob storage
91 # for http(s) URLs. Otherwise, lfs will prompt to set this when it must
91 # for http(s) URLs. Otherwise, lfs will prompt to set this when it must
92 # use this value.
92 # use this value.
93 # (default: unset)
93 # (default: unset)
94 url = https://example.com/repo.git/info/lfs
94 url = https://example.com/repo.git/info/lfs
95
95
96 # Which files to track in LFS. Path tests are "**.extname" for file
96 # Which files to track in LFS. Path tests are "**.extname" for file
97 # extensions, and "path:under/some/directory" for path prefix. Both
97 # extensions, and "path:under/some/directory" for path prefix. Both
98 # are relative to the repository root.
98 # are relative to the repository root.
99 # File size can be tested with the "size()" fileset, and tests can be
99 # File size can be tested with the "size()" fileset, and tests can be
100 # joined with fileset operators. (See "hg help filesets.operators".)
100 # joined with fileset operators. (See "hg help filesets.operators".)
101 #
101 #
102 # Some examples:
102 # Some examples:
103 # - all() # everything
103 # - all() # everything
104 # - none() # nothing
104 # - none() # nothing
105 # - size(">20MB") # larger than 20MB
105 # - size(">20MB") # larger than 20MB
106 # - !**.txt # anything not a *.txt file
106 # - !**.txt # anything not a *.txt file
107 # - **.zip | **.tar.gz | **.7z # some types of compressed files
107 # - **.zip | **.tar.gz | **.7z # some types of compressed files
108 # - path:bin # files under "bin" in the project root
108 # - path:bin # files under "bin" in the project root
109 # - (**.php & size(">2MB")) | (**.js & size(">5MB")) | **.tar.gz
109 # - (**.php & size(">2MB")) | (**.js & size(">5MB")) | **.tar.gz
110 # | (path:bin & !path:/bin/README) | size(">1GB")
110 # | (path:bin & !path:/bin/README) | size(">1GB")
111 # (default: none())
111 # (default: none())
112 #
112 #
113 # This is ignored if there is a tracked '.hglfs' file, and this setting
113 # This is ignored if there is a tracked '.hglfs' file, and this setting
114 # will eventually be deprecated and removed.
114 # will eventually be deprecated and removed.
115 track = size(">10M")
115 track = size(">10M")
116
116
117 # how many times to retry before giving up on transferring an object
117 # how many times to retry before giving up on transferring an object
118 retry = 5
118 retry = 5
119
119
120 # the local directory to store lfs files for sharing across local clones.
120 # the local directory to store lfs files for sharing across local clones.
121 # If not set, the cache is located in an OS specific cache location.
121 # If not set, the cache is located in an OS specific cache location.
122 usercache = /path/to/global/cache
122 usercache = /path/to/global/cache
123 """
123 """
124
124
125 from __future__ import absolute_import
125 from __future__ import absolute_import
126
126
127 from mercurial.i18n import _
127 from mercurial.i18n import _
128
128
129 from mercurial import (
129 from mercurial import (
130 bundle2,
130 bundle2,
131 changegroup,
131 changegroup,
132 cmdutil,
132 cmdutil,
133 config,
133 config,
134 context,
134 context,
135 error,
135 error,
136 exchange,
136 exchange,
137 extensions,
137 extensions,
138 filelog,
138 filelog,
139 fileset,
139 fileset,
140 hg,
140 hg,
141 localrepo,
141 localrepo,
142 minifileset,
142 minifileset,
143 node,
143 node,
144 pycompat,
144 pycompat,
145 registrar,
145 registrar,
146 revlog,
146 revlog,
147 scmutil,
147 scmutil,
148 templateutil,
148 templateutil,
149 upgrade,
149 upgrade,
150 util,
150 util,
151 vfs as vfsmod,
151 vfs as vfsmod,
152 wireprotoserver,
152 wireprotoserver,
153 wireprotov1server,
153 wireprotov1server,
154 )
154 )
155
155
156 from . import (
156 from . import (
157 blobstore,
157 blobstore,
158 wireprotolfsserver,
158 wireprotolfsserver,
159 wrapper,
159 wrapper,
160 )
160 )
161
161
162 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
162 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
163 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
163 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
164 # be specifying the version(s) of Mercurial they are tested with, or
164 # be specifying the version(s) of Mercurial they are tested with, or
165 # leave the attribute unspecified.
165 # leave the attribute unspecified.
166 testedwith = 'ships-with-hg-core'
166 testedwith = 'ships-with-hg-core'
167
167
168 configtable = {}
168 configtable = {}
169 configitem = registrar.configitem(configtable)
169 configitem = registrar.configitem(configtable)
170
170
171 configitem('experimental', 'lfs.serve',
171 configitem('experimental', 'lfs.serve',
172 default=True,
172 default=True,
173 )
173 )
174 configitem('experimental', 'lfs.user-agent',
174 configitem('experimental', 'lfs.user-agent',
175 default=None,
175 default=None,
176 )
176 )
177 configitem('experimental', 'lfs.disableusercache',
177 configitem('experimental', 'lfs.disableusercache',
178 default=False,
178 default=False,
179 )
179 )
180 configitem('experimental', 'lfs.worker-enable',
180 configitem('experimental', 'lfs.worker-enable',
181 default=False,
181 default=False,
182 )
182 )
183
183
184 configitem('lfs', 'url',
184 configitem('lfs', 'url',
185 default=None,
185 default=None,
186 )
186 )
187 configitem('lfs', 'usercache',
187 configitem('lfs', 'usercache',
188 default=None,
188 default=None,
189 )
189 )
190 # Deprecated
190 # Deprecated
191 configitem('lfs', 'threshold',
191 configitem('lfs', 'threshold',
192 default=None,
192 default=None,
193 )
193 )
194 configitem('lfs', 'track',
194 configitem('lfs', 'track',
195 default='none()',
195 default='none()',
196 )
196 )
197 configitem('lfs', 'retry',
197 configitem('lfs', 'retry',
198 default=5,
198 default=5,
199 )
199 )
200
200
201 cmdtable = {}
201 cmdtable = {}
202 command = registrar.command(cmdtable)
202 command = registrar.command(cmdtable)
203
203
204 templatekeyword = registrar.templatekeyword()
204 templatekeyword = registrar.templatekeyword()
205 filesetpredicate = registrar.filesetpredicate()
205 filesetpredicate = registrar.filesetpredicate()
206
206
207 def featuresetup(ui, supported):
207 def featuresetup(ui, supported):
208 # don't die on seeing a repo with the lfs requirement
208 # don't die on seeing a repo with the lfs requirement
209 supported |= {'lfs'}
209 supported |= {'lfs'}
210
210
211 def uisetup(ui):
211 def uisetup(ui):
212 localrepo.featuresetupfuncs.add(featuresetup)
212 localrepo.featuresetupfuncs.add(featuresetup)
213
213
214 def reposetup(ui, repo):
214 def reposetup(ui, repo):
215 # Nothing to do with a remote repo
215 # Nothing to do with a remote repo
216 if not repo.local():
216 if not repo.local():
217 return
217 return
218
218
219 repo.svfs.lfslocalblobstore = blobstore.local(repo)
219 repo.svfs.lfslocalblobstore = blobstore.local(repo)
220 repo.svfs.lfsremoteblobstore = blobstore.remote(repo)
220 repo.svfs.lfsremoteblobstore = blobstore.remote(repo)
221
221
222 class lfsrepo(repo.__class__):
222 class lfsrepo(repo.__class__):
223 @localrepo.unfilteredmethod
223 @localrepo.unfilteredmethod
224 def commitctx(self, ctx, error=False):
224 def commitctx(self, ctx, error=False):
225 repo.svfs.options['lfstrack'] = _trackedmatcher(self)
225 repo.svfs.options['lfstrack'] = _trackedmatcher(self)
226 return super(lfsrepo, self).commitctx(ctx, error)
226 return super(lfsrepo, self).commitctx(ctx, error)
227
227
228 repo.__class__ = lfsrepo
228 repo.__class__ = lfsrepo
229
229
230 if 'lfs' not in repo.requirements:
230 if 'lfs' not in repo.requirements:
231 def checkrequireslfs(ui, repo, **kwargs):
231 def checkrequireslfs(ui, repo, **kwargs):
232 if 'lfs' not in repo.requirements:
232 if 'lfs' not in repo.requirements:
233 last = kwargs.get(r'node_last')
233 last = kwargs.get(r'node_last')
234 _bin = node.bin
234 _bin = node.bin
235 if last:
235 if last:
236 s = repo.set('%n:%n', _bin(kwargs[r'node']), _bin(last))
236 s = repo.set('%n:%n', _bin(kwargs[r'node']), _bin(last))
237 else:
237 else:
238 s = repo.set('%n', _bin(kwargs[r'node']))
238 s = repo.set('%n', _bin(kwargs[r'node']))
239 match = repo.narrowmatch()
239 match = repo.narrowmatch()
240 for ctx in s:
240 for ctx in s:
241 # TODO: is there a way to just walk the files in the commit?
241 # TODO: is there a way to just walk the files in the commit?
242 if any(ctx[f].islfs() for f in ctx.files()
242 if any(ctx[f].islfs() for f in ctx.files()
243 if f in ctx and match(f)):
243 if f in ctx and match(f)):
244 repo.requirements.add('lfs')
244 repo.requirements.add('lfs')
245 repo._writerequirements()
245 repo._writerequirements()
246 repo.prepushoutgoinghooks.add('lfs', wrapper.prepush)
246 repo.prepushoutgoinghooks.add('lfs', wrapper.prepush)
247 break
247 break
248
248
249 ui.setconfig('hooks', 'commit.lfs', checkrequireslfs, 'lfs')
249 ui.setconfig('hooks', 'commit.lfs', checkrequireslfs, 'lfs')
250 ui.setconfig('hooks', 'pretxnchangegroup.lfs', checkrequireslfs, 'lfs')
250 ui.setconfig('hooks', 'pretxnchangegroup.lfs', checkrequireslfs, 'lfs')
251 else:
251 else:
252 repo.prepushoutgoinghooks.add('lfs', wrapper.prepush)
252 repo.prepushoutgoinghooks.add('lfs', wrapper.prepush)
253
253
254 def _trackedmatcher(repo):
254 def _trackedmatcher(repo):
255 """Return a function (path, size) -> bool indicating whether or not to
255 """Return a function (path, size) -> bool indicating whether or not to
256 track a given file with lfs."""
256 track a given file with lfs."""
257 if not repo.wvfs.exists('.hglfs'):
257 if not repo.wvfs.exists('.hglfs'):
258 # No '.hglfs' in wdir. Fallback to config for now.
258 # No '.hglfs' in wdir. Fallback to config for now.
259 trackspec = repo.ui.config('lfs', 'track')
259 trackspec = repo.ui.config('lfs', 'track')
260
260
261 # deprecated config: lfs.threshold
261 # deprecated config: lfs.threshold
262 threshold = repo.ui.configbytes('lfs', 'threshold')
262 threshold = repo.ui.configbytes('lfs', 'threshold')
263 if threshold:
263 if threshold:
264 fileset.parse(trackspec) # make sure syntax errors are confined
264 fileset.parse(trackspec) # make sure syntax errors are confined
265 trackspec = "(%s) | size('>%d')" % (trackspec, threshold)
265 trackspec = "(%s) | size('>%d')" % (trackspec, threshold)
266
266
267 return minifileset.compile(trackspec)
267 return minifileset.compile(trackspec)
268
268
269 data = repo.wvfs.tryread('.hglfs')
269 data = repo.wvfs.tryread('.hglfs')
270 if not data:
270 if not data:
271 return lambda p, s: False
271 return lambda p, s: False
272
272
273 # Parse errors here will abort with a message that points to the .hglfs file
273 # Parse errors here will abort with a message that points to the .hglfs file
274 # and line number.
274 # and line number.
275 cfg = config.config()
275 cfg = config.config()
276 cfg.parse('.hglfs', data)
276 cfg.parse('.hglfs', data)
277
277
278 try:
278 try:
279 rules = [(minifileset.compile(pattern), minifileset.compile(rule))
279 rules = [(minifileset.compile(pattern), minifileset.compile(rule))
280 for pattern, rule in cfg.items('track')]
280 for pattern, rule in cfg.items('track')]
281 except error.ParseError as e:
281 except error.ParseError as e:
282 # The original exception gives no indicator that the error is in the
282 # The original exception gives no indicator that the error is in the
283 # .hglfs file, so add that.
283 # .hglfs file, so add that.
284
284
285 # TODO: See if the line number of the file can be made available.
285 # TODO: See if the line number of the file can be made available.
286 raise error.Abort(_('parse error in .hglfs: %s') % e)
286 raise error.Abort(_('parse error in .hglfs: %s') % e)
287
287
288 def _match(path, size):
288 def _match(path, size):
289 for pat, rule in rules:
289 for pat, rule in rules:
290 if pat(path, size):
290 if pat(path, size):
291 return rule(path, size)
291 return rule(path, size)
292
292
293 return False
293 return False
294
294
295 return _match
295 return _match
296
296
297 def wrapfilelog(filelog):
297 def wrapfilelog(filelog):
298 wrapfunction = extensions.wrapfunction
298 wrapfunction = extensions.wrapfunction
299
299
300 wrapfunction(filelog, 'addrevision', wrapper.filelogaddrevision)
300 wrapfunction(filelog, 'addrevision', wrapper.filelogaddrevision)
301 wrapfunction(filelog, 'renamed', wrapper.filelogrenamed)
301 wrapfunction(filelog, 'renamed', wrapper.filelogrenamed)
302 wrapfunction(filelog, 'size', wrapper.filelogsize)
302 wrapfunction(filelog, 'size', wrapper.filelogsize)
303
303
304 def extsetup(ui):
304 def extsetup(ui):
305 wrapfilelog(filelog.filelog)
305 wrapfilelog(filelog.filelog)
306
306
307 wrapfunction = extensions.wrapfunction
307 wrapfunction = extensions.wrapfunction
308
308
309 wrapfunction(cmdutil, '_updatecatformatter', wrapper._updatecatformatter)
309 wrapfunction(cmdutil, '_updatecatformatter', wrapper._updatecatformatter)
310 wrapfunction(scmutil, 'wrapconvertsink', wrapper.convertsink)
310 wrapfunction(scmutil, 'wrapconvertsink', wrapper.convertsink)
311
311
312 wrapfunction(upgrade, '_finishdatamigration',
312 wrapfunction(upgrade, '_finishdatamigration',
313 wrapper.upgradefinishdatamigration)
313 wrapper.upgradefinishdatamigration)
314
314
315 wrapfunction(upgrade, 'preservedrequirements',
315 wrapfunction(upgrade, 'preservedrequirements',
316 wrapper.upgraderequirements)
316 wrapper.upgraderequirements)
317
317
318 wrapfunction(upgrade, 'supporteddestrequirements',
318 wrapfunction(upgrade, 'supporteddestrequirements',
319 wrapper.upgraderequirements)
319 wrapper.upgraderequirements)
320
320
321 wrapfunction(changegroup,
321 wrapfunction(changegroup,
322 'allsupportedversions',
322 'allsupportedversions',
323 wrapper.allsupportedversions)
323 wrapper.allsupportedversions)
324
324
325 wrapfunction(exchange, 'push', wrapper.push)
325 wrapfunction(exchange, 'push', wrapper.push)
326 wrapfunction(wireprotov1server, '_capabilities', wrapper._capabilities)
326 wrapfunction(wireprotov1server, '_capabilities', wrapper._capabilities)
327 wrapfunction(wireprotoserver, 'handlewsgirequest',
327 wrapfunction(wireprotoserver, 'handlewsgirequest',
328 wireprotolfsserver.handlewsgirequest)
328 wireprotolfsserver.handlewsgirequest)
329
329
330 wrapfunction(context.basefilectx, 'cmp', wrapper.filectxcmp)
330 wrapfunction(context.basefilectx, 'cmp', wrapper.filectxcmp)
331 wrapfunction(context.basefilectx, 'isbinary', wrapper.filectxisbinary)
331 wrapfunction(context.basefilectx, 'isbinary', wrapper.filectxisbinary)
332 context.basefilectx.islfs = wrapper.filectxislfs
332 context.basefilectx.islfs = wrapper.filectxislfs
333
333
334 revlog.addflagprocessor(
334 revlog.addflagprocessor(
335 revlog.REVIDX_EXTSTORED,
335 revlog.REVIDX_EXTSTORED,
336 (
336 (
337 wrapper.readfromstore,
337 wrapper.readfromstore,
338 wrapper.writetostore,
338 wrapper.writetostore,
339 wrapper.bypasscheckhash,
339 wrapper.bypasscheckhash,
340 ),
340 ),
341 )
341 )
342
342
343 wrapfunction(hg, 'clone', wrapper.hgclone)
343 wrapfunction(hg, 'clone', wrapper.hgclone)
344 wrapfunction(hg, 'postshare', wrapper.hgpostshare)
344 wrapfunction(hg, 'postshare', wrapper.hgpostshare)
345
345
346 scmutil.fileprefetchhooks.add('lfs', wrapper._prefetchfiles)
346 scmutil.fileprefetchhooks.add('lfs', wrapper._prefetchfiles)
347
347
348 # Make bundle choose changegroup3 instead of changegroup2. This affects
348 # Make bundle choose changegroup3 instead of changegroup2. This affects
349 # "hg bundle" command. Note: it does not cover all bundle formats like
349 # "hg bundle" command. Note: it does not cover all bundle formats like
350 # "packed1". Using "packed1" with lfs will likely cause trouble.
350 # "packed1". Using "packed1" with lfs will likely cause trouble.
351 exchange._bundlespeccontentopts["v2"]["cg.version"] = "03"
351 exchange._bundlespeccontentopts["v2"]["cg.version"] = "03"
352
352
353 # bundlerepo uses "vfsmod.readonlyvfs(othervfs)", we need to make sure lfs
353 # bundlerepo uses "vfsmod.readonlyvfs(othervfs)", we need to make sure lfs
354 # options and blob stores are passed from othervfs to the new readonlyvfs.
354 # options and blob stores are passed from othervfs to the new readonlyvfs.
355 wrapfunction(vfsmod.readonlyvfs, '__init__', wrapper.vfsinit)
355 wrapfunction(vfsmod.readonlyvfs, '__init__', wrapper.vfsinit)
356
356
357 # when writing a bundle via "hg bundle" command, upload related LFS blobs
357 # when writing a bundle via "hg bundle" command, upload related LFS blobs
358 wrapfunction(bundle2, 'writenewbundle', wrapper.writenewbundle)
358 wrapfunction(bundle2, 'writenewbundle', wrapper.writenewbundle)
359
359
360 @filesetpredicate('lfs()', callstatus=True)
360 @filesetpredicate('lfs()')
361 def lfsfileset(mctx, x):
361 def lfsfileset(mctx, x):
362 """File that uses LFS storage."""
362 """File that uses LFS storage."""
363 # i18n: "lfs" is a keyword
363 # i18n: "lfs" is a keyword
364 fileset.getargs(x, 0, 0, _("lfs takes no arguments"))
364 fileset.getargs(x, 0, 0, _("lfs takes no arguments"))
365 ctx = mctx.ctx
365 ctx = mctx.ctx
366 def lfsfilep(f):
366 def lfsfilep(f):
367 return wrapper.pointerfromctx(ctx, f, removed=True) is not None
367 return wrapper.pointerfromctx(ctx, f, removed=True) is not None
368 return mctx.predicate(lfsfilep, predrepr='<lfs>')
368 return mctx.predicate(lfsfilep, predrepr='<lfs>')
369
369
370 @templatekeyword('lfs_files', requires={'ctx'})
370 @templatekeyword('lfs_files', requires={'ctx'})
371 def lfsfiles(context, mapping):
371 def lfsfiles(context, mapping):
372 """List of strings. All files modified, added, or removed by this
372 """List of strings. All files modified, added, or removed by this
373 changeset."""
373 changeset."""
374 ctx = context.resource(mapping, 'ctx')
374 ctx = context.resource(mapping, 'ctx')
375
375
376 pointers = wrapper.pointersfromctx(ctx, removed=True) # {path: pointer}
376 pointers = wrapper.pointersfromctx(ctx, removed=True) # {path: pointer}
377 files = sorted(pointers.keys())
377 files = sorted(pointers.keys())
378
378
379 def pointer(v):
379 def pointer(v):
380 # In the file spec, version is first and the other keys are sorted.
380 # In the file spec, version is first and the other keys are sorted.
381 sortkeyfunc = lambda x: (x[0] != 'version', x)
381 sortkeyfunc = lambda x: (x[0] != 'version', x)
382 items = sorted(pointers[v].iteritems(), key=sortkeyfunc)
382 items = sorted(pointers[v].iteritems(), key=sortkeyfunc)
383 return util.sortdict(items)
383 return util.sortdict(items)
384
384
385 makemap = lambda v: {
385 makemap = lambda v: {
386 'file': v,
386 'file': v,
387 'lfsoid': pointers[v].oid() if pointers[v] else None,
387 'lfsoid': pointers[v].oid() if pointers[v] else None,
388 'lfspointer': templateutil.hybriddict(pointer(v)),
388 'lfspointer': templateutil.hybriddict(pointer(v)),
389 }
389 }
390
390
391 # TODO: make the separator ', '?
391 # TODO: make the separator ', '?
392 f = templateutil._showcompatlist(context, mapping, 'lfs_file', files)
392 f = templateutil._showcompatlist(context, mapping, 'lfs_file', files)
393 return templateutil.hybrid(f, files, makemap, pycompat.identity)
393 return templateutil.hybrid(f, files, makemap, pycompat.identity)
394
394
395 @command('debuglfsupload',
395 @command('debuglfsupload',
396 [('r', 'rev', [], _('upload large files introduced by REV'))])
396 [('r', 'rev', [], _('upload large files introduced by REV'))])
397 def debuglfsupload(ui, repo, **opts):
397 def debuglfsupload(ui, repo, **opts):
398 """upload lfs blobs added by the working copy parent or given revisions"""
398 """upload lfs blobs added by the working copy parent or given revisions"""
399 revs = opts.get(r'rev', [])
399 revs = opts.get(r'rev', [])
400 pointers = wrapper.extractpointers(repo, scmutil.revrange(repo, revs))
400 pointers = wrapper.extractpointers(repo, scmutil.revrange(repo, revs))
401 wrapper.uploadblobs(repo, pointers)
401 wrapper.uploadblobs(repo, pointers)
General Comments 0
You need to be logged in to leave comments. Login now