Show More
@@ -7,6 +7,30 b'' | |||||
7 |
|
7 | |||
8 | """lfs - large file support (EXPERIMENTAL) |
|
8 | """lfs - large file support (EXPERIMENTAL) | |
9 |
|
9 | |||
|
10 | The extension reads its configuration from a versioned ``.hglfs`` | |||
|
11 | configuration file found in the root of the working directory. The | |||
|
12 | ``.hglfs`` file uses the same syntax as all other Mercurial | |||
|
13 | configuration files. It uses a single section, ``[track]``. | |||
|
14 | ||||
|
15 | The ``[track]`` section specifies which files are stored as LFS (or | |||
|
16 | not). Each line is keyed by a file pattern, with a predicate value. | |||
|
17 | The first file pattern match is used, so put more specific patterns | |||
|
18 | first. The available predicates are ``all()``, ``none()``, and | |||
|
19 | ``size()``. See "hg help filesets.size" for the latter. | |||
|
20 | ||||
|
21 | Example versioned ``.hglfs`` file:: | |||
|
22 | ||||
|
23 | [track] | |||
|
24 | # No Makefile or python file, anywhere, will be LFS | |||
|
25 | **Makefile = none() | |||
|
26 | **.py = none() | |||
|
27 | ||||
|
28 | **.zip = all() | |||
|
29 | **.exe = size(">1MB") | |||
|
30 | ||||
|
31 | # Catchall for everything not matched above | |||
|
32 | ** = size(">10MB") | |||
|
33 | ||||
10 | Configs:: |
|
34 | Configs:: | |
11 |
|
35 | |||
12 | [lfs] |
|
36 | [lfs] | |
@@ -35,6 +59,9 b' Configs::' | |||||
35 | # - (**.php & size(">2MB")) | (**.js & size(">5MB")) | **.tar.gz |
|
59 | # - (**.php & size(">2MB")) | (**.js & size(">5MB")) | **.tar.gz | |
36 | # | ("path:bin" & !"path:/bin/README") | size(">1GB") |
|
60 | # | ("path:bin" & !"path:/bin/README") | size(">1GB") | |
37 | # (default: none()) |
|
61 | # (default: none()) | |
|
62 | # | |||
|
63 | # This is ignored if there is a tracked '.hglfs' file, and this setting | |||
|
64 | # will eventually be deprecated and removed. | |||
38 | track = size(">10M") |
|
65 | track = size(">10M") | |
39 |
|
66 | |||
40 | # how many times to retry before giving up on transferring an object |
|
67 | # how many times to retry before giving up on transferring an object | |
@@ -53,7 +80,9 b' from mercurial import (' | |||||
53 | bundle2, |
|
80 | bundle2, | |
54 | changegroup, |
|
81 | changegroup, | |
55 | cmdutil, |
|
82 | cmdutil, | |
|
83 | config, | |||
56 | context, |
|
84 | context, | |
|
85 | error, | |||
57 | exchange, |
|
86 | exchange, | |
58 | extensions, |
|
87 | extensions, | |
59 | filelog, |
|
88 | filelog, | |
@@ -124,13 +153,20 b' def reposetup(ui, repo):' | |||||
124 | if not repo.local(): |
|
153 | if not repo.local(): | |
125 | return |
|
154 | return | |
126 |
|
155 | |||
127 | repo.svfs.options['lfstrack'] = _trackedmatcher(repo) |
|
|||
128 | repo.svfs.lfslocalblobstore = blobstore.local(repo) |
|
156 | repo.svfs.lfslocalblobstore = blobstore.local(repo) | |
129 | repo.svfs.lfsremoteblobstore = blobstore.remote(repo) |
|
157 | repo.svfs.lfsremoteblobstore = blobstore.remote(repo) | |
130 |
|
158 | |||
131 | # Push hook |
|
159 | # Push hook | |
132 | repo.prepushoutgoinghooks.add('lfs', wrapper.prepush) |
|
160 | repo.prepushoutgoinghooks.add('lfs', wrapper.prepush) | |
133 |
|
161 | |||
|
162 | class lfsrepo(repo.__class__): | |||
|
163 | @localrepo.unfilteredmethod | |||
|
164 | def commitctx(self, ctx, error=False): | |||
|
165 | repo.svfs.options['lfstrack'] = _trackedmatcher(self, ctx) | |||
|
166 | return super(lfsrepo, self).commitctx(ctx, error) | |||
|
167 | ||||
|
168 | repo.__class__ = lfsrepo | |||
|
169 | ||||
134 | if 'lfs' not in repo.requirements: |
|
170 | if 'lfs' not in repo.requirements: | |
135 | def checkrequireslfs(ui, repo, **kwargs): |
|
171 | def checkrequireslfs(ui, repo, **kwargs): | |
136 | if 'lfs' not in repo.requirements: |
|
172 | if 'lfs' not in repo.requirements: | |
@@ -150,18 +186,58 b' def reposetup(ui, repo):' | |||||
150 | ui.setconfig('hooks', 'commit.lfs', checkrequireslfs, 'lfs') |
|
186 | ui.setconfig('hooks', 'commit.lfs', checkrequireslfs, 'lfs') | |
151 | ui.setconfig('hooks', 'pretxnchangegroup.lfs', checkrequireslfs, 'lfs') |
|
187 | ui.setconfig('hooks', 'pretxnchangegroup.lfs', checkrequireslfs, 'lfs') | |
152 |
|
188 | |||
153 | def _trackedmatcher(repo): |
|
189 | def _trackedmatcher(repo, ctx): | |
154 | """Return a function (path, size) -> bool indicating whether or not to |
|
190 | """Return a function (path, size) -> bool indicating whether or not to | |
155 | track a given file with lfs.""" |
|
191 | track a given file with lfs.""" | |
156 | trackspec = repo.ui.config('lfs', 'track') |
|
192 | data = '' | |
|
193 | ||||
|
194 | if '.hglfs' in ctx.added() or '.hglfs' in ctx.modified(): | |||
|
195 | data = ctx['.hglfs'].data() | |||
|
196 | elif '.hglfs' not in ctx.removed(): | |||
|
197 | p1 = repo['.'] | |||
|
198 | ||||
|
199 | if '.hglfs' not in p1: | |||
|
200 | # No '.hglfs' in wdir or in parent. Fallback to config | |||
|
201 | # for now. | |||
|
202 | trackspec = repo.ui.config('lfs', 'track') | |||
|
203 | ||||
|
204 | # deprecated config: lfs.threshold | |||
|
205 | threshold = repo.ui.configbytes('lfs', 'threshold') | |||
|
206 | if threshold: | |||
|
207 | fileset.parse(trackspec) # make sure syntax errors are confined | |||
|
208 | trackspec = "(%s) | size('>%d')" % (trackspec, threshold) | |||
|
209 | ||||
|
210 | return minifileset.compile(trackspec) | |||
|
211 | ||||
|
212 | data = p1['.hglfs'].data() | |||
157 |
|
213 | |||
158 | # deprecated config: lfs.threshold |
|
214 | # In removed, or not in parent | |
159 | threshold = repo.ui.configbytes('lfs', 'threshold') |
|
215 | if not data: | |
160 | if threshold: |
|
216 | return lambda p, s: False | |
161 | fileset.parse(trackspec) # make sure syntax errors are confined |
|
217 | ||
162 | trackspec = "(%s) | size('>%d')" % (trackspec, threshold) |
|
218 | # Parse errors here will abort with a message that points to the .hglfs file | |
|
219 | # and line number. | |||
|
220 | cfg = config.config() | |||
|
221 | cfg.parse('.hglfs', data) | |||
163 |
|
222 | |||
164 | return minifileset.compile(trackspec) |
|
223 | try: | |
|
224 | rules = [(minifileset.compile(pattern), minifileset.compile(rule)) | |||
|
225 | for pattern, rule in cfg.items('track')] | |||
|
226 | except error.ParseError as e: | |||
|
227 | # The original exception gives no indicator that the error is in the | |||
|
228 | # .hglfs file, so add that. | |||
|
229 | ||||
|
230 | # TODO: See if the line number of the file can be made available. | |||
|
231 | raise error.Abort(_('parse error in .hglfs: %s') % e) | |||
|
232 | ||||
|
233 | def _match(path, size): | |||
|
234 | for pat, rule in rules: | |||
|
235 | if pat(path, size): | |||
|
236 | return rule(path, size) | |||
|
237 | ||||
|
238 | return False | |||
|
239 | ||||
|
240 | return _match | |||
165 |
|
241 | |||
166 | def wrapfilelog(filelog): |
|
242 | def wrapfilelog(filelog): | |
167 | wrapfunction = extensions.wrapfunction |
|
243 | wrapfunction = extensions.wrapfunction |
@@ -937,6 +937,79 b' Committing deleted files works:' | |||||
937 | $ hg commit -m 'add A' -A A |
|
937 | $ hg commit -m 'add A' -A A | |
938 | $ hg rm A |
|
938 | $ hg rm A | |
939 | $ hg commit -m 'rm A' |
|
939 | $ hg commit -m 'rm A' | |
|
940 | ||||
|
941 | Bad .hglfs files will block the commit with a useful message | |||
|
942 | ||||
|
943 | $ cat > .hglfs << EOF | |||
|
944 | > [track] | |||
|
945 | > **.test = size(">5B") | |||
|
946 | > bad file ... no commit | |||
|
947 | > EOF | |||
|
948 | ||||
|
949 | $ echo x > file.txt | |||
|
950 | $ hg ci -Aqm 'should fail' | |||
|
951 | hg: parse error at .hglfs:3: bad file ... no commit | |||
|
952 | [255] | |||
|
953 | ||||
|
954 | $ cat > .hglfs << EOF | |||
|
955 | > [track] | |||
|
956 | > **.test = size(">5B") | |||
|
957 | > ** = nonexistent() | |||
|
958 | > EOF | |||
|
959 | ||||
|
960 | $ hg ci -Aqm 'should fail' | |||
|
961 | abort: parse error in .hglfs: unknown identifier: nonexistent | |||
|
962 | [255] | |||
|
963 | ||||
|
964 | '**' works out to mean all files. | |||
|
965 | ||||
|
966 | $ cat > .hglfs << EOF | |||
|
967 | > [track] | |||
|
968 | > **.test = size(">5B") | |||
|
969 | > **.exclude = none() | |||
|
970 | > ** = size(">10B") | |||
|
971 | > EOF | |||
|
972 | ||||
|
973 | The LFS policy takes effect as the .hglfs file is committed | |||
|
974 | ||||
|
975 | $ echo 'largefile' > lfs.test | |||
|
976 | $ echo '012345678901234567890' > nolfs.exclude | |||
|
977 | $ echo '01234567890123456' > lfs.catchall | |||
|
978 | $ hg ci -Aqm 'added .hglfs' | |||
|
979 | $ hg log -r . -T '{rev}: {lfs_files % "{file}: {oid}\n"}\n' | |||
|
980 | 2: lfs.catchall: d4ec46c2869ba22eceb42a729377432052d9dd75d82fc40390ebaadecee87ee9 | |||
|
981 | lfs.test: 5489e6ced8c36a7b267292bde9fd5242a5f80a7482e8f23fa0477393dfaa4d6c | |||
|
982 | ||||
|
983 | The existing .hglfs file is used even when it is not in the 'A' or 'M' states | |||
|
984 | ||||
|
985 | $ echo 'largefile2' > lfs.test | |||
|
986 | $ echo '012345678901234567890a' > nolfs.exclude | |||
|
987 | $ echo '01234567890123456a' > lfs.catchall | |||
|
988 | $ hg ci -qm 'unmodified .hglfs' | |||
|
989 | $ hg log -r . -T '{rev}: {lfs_files % "{file}: {oid}\n"}\n' | |||
|
990 | 3: lfs.catchall: 31f43b9c62b540126b0ad5884dc013d21a61c9329b77de1fceeae2fc58511573 | |||
|
991 | lfs.test: 8acd23467967bc7b8cc5a280056589b0ba0b17ff21dbd88a7b6474d6290378a6 | |||
|
992 | ||||
|
993 | Excluding the .hglfs file from the commit postpones the policy change | |||
|
994 | ||||
|
995 | $ hg rm .hglfs | |||
|
996 | $ echo 'largefile3' > lfs.test | |||
|
997 | $ echo '012345678901234567890abc' > nolfs.exclude | |||
|
998 | $ echo '01234567890123456abc' > lfs.catchall | |||
|
999 | $ hg ci -qm 'file test' -X .hglfs | |||
|
1000 | $ hg log -r . -T '{rev}: {lfs_files % "{file}: {oid}\n"}\n' | |||
|
1001 | 4: lfs.catchall: 6747cfb1b83965b4a884e7a6061813ae31e4122028bc6a88d2ac5e5f9e05c5af | |||
|
1002 | lfs.test: 3f40b70c2294e91e0fa789ebcf73c5a1d1c7aef270f83e477e40cb0513237e8c | |||
|
1003 | ||||
|
1004 | The policy change takes effect when the .hglfs is committed | |||
|
1005 | ||||
|
1006 | $ echo 'largefile4' > lfs.test | |||
|
1007 | $ echo '012345678901234567890abcdef' > nolfs.exclude | |||
|
1008 | $ echo '01234567890123456abcdef' > lfs.catchall | |||
|
1009 | $ hg ci -qm 'file test' | |||
|
1010 | $ hg log -r . -T '{rev}: {lfs_files % "{file}: {oid}\n"}\n' | |||
|
1011 | 5: | |||
|
1012 | ||||
940 | $ cd .. |
|
1013 | $ cd .. | |
941 |
|
1014 | |||
942 | Unbundling adds a requirement to a non-lfs repo, if necessary. |
|
1015 | Unbundling adds a requirement to a non-lfs repo, if necessary. |
General Comments 0
You need to be logged in to leave comments.
Login now