##// END OF EJS Templates
largefiles: opts appears to already be bytes in this instance...
Augie Fackler -
r37773:88675432 default
parent child Browse files
Show More
@@ -1,450 +1,451 b''
1 1 test-abort-checkin.t
2 2 test-add.t
3 3 test-addremove-similar.t
4 4 test-addremove.t
5 5 test-amend-subrepo.t
6 6 test-amend.t
7 7 test-ancestor.py
8 8 test-annotate.py
9 9 test-annotate.t
10 10 test-archive-symlinks.t
11 11 test-atomictempfile.py
12 12 test-audit-path.t
13 13 test-audit-subrepo.t
14 14 test-automv.t
15 15 test-backout.t
16 16 test-backwards-remove.t
17 17 test-basic.t
18 18 test-bheads.t
19 19 test-bisect.t
20 20 test-bisect2.t
21 21 test-bisect3.t
22 22 test-blackbox.t
23 23 test-bookmarks-current.t
24 24 test-bookmarks-merge.t
25 25 test-bookmarks-rebase.t
26 26 test-bookmarks-strip.t
27 27 test-bookmarks.t
28 28 test-branch-change.t
29 29 test-branch-option.t
30 30 test-branch-tag-confict.t
31 31 test-branches.t
32 32 test-bundle-phases.t
33 33 test-bundle-type.t
34 34 test-bundle-vs-outgoing.t
35 35 test-bundle2-multiple-changegroups.t
36 36 test-cappedreader.py
37 37 test-casecollision.t
38 38 test-cat.t
39 39 test-censor.t
40 40 test-changelog-exec.t
41 41 test-check-commit.t
42 42 test-check-execute.t
43 43 test-check-module-imports.t
44 44 test-check-pyflakes.t
45 45 test-check-pylint.t
46 46 test-check-shbang.t
47 47 test-children.t
48 48 test-clone-cgi.t
49 49 test-clone-pull-corruption.t
50 50 test-clone-r.t
51 51 test-clone-update-order.t
52 52 test-command-template.t
53 53 test-commit-amend.t
54 54 test-commit-interactive.t
55 55 test-commit-multiple.t
56 56 test-commit-unresolved.t
57 57 test-commit.t
58 58 test-committer.t
59 59 test-completion.t
60 60 test-config-env.py
61 61 test-config.t
62 62 test-conflict.t
63 63 test-confused-revert.t
64 64 test-contrib-check-code.t
65 65 test-contrib-check-commit.t
66 66 test-convert-authormap.t
67 67 test-convert-clonebranches.t
68 68 test-convert-datesort.t
69 69 test-convert-filemap.t
70 70 test-convert-hg-sink.t
71 71 test-convert-hg-source.t
72 72 test-convert-hg-startrev.t
73 73 test-convert-splicemap.t
74 74 test-convert-tagsbranch-topology.t
75 75 test-copy-move-merge.t
76 76 test-copy.t
77 77 test-copytrace-heuristics.t
78 78 test-debugbuilddag.t
79 79 test-debugbundle.t
80 80 test-debugextensions.t
81 81 test-debugindexdot.t
82 82 test-debugrename.t
83 83 test-default-push.t
84 84 test-diff-binary-file.t
85 85 test-diff-change.t
86 86 test-diff-copy-depth.t
87 87 test-diff-hashes.t
88 88 test-diff-ignore-whitespace.t
89 89 test-diff-indent-heuristic.t
90 90 test-diff-issue2761.t
91 91 test-diff-newlines.t
92 92 test-diff-reverse.t
93 93 test-diff-subdir.t
94 94 test-diff-unified.t
95 95 test-diff-upgrade.t
96 96 test-diffdir.t
97 97 test-diffstat.t
98 98 test-directaccess.t
99 99 test-dirstate-backup.t
100 100 test-dirstate-nonnormalset.t
101 101 test-dirstate.t
102 102 test-doctest.py
103 103 test-double-merge.t
104 104 test-drawdag.t
105 105 test-duplicateoptions.py
106 106 test-editor-filename.t
107 107 test-empty-dir.t
108 108 test-empty-file.t
109 109 test-empty-group.t
110 110 test-empty.t
111 111 test-encode.t
112 112 test-encoding-func.py
113 113 test-encoding.t
114 114 test-eol-add.t
115 115 test-eol-clone.t
116 116 test-eol-hook.t
117 117 test-eol-tag.t
118 118 test-eol-update.t
119 119 test-excessive-merge.t
120 120 test-exchange-obsmarkers-case-A1.t
121 121 test-exchange-obsmarkers-case-A2.t
122 122 test-exchange-obsmarkers-case-A3.t
123 123 test-exchange-obsmarkers-case-A4.t
124 124 test-exchange-obsmarkers-case-A5.t
125 125 test-exchange-obsmarkers-case-A6.t
126 126 test-exchange-obsmarkers-case-A7.t
127 127 test-exchange-obsmarkers-case-B1.t
128 128 test-exchange-obsmarkers-case-B2.t
129 129 test-exchange-obsmarkers-case-B3.t
130 130 test-exchange-obsmarkers-case-B4.t
131 131 test-exchange-obsmarkers-case-B5.t
132 132 test-exchange-obsmarkers-case-B6.t
133 133 test-exchange-obsmarkers-case-B7.t
134 134 test-exchange-obsmarkers-case-C1.t
135 135 test-exchange-obsmarkers-case-C2.t
136 136 test-exchange-obsmarkers-case-C3.t
137 137 test-exchange-obsmarkers-case-C4.t
138 138 test-exchange-obsmarkers-case-D1.t
139 139 test-exchange-obsmarkers-case-D2.t
140 140 test-exchange-obsmarkers-case-D3.t
141 141 test-exchange-obsmarkers-case-D4.t
142 142 test-execute-bit.t
143 143 test-export.t
144 144 test-extdata.t
145 145 test-extdiff.t
146 146 test-extra-filelog-entry.t
147 147 test-filebranch.t
148 148 test-fileset-generated.t
149 149 test-flags.t
150 150 test-generaldelta.t
151 151 test-getbundle.t
152 152 test-git-export.t
153 153 test-glog-topological.t
154 154 test-gpg.t
155 155 test-graft.t
156 156 test-hg-parseurl.py
157 157 test-hghave.t
158 158 test-hgignore.t
159 159 test-hgk.t
160 160 test-hgweb-bundle.t
161 161 test-hgweb-descend-empties.t
162 162 test-hgweb-empty.t
163 163 test-hgweb-removed.t
164 164 test-histedit-arguments.t
165 165 test-histedit-base.t
166 166 test-histedit-bookmark-motion.t
167 167 test-histedit-commute.t
168 168 test-histedit-drop.t
169 169 test-histedit-edit.t
170 170 test-histedit-fold-non-commute.t
171 171 test-histedit-fold.t
172 172 test-histedit-no-change.t
173 173 test-histedit-non-commute-abort.t
174 174 test-histedit-non-commute.t
175 175 test-histedit-obsolete.t
176 176 test-histedit-outgoing.t
177 177 test-histedit-templates.t
178 178 test-http-branchmap.t
179 179 test-http-bundle1.t
180 180 test-http-clone-r.t
181 181 test-http.t
182 182 test-identify.t
183 183 test-import-unknown.t
184 184 test-import.t
185 185 test-imports-checker.t
186 186 test-incoming-outgoing.t
187 187 test-inherit-mode.t
188 188 test-issue1089.t
189 189 test-issue1102.t
190 190 test-issue1175.t
191 191 test-issue1306.t
192 192 test-issue1438.t
193 193 test-issue1502.t
194 194 test-issue1802.t
195 195 test-issue1877.t
196 196 test-issue1993.t
197 197 test-issue2137.t
198 198 test-issue3084.t
199 199 test-issue4074.t
200 200 test-issue522.t
201 201 test-issue586.t
202 202 test-issue612.t
203 203 test-issue619.t
204 204 test-issue660.t
205 205 test-issue672.t
206 206 test-issue842.t
207 207 test-journal-exists.t
208 208 test-journal-share.t
209 209 test-journal.t
210 210 test-largefiles-cache.t
211 211 test-largefiles-misc.t
212 212 test-largefiles-small-disk.t
213 213 test-largefiles-update.t
214 test-largefiles.t
214 215 test-lfs-largefiles.t
215 216 test-linerange.py
216 217 test-locate.t
217 218 test-lock-badness.t
218 219 test-log-linerange.t
219 220 test-log.t
220 221 test-logexchange.t
221 222 test-lrucachedict.py
222 223 test-mactext.t
223 224 test-mailmap.t
224 225 test-manifest-merging.t
225 226 test-manifest.py
226 227 test-manifest.t
227 228 test-match.py
228 229 test-mdiff.py
229 230 test-merge-changedelete.t
230 231 test-merge-closedheads.t
231 232 test-merge-commit.t
232 233 test-merge-criss-cross.t
233 234 test-merge-default.t
234 235 test-merge-force.t
235 236 test-merge-halt.t
236 237 test-merge-internal-tools-pattern.t
237 238 test-merge-local.t
238 239 test-merge-remove.t
239 240 test-merge-revert.t
240 241 test-merge-revert2.t
241 242 test-merge-subrepos.t
242 243 test-merge-symlinks.t
243 244 test-merge-tools.t
244 245 test-merge-types.t
245 246 test-merge1.t
246 247 test-merge10.t
247 248 test-merge2.t
248 249 test-merge4.t
249 250 test-merge5.t
250 251 test-merge6.t
251 252 test-merge7.t
252 253 test-merge8.t
253 254 test-merge9.t
254 255 test-mq-git.t
255 256 test-mq-header-date.t
256 257 test-mq-header-from.t
257 258 test-mq-merge.t
258 259 test-mq-pull-from-bundle.t
259 260 test-mq-qdelete.t
260 261 test-mq-qdiff.t
261 262 test-mq-qfold.t
262 263 test-mq-qgoto.t
263 264 test-mq-qimport-fail-cleanup.t
264 265 test-mq-qnew.t
265 266 test-mq-qpush-exact.t
266 267 test-mq-qqueue.t
267 268 test-mq-qrefresh-interactive.t
268 269 test-mq-qrefresh-replace-log-message.t
269 270 test-mq-qrefresh.t
270 271 test-mq-qrename.t
271 272 test-mq-qsave.t
272 273 test-mq-safety.t
273 274 test-mq-subrepo.t
274 275 test-mq-symlinks.t
275 276 test-mv-cp-st-diff.t
276 277 test-narrow-archive.t
277 278 test-narrow-clone-no-ellipsis.t
278 279 test-narrow-clone-non-narrow-server.t
279 280 test-narrow-clone-nonlinear.t
280 281 test-narrow-clone.t
281 282 test-narrow-commit.t
282 283 test-narrow-copies.t
283 284 test-narrow-debugcommands.t
284 285 test-narrow-debugrebuilddirstate.t
285 286 test-narrow-exchange-merges.t
286 287 test-narrow-exchange.t
287 288 test-narrow-expanddirstate.t
288 289 test-narrow-merge.t
289 290 test-narrow-patch.t
290 291 test-narrow-patterns.t
291 292 test-narrow-pull.t
292 293 test-narrow-rebase.t
293 294 test-narrow-shallow-merges.t
294 295 test-narrow-shallow.t
295 296 test-narrow-strip.t
296 297 test-narrow-update.t
297 298 test-nested-repo.t
298 299 test-newbranch.t
299 300 test-obshistory.t
300 301 test-obsmarker-template.t
301 302 test-obsmarkers-effectflag.t
302 303 test-obsolete-bundle-strip.t
303 304 test-obsolete-changeset-exchange.t
304 305 test-obsolete-checkheads.t
305 306 test-obsolete-distributed.t
306 307 test-obsolete-tag-cache.t
307 308 test-parents.t
308 309 test-pathconflicts-merge.t
309 310 test-pathconflicts-update.t
310 311 test-pending.t
311 312 test-permissions.t
312 313 test-phases.t
313 314 test-pull-branch.t
314 315 test-pull-http.t
315 316 test-pull-permission.t
316 317 test-pull-pull-corruption.t
317 318 test-pull-r.t
318 319 test-pull-update.t
319 320 test-purge.t
320 321 test-push-checkheads-partial-C1.t
321 322 test-push-checkheads-partial-C2.t
322 323 test-push-checkheads-partial-C3.t
323 324 test-push-checkheads-partial-C4.t
324 325 test-push-checkheads-pruned-B1.t
325 326 test-push-checkheads-pruned-B2.t
326 327 test-push-checkheads-pruned-B3.t
327 328 test-push-checkheads-pruned-B4.t
328 329 test-push-checkheads-pruned-B5.t
329 330 test-push-checkheads-pruned-B6.t
330 331 test-push-checkheads-pruned-B7.t
331 332 test-push-checkheads-pruned-B8.t
332 333 test-push-checkheads-superceed-A1.t
333 334 test-push-checkheads-superceed-A2.t
334 335 test-push-checkheads-superceed-A3.t
335 336 test-push-checkheads-superceed-A4.t
336 337 test-push-checkheads-superceed-A5.t
337 338 test-push-checkheads-superceed-A6.t
338 339 test-push-checkheads-superceed-A7.t
339 340 test-push-checkheads-superceed-A8.t
340 341 test-push-checkheads-unpushed-D1.t
341 342 test-push-checkheads-unpushed-D2.t
342 343 test-push-checkheads-unpushed-D3.t
343 344 test-push-checkheads-unpushed-D4.t
344 345 test-push-checkheads-unpushed-D5.t
345 346 test-push-checkheads-unpushed-D6.t
346 347 test-push-checkheads-unpushed-D7.t
347 348 test-push-http.t
348 349 test-push-warn.t
349 350 test-pushvars.t
350 351 test-rebase-abort.t
351 352 test-rebase-base-flag.t
352 353 test-rebase-bookmarks.t
353 354 test-rebase-brute-force.t
354 355 test-rebase-cache.t
355 356 test-rebase-check-restore.t
356 357 test-rebase-collapse.t
357 358 test-rebase-conflicts.t
358 359 test-rebase-dest.t
359 360 test-rebase-detach.t
360 361 test-rebase-emptycommit.t
361 362 test-rebase-inmemory.t
362 363 test-rebase-interruptions.t
363 364 test-rebase-issue-noparam-single-rev.t
364 365 test-rebase-legacy.t
365 366 test-rebase-mq-skip.t
366 367 test-rebase-mq.t
367 368 test-rebase-named-branches.t
368 369 test-rebase-newancestor.t
369 370 test-rebase-obsolete.t
370 371 test-rebase-parameters.t
371 372 test-rebase-partial.t
372 373 test-rebase-pull.t
373 374 test-rebase-rename.t
374 375 test-rebase-scenario-global.t
375 376 test-rebase-templates.t
376 377 test-rebase-transaction.t
377 378 test-record.t
378 379 test-relink.t
379 380 test-remove.t
380 381 test-rename-after-merge.t
381 382 test-rename-dir-merge.t
382 383 test-rename-merge1.t
383 384 test-rename.t
384 385 test-repair-strip.t
385 386 test-repo-compengines.t
386 387 test-resolve.t
387 388 test-revert-flags.t
388 389 test-revert-unknown.t
389 390 test-revlog-ancestry.py
390 391 test-revlog-group-emptyiter.t
391 392 test-revlog-mmapindex.t
392 393 test-revlog-packentry.t
393 394 test-revset-dirstate-parents.t
394 395 test-revset-outgoing.t
395 396 test-rollback.t
396 397 test-run-tests.py
397 398 test-run-tests.t
398 399 test-schemes.t
399 400 test-serve.t
400 401 test-setdiscovery.t
401 402 test-share.t
402 403 test-shelve.t
403 404 test-show-stack.t
404 405 test-show-work.t
405 406 test-show.t
406 407 test-simple-update.t
407 408 test-single-head.t
408 409 test-sparse-clear.t
409 410 test-sparse-import.t
410 411 test-sparse-merges.t
411 412 test-sparse-profiles.t
412 413 test-sparse-requirement.t
413 414 test-sparse-verbose-json.t
414 415 test-ssh-clone-r.t
415 416 test-ssh-proto.t
416 417 test-sshserver.py
417 418 test-stack.t
418 419 test-status-rev.t
419 420 test-status-terse.t
420 421 test-strip-cross.t
421 422 test-strip.t
422 423 test-subrepo-deep-nested-change.t
423 424 test-subrepo-missing.t
424 425 test-subrepo.t
425 426 test-symlinks.t
426 427 test-tag.t
427 428 test-tags.t
428 429 test-template-engine.t
429 430 test-treemanifest.t
430 431 test-unamend.t
431 432 test-uncommit.t
432 433 test-unified-test.t
433 434 test-unrelated-pull.t
434 435 test-up-local-change.t
435 436 test-update-branches.t
436 437 test-update-dest.t
437 438 test-update-issue1456.t
438 439 test-update-names.t
439 440 test-update-reverse.t
440 441 test-upgrade-repo.t
441 442 test-url-download.t
442 443 test-url-rev.t
443 444 test-username-newline.t
444 445 test-verify.t
445 446 test-websub.t
446 447 test-win32text.t
447 448 test-wireproto-clientreactor.py
448 449 test-wireproto-framing.py
449 450 test-wireproto-serverreactor.py
450 451 test-xdg.t
@@ -1,1495 +1,1495 b''
1 1 # Copyright 2009-2010 Gregory P. Ward
2 2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 3 # Copyright 2010-2011 Fog Creek Software
4 4 # Copyright 2010-2011 Unity Technologies
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 '''Overridden Mercurial commands and functions for the largefiles extension'''
10 10 from __future__ import absolute_import
11 11
12 12 import copy
13 13 import os
14 14
15 15 from mercurial.i18n import _
16 16
17 17 from mercurial import (
18 18 archival,
19 19 cmdutil,
20 20 error,
21 21 hg,
22 22 logcmdutil,
23 23 match as matchmod,
24 24 pathutil,
25 25 pycompat,
26 26 registrar,
27 27 scmutil,
28 28 smartset,
29 29 util,
30 30 )
31 31
32 32 from . import (
33 33 lfcommands,
34 34 lfutil,
35 35 storefactory,
36 36 )
37 37
38 38 # -- Utility functions: commonly/repeatedly needed functionality ---------------
39 39
40 40 def composelargefilematcher(match, manifest):
41 41 '''create a matcher that matches only the largefiles in the original
42 42 matcher'''
43 43 m = copy.copy(match)
44 44 lfile = lambda f: lfutil.standin(f) in manifest
45 45 m._files = [lf for lf in m._files if lfile(lf)]
46 46 m._fileset = set(m._files)
47 47 m.always = lambda: False
48 48 origmatchfn = m.matchfn
49 49 m.matchfn = lambda f: lfile(f) and origmatchfn(f)
50 50 return m
51 51
52 52 def composenormalfilematcher(match, manifest, exclude=None):
53 53 excluded = set()
54 54 if exclude is not None:
55 55 excluded.update(exclude)
56 56
57 57 m = copy.copy(match)
58 58 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
59 59 manifest or f in excluded)
60 60 m._files = [lf for lf in m._files if notlfile(lf)]
61 61 m._fileset = set(m._files)
62 62 m.always = lambda: False
63 63 origmatchfn = m.matchfn
64 64 m.matchfn = lambda f: notlfile(f) and origmatchfn(f)
65 65 return m
66 66
67 67 def installnormalfilesmatchfn(manifest):
68 68 '''installmatchfn with a matchfn that ignores all largefiles'''
69 69 def overridematch(ctx, pats=(), opts=None, globbed=False,
70 70 default='relpath', badfn=None):
71 71 if opts is None:
72 72 opts = {}
73 73 match = oldmatch(ctx, pats, opts, globbed, default, badfn=badfn)
74 74 return composenormalfilematcher(match, manifest)
75 75 oldmatch = installmatchfn(overridematch)
76 76
77 77 def installmatchfn(f):
78 78 '''monkey patch the scmutil module with a custom match function.
79 79 Warning: it is monkey patching the _module_ on runtime! Not thread safe!'''
80 80 oldmatch = scmutil.match
81 81 setattr(f, 'oldmatch', oldmatch)
82 82 scmutil.match = f
83 83 return oldmatch
84 84
85 85 def restorematchfn():
86 86 '''restores scmutil.match to what it was before installmatchfn
87 87 was called. no-op if scmutil.match is its original function.
88 88
89 89 Note that n calls to installmatchfn will require n calls to
90 90 restore the original matchfn.'''
91 91 scmutil.match = getattr(scmutil.match, 'oldmatch')
92 92
93 93 def installmatchandpatsfn(f):
94 94 oldmatchandpats = scmutil.matchandpats
95 95 setattr(f, 'oldmatchandpats', oldmatchandpats)
96 96 scmutil.matchandpats = f
97 97 return oldmatchandpats
98 98
99 99 def restorematchandpatsfn():
100 100 '''restores scmutil.matchandpats to what it was before
101 101 installmatchandpatsfn was called. No-op if scmutil.matchandpats
102 102 is its original function.
103 103
104 104 Note that n calls to installmatchandpatsfn will require n calls
105 105 to restore the original matchfn.'''
106 106 scmutil.matchandpats = getattr(scmutil.matchandpats, 'oldmatchandpats',
107 107 scmutil.matchandpats)
108 108
109 109 def addlargefiles(ui, repo, isaddremove, matcher, **opts):
110 110 large = opts.get(r'large')
111 111 lfsize = lfutil.getminsize(
112 112 ui, lfutil.islfilesrepo(repo), opts.get(r'lfsize'))
113 113
114 114 lfmatcher = None
115 115 if lfutil.islfilesrepo(repo):
116 116 lfpats = ui.configlist(lfutil.longname, 'patterns')
117 117 if lfpats:
118 118 lfmatcher = matchmod.match(repo.root, '', list(lfpats))
119 119
120 120 lfnames = []
121 121 m = matcher
122 122
123 123 wctx = repo[None]
124 124 for f in wctx.walk(matchmod.badmatch(m, lambda x, y: None)):
125 125 exact = m.exact(f)
126 126 lfile = lfutil.standin(f) in wctx
127 127 nfile = f in wctx
128 128 exists = lfile or nfile
129 129
130 130 # addremove in core gets fancy with the name, add doesn't
131 131 if isaddremove:
132 132 name = m.uipath(f)
133 133 else:
134 134 name = m.rel(f)
135 135
136 136 # Don't warn the user when they attempt to add a normal tracked file.
137 137 # The normal add code will do that for us.
138 138 if exact and exists:
139 139 if lfile:
140 140 ui.warn(_('%s already a largefile\n') % name)
141 141 continue
142 142
143 143 if (exact or not exists) and not lfutil.isstandin(f):
144 144 # In case the file was removed previously, but not committed
145 145 # (issue3507)
146 146 if not repo.wvfs.exists(f):
147 147 continue
148 148
149 149 abovemin = (lfsize and
150 150 repo.wvfs.lstat(f).st_size >= lfsize * 1024 * 1024)
151 151 if large or abovemin or (lfmatcher and lfmatcher(f)):
152 152 lfnames.append(f)
153 153 if ui.verbose or not exact:
154 154 ui.status(_('adding %s as a largefile\n') % name)
155 155
156 156 bad = []
157 157
158 158 # Need to lock, otherwise there could be a race condition between
159 159 # when standins are created and added to the repo.
160 160 with repo.wlock():
161 161 if not opts.get(r'dry_run'):
162 162 standins = []
163 163 lfdirstate = lfutil.openlfdirstate(ui, repo)
164 164 for f in lfnames:
165 165 standinname = lfutil.standin(f)
166 166 lfutil.writestandin(repo, standinname, hash='',
167 167 executable=lfutil.getexecutable(repo.wjoin(f)))
168 168 standins.append(standinname)
169 169 if lfdirstate[f] == 'r':
170 170 lfdirstate.normallookup(f)
171 171 else:
172 172 lfdirstate.add(f)
173 173 lfdirstate.write()
174 174 bad += [lfutil.splitstandin(f)
175 175 for f in repo[None].add(standins)
176 176 if f in m.files()]
177 177
178 178 added = [f for f in lfnames if f not in bad]
179 179 return added, bad
180 180
181 181 def removelargefiles(ui, repo, isaddremove, matcher, dryrun, **opts):
182 182 after = opts.get(r'after')
183 183 m = composelargefilematcher(matcher, repo[None].manifest())
184 184 try:
185 185 repo.lfstatus = True
186 186 s = repo.status(match=m, clean=not isaddremove)
187 187 finally:
188 188 repo.lfstatus = False
189 189 manifest = repo[None].manifest()
190 190 modified, added, deleted, clean = [[f for f in list
191 191 if lfutil.standin(f) in manifest]
192 192 for list in (s.modified, s.added,
193 193 s.deleted, s.clean)]
194 194
195 195 def warn(files, msg):
196 196 for f in files:
197 197 ui.warn(msg % m.rel(f))
198 198 return int(len(files) > 0)
199 199
200 200 result = 0
201 201
202 202 if after:
203 203 remove = deleted
204 204 result = warn(modified + added + clean,
205 205 _('not removing %s: file still exists\n'))
206 206 else:
207 207 remove = deleted + clean
208 208 result = warn(modified, _('not removing %s: file is modified (use -f'
209 209 ' to force removal)\n'))
210 210 result = warn(added, _('not removing %s: file has been marked for add'
211 211 ' (use forget to undo)\n')) or result
212 212
213 213 # Need to lock because standin files are deleted then removed from the
214 214 # repository and we could race in-between.
215 215 with repo.wlock():
216 216 lfdirstate = lfutil.openlfdirstate(ui, repo)
217 217 for f in sorted(remove):
218 218 if ui.verbose or not m.exact(f):
219 219 # addremove in core gets fancy with the name, remove doesn't
220 220 if isaddremove:
221 221 name = m.uipath(f)
222 222 else:
223 223 name = m.rel(f)
224 224 ui.status(_('removing %s\n') % name)
225 225
226 226 if not dryrun:
227 227 if not after:
228 228 repo.wvfs.unlinkpath(f, ignoremissing=True)
229 229
230 230 if dryrun:
231 231 return result
232 232
233 233 remove = [lfutil.standin(f) for f in remove]
234 234 # If this is being called by addremove, let the original addremove
235 235 # function handle this.
236 236 if not isaddremove:
237 237 for f in remove:
238 238 repo.wvfs.unlinkpath(f, ignoremissing=True)
239 239 repo[None].forget(remove)
240 240
241 241 for f in remove:
242 242 lfutil.synclfdirstate(repo, lfdirstate, lfutil.splitstandin(f),
243 243 False)
244 244
245 245 lfdirstate.write()
246 246
247 247 return result
248 248
249 249 # For overriding mercurial.hgweb.webcommands so that largefiles will
250 250 # appear at their right place in the manifests.
251 251 def decodepath(orig, path):
252 252 return lfutil.splitstandin(path) or path
253 253
254 254 # -- Wrappers: modify existing commands --------------------------------
255 255
256 256 def overrideadd(orig, ui, repo, *pats, **opts):
257 257 if opts.get(r'normal') and opts.get(r'large'):
258 258 raise error.Abort(_('--normal cannot be used with --large'))
259 259 return orig(ui, repo, *pats, **opts)
260 260
261 261 def cmdutiladd(orig, ui, repo, matcher, prefix, explicitonly, **opts):
262 262 # The --normal flag short circuits this override
263 263 if opts.get(r'normal'):
264 264 return orig(ui, repo, matcher, prefix, explicitonly, **opts)
265 265
266 266 ladded, lbad = addlargefiles(ui, repo, False, matcher, **opts)
267 267 normalmatcher = composenormalfilematcher(matcher, repo[None].manifest(),
268 268 ladded)
269 269 bad = orig(ui, repo, normalmatcher, prefix, explicitonly, **opts)
270 270
271 271 bad.extend(f for f in lbad)
272 272 return bad
273 273
274 274 def cmdutilremove(orig, ui, repo, matcher, prefix, after, force, subrepos,
275 275 dryrun):
276 276 normalmatcher = composenormalfilematcher(matcher, repo[None].manifest())
277 277 result = orig(ui, repo, normalmatcher, prefix, after, force, subrepos,
278 278 dryrun)
279 279 return removelargefiles(ui, repo, False, matcher, dryrun, after=after,
280 280 force=force) or result
281 281
282 282 def overridestatusfn(orig, repo, rev2, **opts):
283 283 try:
284 284 repo._repo.lfstatus = True
285 285 return orig(repo, rev2, **opts)
286 286 finally:
287 287 repo._repo.lfstatus = False
288 288
289 289 def overridestatus(orig, ui, repo, *pats, **opts):
290 290 try:
291 291 repo.lfstatus = True
292 292 return orig(ui, repo, *pats, **opts)
293 293 finally:
294 294 repo.lfstatus = False
295 295
296 296 def overridedirty(orig, repo, ignoreupdate=False, missing=False):
297 297 try:
298 298 repo._repo.lfstatus = True
299 299 return orig(repo, ignoreupdate=ignoreupdate, missing=missing)
300 300 finally:
301 301 repo._repo.lfstatus = False
302 302
303 303 def overridelog(orig, ui, repo, *pats, **opts):
304 304 def overridematchandpats(ctx, pats=(), opts=None, globbed=False,
305 305 default='relpath', badfn=None):
306 306 """Matcher that merges root directory with .hglf, suitable for log.
307 307 It is still possible to match .hglf directly.
308 308 For any listed files run log on the standin too.
309 309 matchfn tries both the given filename and with .hglf stripped.
310 310 """
311 311 if opts is None:
312 312 opts = {}
313 313 matchandpats = oldmatchandpats(ctx, pats, opts, globbed, default,
314 314 badfn=badfn)
315 315 m, p = copy.copy(matchandpats)
316 316
317 317 if m.always():
318 318 # We want to match everything anyway, so there's no benefit trying
319 319 # to add standins.
320 320 return matchandpats
321 321
322 322 pats = set(p)
323 323
324 324 def fixpats(pat, tostandin=lfutil.standin):
325 325 if pat.startswith('set:'):
326 326 return pat
327 327
328 328 kindpat = matchmod._patsplit(pat, None)
329 329
330 330 if kindpat[0] is not None:
331 331 return kindpat[0] + ':' + tostandin(kindpat[1])
332 332 return tostandin(kindpat[1])
333 333
334 334 if m._cwd:
335 335 hglf = lfutil.shortname
336 336 back = util.pconvert(m.rel(hglf)[:-len(hglf)])
337 337
338 338 def tostandin(f):
339 339 # The file may already be a standin, so truncate the back
340 340 # prefix and test before mangling it. This avoids turning
341 341 # 'glob:../.hglf/foo*' into 'glob:../.hglf/../.hglf/foo*'.
342 342 if f.startswith(back) and lfutil.splitstandin(f[len(back):]):
343 343 return f
344 344
345 345 # An absolute path is from outside the repo, so truncate the
346 346 # path to the root before building the standin. Otherwise cwd
347 347 # is somewhere in the repo, relative to root, and needs to be
348 348 # prepended before building the standin.
349 349 if os.path.isabs(m._cwd):
350 350 f = f[len(back):]
351 351 else:
352 352 f = m._cwd + '/' + f
353 353 return back + lfutil.standin(f)
354 354 else:
355 355 def tostandin(f):
356 356 if lfutil.isstandin(f):
357 357 return f
358 358 return lfutil.standin(f)
359 359 pats.update(fixpats(f, tostandin) for f in p)
360 360
361 361 for i in range(0, len(m._files)):
362 362 # Don't add '.hglf' to m.files, since that is already covered by '.'
363 363 if m._files[i] == '.':
364 364 continue
365 365 standin = lfutil.standin(m._files[i])
366 366 # If the "standin" is a directory, append instead of replace to
367 367 # support naming a directory on the command line with only
368 368 # largefiles. The original directory is kept to support normal
369 369 # files.
370 370 if standin in ctx:
371 371 m._files[i] = standin
372 372 elif m._files[i] not in ctx and repo.wvfs.isdir(standin):
373 373 m._files.append(standin)
374 374
375 375 m._fileset = set(m._files)
376 376 m.always = lambda: False
377 377 origmatchfn = m.matchfn
378 378 def lfmatchfn(f):
379 379 lf = lfutil.splitstandin(f)
380 380 if lf is not None and origmatchfn(lf):
381 381 return True
382 382 r = origmatchfn(f)
383 383 return r
384 384 m.matchfn = lfmatchfn
385 385
386 386 ui.debug('updated patterns: %s\n' % ', '.join(sorted(pats)))
387 387 return m, pats
388 388
389 389 # For hg log --patch, the match object is used in two different senses:
390 390 # (1) to determine what revisions should be printed out, and
391 391 # (2) to determine what files to print out diffs for.
392 392 # The magic matchandpats override should be used for case (1) but not for
393 393 # case (2).
394 394 def overridemakefilematcher(repo, pats, opts, badfn=None):
395 395 wctx = repo[None]
396 396 match, pats = oldmatchandpats(wctx, pats, opts, badfn=badfn)
397 397 return lambda ctx: match
398 398
399 399 oldmatchandpats = installmatchandpatsfn(overridematchandpats)
400 400 oldmakefilematcher = logcmdutil._makenofollowfilematcher
401 401 setattr(logcmdutil, '_makenofollowfilematcher', overridemakefilematcher)
402 402
403 403 try:
404 404 return orig(ui, repo, *pats, **opts)
405 405 finally:
406 406 restorematchandpatsfn()
407 407 setattr(logcmdutil, '_makenofollowfilematcher', oldmakefilematcher)
408 408
409 409 def overrideverify(orig, ui, repo, *pats, **opts):
410 410 large = opts.pop(r'large', False)
411 411 all = opts.pop(r'lfa', False)
412 412 contents = opts.pop(r'lfc', False)
413 413
414 414 result = orig(ui, repo, *pats, **opts)
415 415 if large or all or contents:
416 416 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
417 417 return result
418 418
419 419 def overridedebugstate(orig, ui, repo, *pats, **opts):
420 420 large = opts.pop(r'large', False)
421 421 if large:
422 422 class fakerepo(object):
423 423 dirstate = lfutil.openlfdirstate(ui, repo)
424 424 orig(ui, fakerepo, *pats, **opts)
425 425 else:
426 426 orig(ui, repo, *pats, **opts)
427 427
428 428 # Before starting the manifest merge, merge.updates will call
429 429 # _checkunknownfile to check if there are any files in the merged-in
430 430 # changeset that collide with unknown files in the working copy.
431 431 #
432 432 # The largefiles are seen as unknown, so this prevents us from merging
433 433 # in a file 'foo' if we already have a largefile with the same name.
434 434 #
435 435 # The overridden function filters the unknown files by removing any
436 436 # largefiles. This makes the merge proceed and we can then handle this
437 437 # case further in the overridden calculateupdates function below.
438 438 def overridecheckunknownfile(origfn, repo, wctx, mctx, f, f2=None):
439 439 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
440 440 return False
441 441 return origfn(repo, wctx, mctx, f, f2)
442 442
443 443 # The manifest merge handles conflicts on the manifest level. We want
444 444 # to handle changes in largefile-ness of files at this level too.
445 445 #
446 446 # The strategy is to run the original calculateupdates and then process
447 447 # the action list it outputs. There are two cases we need to deal with:
448 448 #
449 449 # 1. Normal file in p1, largefile in p2. Here the largefile is
450 450 # detected via its standin file, which will enter the working copy
451 451 # with a "get" action. It is not "merge" since the standin is all
452 452 # Mercurial is concerned with at this level -- the link to the
453 453 # existing normal file is not relevant here.
454 454 #
455 455 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
456 456 # since the largefile will be present in the working copy and
457 457 # different from the normal file in p2. Mercurial therefore
458 458 # triggers a merge action.
459 459 #
460 460 # In both cases, we prompt the user and emit new actions to either
461 461 # remove the standin (if the normal file was kept) or to remove the
462 462 # normal file and get the standin (if the largefile was kept). The
463 463 # default prompt answer is to use the largefile version since it was
464 464 # presumably changed on purpose.
465 465 #
466 466 # Finally, the merge.applyupdates function will then take care of
467 467 # writing the files into the working copy and lfcommands.updatelfiles
468 468 # will update the largefiles.
469 469 def overridecalculateupdates(origfn, repo, p1, p2, pas, branchmerge, force,
470 470 acceptremote, *args, **kwargs):
471 471 overwrite = force and not branchmerge
472 472 actions, diverge, renamedelete = origfn(
473 473 repo, p1, p2, pas, branchmerge, force, acceptremote, *args, **kwargs)
474 474
475 475 if overwrite:
476 476 return actions, diverge, renamedelete
477 477
478 478 # Convert to dictionary with filename as key and action as value.
479 479 lfiles = set()
480 480 for f in actions:
481 481 splitstandin = lfutil.splitstandin(f)
482 482 if splitstandin in p1:
483 483 lfiles.add(splitstandin)
484 484 elif lfutil.standin(f) in p1:
485 485 lfiles.add(f)
486 486
487 487 for lfile in sorted(lfiles):
488 488 standin = lfutil.standin(lfile)
489 489 (lm, largs, lmsg) = actions.get(lfile, (None, None, None))
490 490 (sm, sargs, smsg) = actions.get(standin, (None, None, None))
491 491 if sm in ('g', 'dc') and lm != 'r':
492 492 if sm == 'dc':
493 493 f1, f2, fa, move, anc = sargs
494 494 sargs = (p2[f2].flags(), False)
495 495 # Case 1: normal file in the working copy, largefile in
496 496 # the second parent
497 497 usermsg = _('remote turned local normal file %s into a largefile\n'
498 498 'use (l)argefile or keep (n)ormal file?'
499 499 '$$ &Largefile $$ &Normal file') % lfile
500 500 if repo.ui.promptchoice(usermsg, 0) == 0: # pick remote largefile
501 501 actions[lfile] = ('r', None, 'replaced by standin')
502 502 actions[standin] = ('g', sargs, 'replaces standin')
503 503 else: # keep local normal file
504 504 actions[lfile] = ('k', None, 'replaces standin')
505 505 if branchmerge:
506 506 actions[standin] = ('k', None, 'replaced by non-standin')
507 507 else:
508 508 actions[standin] = ('r', None, 'replaced by non-standin')
509 509 elif lm in ('g', 'dc') and sm != 'r':
510 510 if lm == 'dc':
511 511 f1, f2, fa, move, anc = largs
512 512 largs = (p2[f2].flags(), False)
513 513 # Case 2: largefile in the working copy, normal file in
514 514 # the second parent
515 515 usermsg = _('remote turned local largefile %s into a normal file\n'
516 516 'keep (l)argefile or use (n)ormal file?'
517 517 '$$ &Largefile $$ &Normal file') % lfile
518 518 if repo.ui.promptchoice(usermsg, 0) == 0: # keep local largefile
519 519 if branchmerge:
520 520 # largefile can be restored from standin safely
521 521 actions[lfile] = ('k', None, 'replaced by standin')
522 522 actions[standin] = ('k', None, 'replaces standin')
523 523 else:
524 524 # "lfile" should be marked as "removed" without
525 525 # removal of itself
526 526 actions[lfile] = ('lfmr', None,
527 527 'forget non-standin largefile')
528 528
529 529 # linear-merge should treat this largefile as 're-added'
530 530 actions[standin] = ('a', None, 'keep standin')
531 531 else: # pick remote normal file
532 532 actions[lfile] = ('g', largs, 'replaces standin')
533 533 actions[standin] = ('r', None, 'replaced by non-standin')
534 534
535 535 return actions, diverge, renamedelete
536 536
537 537 def mergerecordupdates(orig, repo, actions, branchmerge):
538 538 if 'lfmr' in actions:
539 539 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
540 540 for lfile, args, msg in actions['lfmr']:
541 541 # this should be executed before 'orig', to execute 'remove'
542 542 # before all other actions
543 543 repo.dirstate.remove(lfile)
544 544 # make sure lfile doesn't get synclfdirstate'd as normal
545 545 lfdirstate.add(lfile)
546 546 lfdirstate.write()
547 547
548 548 return orig(repo, actions, branchmerge)
549 549
550 550 # Override filemerge to prompt the user about how they wish to merge
551 551 # largefiles. This will handle identical edits without prompting the user.
552 552 def overridefilemerge(origfn, premerge, repo, wctx, mynode, orig, fcd, fco, fca,
553 553 labels=None):
554 554 if not lfutil.isstandin(orig) or fcd.isabsent() or fco.isabsent():
555 555 return origfn(premerge, repo, wctx, mynode, orig, fcd, fco, fca,
556 556 labels=labels)
557 557
558 558 ahash = lfutil.readasstandin(fca).lower()
559 559 dhash = lfutil.readasstandin(fcd).lower()
560 560 ohash = lfutil.readasstandin(fco).lower()
561 561 if (ohash != ahash and
562 562 ohash != dhash and
563 563 (dhash == ahash or
564 564 repo.ui.promptchoice(
565 565 _('largefile %s has a merge conflict\nancestor was %s\n'
566 566 'keep (l)ocal %s or\ntake (o)ther %s?'
567 567 '$$ &Local $$ &Other') %
568 568 (lfutil.splitstandin(orig), ahash, dhash, ohash),
569 569 0) == 1)):
570 570 repo.wwrite(fcd.path(), fco.data(), fco.flags())
571 571 return True, 0, False
572 572
573 573 def copiespathcopies(orig, ctx1, ctx2, match=None):
574 574 copies = orig(ctx1, ctx2, match=match)
575 575 updated = {}
576 576
577 577 for k, v in copies.iteritems():
578 578 updated[lfutil.splitstandin(k) or k] = lfutil.splitstandin(v) or v
579 579
580 580 return updated
581 581
582 582 # Copy first changes the matchers to match standins instead of
583 583 # largefiles. Then it overrides util.copyfile in that function it
584 584 # checks if the destination largefile already exists. It also keeps a
585 585 # list of copied files so that the largefiles can be copied and the
586 586 # dirstate updated.
587 587 def overridecopy(orig, ui, repo, pats, opts, rename=False):
588 588 # doesn't remove largefile on rename
589 589 if len(pats) < 2:
590 590 # this isn't legal, let the original function deal with it
591 591 return orig(ui, repo, pats, opts, rename)
592 592
593 593 # This could copy both lfiles and normal files in one command,
594 594 # but we don't want to do that. First replace their matcher to
595 595 # only match normal files and run it, then replace it to just
596 596 # match largefiles and run it again.
597 597 nonormalfiles = False
598 598 nolfiles = False
599 599 installnormalfilesmatchfn(repo[None].manifest())
600 600 try:
601 601 result = orig(ui, repo, pats, opts, rename)
602 602 except error.Abort as e:
603 603 if pycompat.bytestr(e) != _('no files to copy'):
604 604 raise e
605 605 else:
606 606 nonormalfiles = True
607 607 result = 0
608 608 finally:
609 609 restorematchfn()
610 610
611 611 # The first rename can cause our current working directory to be removed.
612 612 # In that case there is nothing left to copy/rename so just quit.
613 613 try:
614 614 repo.getcwd()
615 615 except OSError:
616 616 return result
617 617
618 618 def makestandin(relpath):
619 619 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
620 620 return repo.wvfs.join(lfutil.standin(path))
621 621
622 622 fullpats = scmutil.expandpats(pats)
623 623 dest = fullpats[-1]
624 624
625 625 if os.path.isdir(dest):
626 626 if not os.path.isdir(makestandin(dest)):
627 627 os.makedirs(makestandin(dest))
628 628
629 629 try:
630 630 # When we call orig below it creates the standins but we don't add
631 631 # them to the dir state until later so lock during that time.
632 632 wlock = repo.wlock()
633 633
634 634 manifest = repo[None].manifest()
635 635 def overridematch(ctx, pats=(), opts=None, globbed=False,
636 636 default='relpath', badfn=None):
637 637 if opts is None:
638 638 opts = {}
639 639 newpats = []
640 640 # The patterns were previously mangled to add the standin
641 641 # directory; we need to remove that now
642 642 for pat in pats:
643 643 if matchmod.patkind(pat) is None and lfutil.shortname in pat:
644 644 newpats.append(pat.replace(lfutil.shortname, ''))
645 645 else:
646 646 newpats.append(pat)
647 647 match = oldmatch(ctx, newpats, opts, globbed, default, badfn=badfn)
648 648 m = copy.copy(match)
649 649 lfile = lambda f: lfutil.standin(f) in manifest
650 650 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
651 651 m._fileset = set(m._files)
652 652 origmatchfn = m.matchfn
653 653 def matchfn(f):
654 654 lfile = lfutil.splitstandin(f)
655 655 return (lfile is not None and
656 656 (f in manifest) and
657 657 origmatchfn(lfile) or
658 658 None)
659 659 m.matchfn = matchfn
660 660 return m
661 661 oldmatch = installmatchfn(overridematch)
662 662 listpats = []
663 663 for pat in pats:
664 664 if matchmod.patkind(pat) is not None:
665 665 listpats.append(pat)
666 666 else:
667 667 listpats.append(makestandin(pat))
668 668
669 669 try:
670 670 origcopyfile = util.copyfile
671 671 copiedfiles = []
672 672 def overridecopyfile(src, dest, *args, **kwargs):
673 673 if (lfutil.shortname in src and
674 674 dest.startswith(repo.wjoin(lfutil.shortname))):
675 675 destlfile = dest.replace(lfutil.shortname, '')
676 676 if not opts['force'] and os.path.exists(destlfile):
677 677 raise IOError('',
678 678 _('destination largefile already exists'))
679 679 copiedfiles.append((src, dest))
680 680 origcopyfile(src, dest, *args, **kwargs)
681 681
682 682 util.copyfile = overridecopyfile
683 683 result += orig(ui, repo, listpats, opts, rename)
684 684 finally:
685 685 util.copyfile = origcopyfile
686 686
687 687 lfdirstate = lfutil.openlfdirstate(ui, repo)
688 688 for (src, dest) in copiedfiles:
689 689 if (lfutil.shortname in src and
690 690 dest.startswith(repo.wjoin(lfutil.shortname))):
691 691 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
692 692 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
693 693 destlfiledir = repo.wvfs.dirname(repo.wjoin(destlfile)) or '.'
694 694 if not os.path.isdir(destlfiledir):
695 695 os.makedirs(destlfiledir)
696 696 if rename:
697 697 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
698 698
699 699 # The file is gone, but this deletes any empty parent
700 700 # directories as a side-effect.
701 701 repo.wvfs.unlinkpath(srclfile, ignoremissing=True)
702 702 lfdirstate.remove(srclfile)
703 703 else:
704 704 util.copyfile(repo.wjoin(srclfile),
705 705 repo.wjoin(destlfile))
706 706
707 707 lfdirstate.add(destlfile)
708 708 lfdirstate.write()
709 709 except error.Abort as e:
710 710 if pycompat.bytestr(e) != _('no files to copy'):
711 711 raise e
712 712 else:
713 713 nolfiles = True
714 714 finally:
715 715 restorematchfn()
716 716 wlock.release()
717 717
718 718 if nolfiles and nonormalfiles:
719 719 raise error.Abort(_('no files to copy'))
720 720
721 721 return result
722 722
723 723 # When the user calls revert, we have to be careful to not revert any
724 724 # changes to other largefiles accidentally. This means we have to keep
725 725 # track of the largefiles that are being reverted so we only pull down
726 726 # the necessary largefiles.
727 727 #
728 728 # Standins are only updated (to match the hash of largefiles) before
729 729 # commits. Update the standins then run the original revert, changing
730 730 # the matcher to hit standins instead of largefiles. Based on the
731 731 # resulting standins update the largefiles.
732 732 def overriderevert(orig, ui, repo, ctx, parents, *pats, **opts):
733 733 # Because we put the standins in a bad state (by updating them)
734 734 # and then return them to a correct state we need to lock to
735 735 # prevent others from changing them in their incorrect state.
736 736 with repo.wlock():
737 737 lfdirstate = lfutil.openlfdirstate(ui, repo)
738 738 s = lfutil.lfdirstatestatus(lfdirstate, repo)
739 739 lfdirstate.write()
740 740 for lfile in s.modified:
741 741 lfutil.updatestandin(repo, lfile, lfutil.standin(lfile))
742 742 for lfile in s.deleted:
743 743 fstandin = lfutil.standin(lfile)
744 744 if (repo.wvfs.exists(fstandin)):
745 745 repo.wvfs.unlink(fstandin)
746 746
747 747 oldstandins = lfutil.getstandinsstate(repo)
748 748
749 749 def overridematch(mctx, pats=(), opts=None, globbed=False,
750 750 default='relpath', badfn=None):
751 751 if opts is None:
752 752 opts = {}
753 753 match = oldmatch(mctx, pats, opts, globbed, default, badfn=badfn)
754 754 m = copy.copy(match)
755 755
756 756 # revert supports recursing into subrepos, and though largefiles
757 757 # currently doesn't work correctly in that case, this match is
758 758 # called, so the lfdirstate above may not be the correct one for
759 759 # this invocation of match.
760 760 lfdirstate = lfutil.openlfdirstate(mctx.repo().ui, mctx.repo(),
761 761 False)
762 762
763 763 wctx = repo[None]
764 764 matchfiles = []
765 765 for f in m._files:
766 766 standin = lfutil.standin(f)
767 767 if standin in ctx or standin in mctx:
768 768 matchfiles.append(standin)
769 769 elif standin in wctx or lfdirstate[f] == 'r':
770 770 continue
771 771 else:
772 772 matchfiles.append(f)
773 773 m._files = matchfiles
774 774 m._fileset = set(m._files)
775 775 origmatchfn = m.matchfn
776 776 def matchfn(f):
777 777 lfile = lfutil.splitstandin(f)
778 778 if lfile is not None:
779 779 return (origmatchfn(lfile) and
780 780 (f in ctx or f in mctx))
781 781 return origmatchfn(f)
782 782 m.matchfn = matchfn
783 783 return m
784 784 oldmatch = installmatchfn(overridematch)
785 785 try:
786 786 orig(ui, repo, ctx, parents, *pats, **opts)
787 787 finally:
788 788 restorematchfn()
789 789
790 790 newstandins = lfutil.getstandinsstate(repo)
791 791 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
792 792 # lfdirstate should be 'normallookup'-ed for updated files,
793 793 # because reverting doesn't touch dirstate for 'normal' files
794 794 # when target revision is explicitly specified: in such case,
795 795 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
796 796 # of target (standin) file.
797 797 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
798 798 normallookup=True)
799 799
800 800 # after pulling changesets, we need to take some extra care to get
801 801 # largefiles updated remotely
802 802 def overridepull(orig, ui, repo, source=None, **opts):
803 803 revsprepull = len(repo)
804 804 if not source:
805 805 source = 'default'
806 806 repo.lfpullsource = source
807 807 result = orig(ui, repo, source, **opts)
808 808 revspostpull = len(repo)
809 809 lfrevs = opts.get(r'lfrev', [])
810 810 if opts.get(r'all_largefiles'):
811 811 lfrevs.append('pulled()')
812 812 if lfrevs and revspostpull > revsprepull:
813 813 numcached = 0
814 814 repo.firstpulled = revsprepull # for pulled() revset expression
815 815 try:
816 816 for rev in scmutil.revrange(repo, lfrevs):
817 817 ui.note(_('pulling largefiles for revision %d\n') % rev)
818 818 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
819 819 numcached += len(cached)
820 820 finally:
821 821 del repo.firstpulled
822 822 ui.status(_("%d largefiles cached\n") % numcached)
823 823 return result
824 824
825 825 def overridepush(orig, ui, repo, *args, **kwargs):
826 826 """Override push command and store --lfrev parameters in opargs"""
827 827 lfrevs = kwargs.pop(r'lfrev', None)
828 828 if lfrevs:
829 829 opargs = kwargs.setdefault(r'opargs', {})
830 830 opargs['lfrevs'] = scmutil.revrange(repo, lfrevs)
831 831 return orig(ui, repo, *args, **kwargs)
832 832
833 833 def exchangepushoperation(orig, *args, **kwargs):
834 834 """Override pushoperation constructor and store lfrevs parameter"""
835 835 lfrevs = kwargs.pop(r'lfrevs', None)
836 836 pushop = orig(*args, **kwargs)
837 837 pushop.lfrevs = lfrevs
838 838 return pushop
839 839
840 840 revsetpredicate = registrar.revsetpredicate()
841 841
842 842 @revsetpredicate('pulled()')
843 843 def pulledrevsetsymbol(repo, subset, x):
844 844 """Changesets that just has been pulled.
845 845
846 846 Only available with largefiles from pull --lfrev expressions.
847 847
848 848 .. container:: verbose
849 849
850 850 Some examples:
851 851
852 852 - pull largefiles for all new changesets::
853 853
854 854 hg pull -lfrev "pulled()"
855 855
856 856 - pull largefiles for all new branch heads::
857 857
858 858 hg pull -lfrev "head(pulled()) and not closed()"
859 859
860 860 """
861 861
862 862 try:
863 863 firstpulled = repo.firstpulled
864 864 except AttributeError:
865 865 raise error.Abort(_("pulled() only available in --lfrev"))
866 866 return smartset.baseset([r for r in subset if r >= firstpulled])
867 867
868 868 def overrideclone(orig, ui, source, dest=None, **opts):
869 869 d = dest
870 870 if d is None:
871 871 d = hg.defaultdest(source)
872 872 if opts.get(r'all_largefiles') and not hg.islocal(d):
873 873 raise error.Abort(_(
874 874 '--all-largefiles is incompatible with non-local destination %s') %
875 875 d)
876 876
877 877 return orig(ui, source, dest, **opts)
878 878
879 879 def hgclone(orig, ui, opts, *args, **kwargs):
880 880 result = orig(ui, opts, *args, **kwargs)
881 881
882 882 if result is not None:
883 883 sourcerepo, destrepo = result
884 884 repo = destrepo.local()
885 885
886 886 # When cloning to a remote repo (like through SSH), no repo is available
887 887 # from the peer. Therefore the largefiles can't be downloaded and the
888 888 # hgrc can't be updated.
889 889 if not repo:
890 890 return result
891 891
892 892 # If largefiles is required for this repo, permanently enable it locally
893 893 if 'largefiles' in repo.requirements:
894 894 repo.vfs.append('hgrc',
895 895 util.tonativeeol('\n[extensions]\nlargefiles=\n'))
896 896
897 897 # Caching is implicitly limited to 'rev' option, since the dest repo was
898 898 # truncated at that point. The user may expect a download count with
899 899 # this option, so attempt whether or not this is a largefile repo.
900 if opts.get(r'all_largefiles'):
900 if opts.get('all_largefiles'):
901 901 success, missing = lfcommands.downloadlfiles(ui, repo, None)
902 902
903 903 if missing != 0:
904 904 return None
905 905
906 906 return result
907 907
908 908 def hgpostshare(orig, sourcerepo, destrepo, bookmarks=True, defaultpath=None):
909 909 orig(sourcerepo, destrepo, bookmarks, defaultpath)
910 910
911 911 # If largefiles is required for this repo, permanently enable it locally
912 912 if 'largefiles' in destrepo.requirements:
913 913 destrepo.vfs.append('hgrc',
914 914 util.tonativeeol('\n[extensions]\nlargefiles=\n'))
915 915
916 916 def overriderebase(orig, ui, repo, **opts):
917 917 if not util.safehasattr(repo, '_largefilesenabled'):
918 918 return orig(ui, repo, **opts)
919 919
920 920 resuming = opts.get(r'continue')
921 921 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
922 922 repo._lfstatuswriters.append(lambda *msg, **opts: None)
923 923 try:
924 924 return orig(ui, repo, **opts)
925 925 finally:
926 926 repo._lfstatuswriters.pop()
927 927 repo._lfcommithooks.pop()
928 928
929 929 def overridearchivecmd(orig, ui, repo, dest, **opts):
930 930 repo.unfiltered().lfstatus = True
931 931
932 932 try:
933 933 return orig(ui, repo.unfiltered(), dest, **opts)
934 934 finally:
935 935 repo.unfiltered().lfstatus = False
936 936
937 937 def hgwebarchive(orig, web):
938 938 web.repo.lfstatus = True
939 939
940 940 try:
941 941 return orig(web)
942 942 finally:
943 943 web.repo.lfstatus = False
944 944
945 945 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
946 946 prefix='', mtime=None, subrepos=None):
947 947 # For some reason setting repo.lfstatus in hgwebarchive only changes the
948 948 # unfiltered repo's attr, so check that as well.
949 949 if not repo.lfstatus and not repo.unfiltered().lfstatus:
950 950 return orig(repo, dest, node, kind, decode, matchfn, prefix, mtime,
951 951 subrepos)
952 952
953 953 # No need to lock because we are only reading history and
954 954 # largefile caches, neither of which are modified.
955 955 if node is not None:
956 956 lfcommands.cachelfiles(repo.ui, repo, node)
957 957
958 958 if kind not in archival.archivers:
959 959 raise error.Abort(_("unknown archive type '%s'") % kind)
960 960
961 961 ctx = repo[node]
962 962
963 963 if kind == 'files':
964 964 if prefix:
965 965 raise error.Abort(
966 966 _('cannot give prefix when archiving to files'))
967 967 else:
968 968 prefix = archival.tidyprefix(dest, kind, prefix)
969 969
970 970 def write(name, mode, islink, getdata):
971 971 if matchfn and not matchfn(name):
972 972 return
973 973 data = getdata()
974 974 if decode:
975 975 data = repo.wwritedata(name, data)
976 976 archiver.addfile(prefix + name, mode, islink, data)
977 977
978 978 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
979 979
980 980 if repo.ui.configbool("ui", "archivemeta"):
981 981 write('.hg_archival.txt', 0o644, False,
982 982 lambda: archival.buildmetadata(ctx))
983 983
984 984 for f in ctx:
985 985 ff = ctx.flags(f)
986 986 getdata = ctx[f].data
987 987 lfile = lfutil.splitstandin(f)
988 988 if lfile is not None:
989 989 if node is not None:
990 990 path = lfutil.findfile(repo, getdata().strip())
991 991
992 992 if path is None:
993 993 raise error.Abort(
994 994 _('largefile %s not found in repo store or system cache')
995 995 % lfile)
996 996 else:
997 997 path = lfile
998 998
999 999 f = lfile
1000 1000
1001 1001 getdata = lambda: util.readfile(path)
1002 1002 write(f, 'x' in ff and 0o755 or 0o644, 'l' in ff, getdata)
1003 1003
1004 1004 if subrepos:
1005 1005 for subpath in sorted(ctx.substate):
1006 1006 sub = ctx.workingsub(subpath)
1007 1007 submatch = matchmod.subdirmatcher(subpath, matchfn)
1008 1008 sub._repo.lfstatus = True
1009 1009 sub.archive(archiver, prefix, submatch)
1010 1010
1011 1011 archiver.done()
1012 1012
1013 1013 def hgsubrepoarchive(orig, repo, archiver, prefix, match=None, decode=True):
1014 1014 lfenabled = util.safehasattr(repo._repo, '_largefilesenabled')
1015 1015 if not lfenabled or not repo._repo.lfstatus:
1016 1016 return orig(repo, archiver, prefix, match, decode)
1017 1017
1018 1018 repo._get(repo._state + ('hg',))
1019 1019 rev = repo._state[1]
1020 1020 ctx = repo._repo[rev]
1021 1021
1022 1022 if ctx.node() is not None:
1023 1023 lfcommands.cachelfiles(repo.ui, repo._repo, ctx.node())
1024 1024
1025 1025 def write(name, mode, islink, getdata):
1026 1026 # At this point, the standin has been replaced with the largefile name,
1027 1027 # so the normal matcher works here without the lfutil variants.
1028 1028 if match and not match(f):
1029 1029 return
1030 1030 data = getdata()
1031 1031 if decode:
1032 1032 data = repo._repo.wwritedata(name, data)
1033 1033
1034 1034 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
1035 1035
1036 1036 for f in ctx:
1037 1037 ff = ctx.flags(f)
1038 1038 getdata = ctx[f].data
1039 1039 lfile = lfutil.splitstandin(f)
1040 1040 if lfile is not None:
1041 1041 if ctx.node() is not None:
1042 1042 path = lfutil.findfile(repo._repo, getdata().strip())
1043 1043
1044 1044 if path is None:
1045 1045 raise error.Abort(
1046 1046 _('largefile %s not found in repo store or system cache')
1047 1047 % lfile)
1048 1048 else:
1049 1049 path = lfile
1050 1050
1051 1051 f = lfile
1052 1052
1053 1053 getdata = lambda: util.readfile(os.path.join(prefix, path))
1054 1054
1055 1055 write(f, 'x' in ff and 0o755 or 0o644, 'l' in ff, getdata)
1056 1056
1057 1057 for subpath in sorted(ctx.substate):
1058 1058 sub = ctx.workingsub(subpath)
1059 1059 submatch = matchmod.subdirmatcher(subpath, match)
1060 1060 sub._repo.lfstatus = True
1061 1061 sub.archive(archiver, prefix + repo._path + '/', submatch, decode)
1062 1062
1063 1063 # If a largefile is modified, the change is not reflected in its
1064 1064 # standin until a commit. cmdutil.bailifchanged() raises an exception
1065 1065 # if the repo has uncommitted changes. Wrap it to also check if
1066 1066 # largefiles were changed. This is used by bisect, backout and fetch.
1067 1067 def overridebailifchanged(orig, repo, *args, **kwargs):
1068 1068 orig(repo, *args, **kwargs)
1069 1069 repo.lfstatus = True
1070 1070 s = repo.status()
1071 1071 repo.lfstatus = False
1072 1072 if s.modified or s.added or s.removed or s.deleted:
1073 1073 raise error.Abort(_('uncommitted changes'))
1074 1074
1075 1075 def postcommitstatus(orig, repo, *args, **kwargs):
1076 1076 repo.lfstatus = True
1077 1077 try:
1078 1078 return orig(repo, *args, **kwargs)
1079 1079 finally:
1080 1080 repo.lfstatus = False
1081 1081
1082 1082 def cmdutilforget(orig, ui, repo, match, prefix, explicitonly, dryrun):
1083 1083 normalmatcher = composenormalfilematcher(match, repo[None].manifest())
1084 1084 bad, forgot = orig(ui, repo, normalmatcher, prefix, explicitonly, dryrun)
1085 1085 m = composelargefilematcher(match, repo[None].manifest())
1086 1086
1087 1087 try:
1088 1088 repo.lfstatus = True
1089 1089 s = repo.status(match=m, clean=True)
1090 1090 finally:
1091 1091 repo.lfstatus = False
1092 1092 manifest = repo[None].manifest()
1093 1093 forget = sorted(s.modified + s.added + s.deleted + s.clean)
1094 1094 forget = [f for f in forget if lfutil.standin(f) in manifest]
1095 1095
1096 1096 for f in forget:
1097 1097 fstandin = lfutil.standin(f)
1098 1098 if fstandin not in repo.dirstate and not repo.wvfs.isdir(fstandin):
1099 1099 ui.warn(_('not removing %s: file is already untracked\n')
1100 1100 % m.rel(f))
1101 1101 bad.append(f)
1102 1102
1103 1103 for f in forget:
1104 1104 if ui.verbose or not m.exact(f):
1105 1105 ui.status(_('removing %s\n') % m.rel(f))
1106 1106
1107 1107 # Need to lock because standin files are deleted then removed from the
1108 1108 # repository and we could race in-between.
1109 1109 with repo.wlock():
1110 1110 lfdirstate = lfutil.openlfdirstate(ui, repo)
1111 1111 for f in forget:
1112 1112 if lfdirstate[f] == 'a':
1113 1113 lfdirstate.drop(f)
1114 1114 else:
1115 1115 lfdirstate.remove(f)
1116 1116 lfdirstate.write()
1117 1117 standins = [lfutil.standin(f) for f in forget]
1118 1118 for f in standins:
1119 1119 repo.wvfs.unlinkpath(f, ignoremissing=True)
1120 1120 rejected = repo[None].forget(standins)
1121 1121
1122 1122 bad.extend(f for f in rejected if f in m.files())
1123 1123 forgot.extend(f for f in forget if f not in rejected)
1124 1124 return bad, forgot
1125 1125
1126 1126 def _getoutgoings(repo, other, missing, addfunc):
1127 1127 """get pairs of filename and largefile hash in outgoing revisions
1128 1128 in 'missing'.
1129 1129
1130 1130 largefiles already existing on 'other' repository are ignored.
1131 1131
1132 1132 'addfunc' is invoked with each unique pairs of filename and
1133 1133 largefile hash value.
1134 1134 """
1135 1135 knowns = set()
1136 1136 lfhashes = set()
1137 1137 def dedup(fn, lfhash):
1138 1138 k = (fn, lfhash)
1139 1139 if k not in knowns:
1140 1140 knowns.add(k)
1141 1141 lfhashes.add(lfhash)
1142 1142 lfutil.getlfilestoupload(repo, missing, dedup)
1143 1143 if lfhashes:
1144 1144 lfexists = storefactory.openstore(repo, other).exists(lfhashes)
1145 1145 for fn, lfhash in knowns:
1146 1146 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1147 1147 addfunc(fn, lfhash)
1148 1148
1149 1149 def outgoinghook(ui, repo, other, opts, missing):
1150 1150 if opts.pop('large', None):
1151 1151 lfhashes = set()
1152 1152 if ui.debugflag:
1153 1153 toupload = {}
1154 1154 def addfunc(fn, lfhash):
1155 1155 if fn not in toupload:
1156 1156 toupload[fn] = []
1157 1157 toupload[fn].append(lfhash)
1158 1158 lfhashes.add(lfhash)
1159 1159 def showhashes(fn):
1160 1160 for lfhash in sorted(toupload[fn]):
1161 1161 ui.debug(' %s\n' % (lfhash))
1162 1162 else:
1163 1163 toupload = set()
1164 1164 def addfunc(fn, lfhash):
1165 1165 toupload.add(fn)
1166 1166 lfhashes.add(lfhash)
1167 1167 def showhashes(fn):
1168 1168 pass
1169 1169 _getoutgoings(repo, other, missing, addfunc)
1170 1170
1171 1171 if not toupload:
1172 1172 ui.status(_('largefiles: no files to upload\n'))
1173 1173 else:
1174 1174 ui.status(_('largefiles to upload (%d entities):\n')
1175 1175 % (len(lfhashes)))
1176 1176 for file in sorted(toupload):
1177 1177 ui.status(lfutil.splitstandin(file) + '\n')
1178 1178 showhashes(file)
1179 1179 ui.status('\n')
1180 1180
1181 1181 def summaryremotehook(ui, repo, opts, changes):
1182 1182 largeopt = opts.get('large', False)
1183 1183 if changes is None:
1184 1184 if largeopt:
1185 1185 return (False, True) # only outgoing check is needed
1186 1186 else:
1187 1187 return (False, False)
1188 1188 elif largeopt:
1189 1189 url, branch, peer, outgoing = changes[1]
1190 1190 if peer is None:
1191 1191 # i18n: column positioning for "hg summary"
1192 1192 ui.status(_('largefiles: (no remote repo)\n'))
1193 1193 return
1194 1194
1195 1195 toupload = set()
1196 1196 lfhashes = set()
1197 1197 def addfunc(fn, lfhash):
1198 1198 toupload.add(fn)
1199 1199 lfhashes.add(lfhash)
1200 1200 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1201 1201
1202 1202 if not toupload:
1203 1203 # i18n: column positioning for "hg summary"
1204 1204 ui.status(_('largefiles: (no files to upload)\n'))
1205 1205 else:
1206 1206 # i18n: column positioning for "hg summary"
1207 1207 ui.status(_('largefiles: %d entities for %d files to upload\n')
1208 1208 % (len(lfhashes), len(toupload)))
1209 1209
1210 1210 def overridesummary(orig, ui, repo, *pats, **opts):
1211 1211 try:
1212 1212 repo.lfstatus = True
1213 1213 orig(ui, repo, *pats, **opts)
1214 1214 finally:
1215 1215 repo.lfstatus = False
1216 1216
1217 1217 def scmutiladdremove(orig, repo, matcher, prefix, opts=None):
1218 1218 if opts is None:
1219 1219 opts = {}
1220 1220 if not lfutil.islfilesrepo(repo):
1221 1221 return orig(repo, matcher, prefix, opts)
1222 1222 # Get the list of missing largefiles so we can remove them
1223 1223 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1224 1224 unsure, s = lfdirstate.status(matchmod.always(repo.root, repo.getcwd()),
1225 1225 subrepos=[], ignored=False, clean=False,
1226 1226 unknown=False)
1227 1227
1228 1228 # Call into the normal remove code, but the removing of the standin, we want
1229 1229 # to have handled by original addremove. Monkey patching here makes sure
1230 1230 # we don't remove the standin in the largefiles code, preventing a very
1231 1231 # confused state later.
1232 1232 if s.deleted:
1233 1233 m = copy.copy(matcher)
1234 1234
1235 1235 # The m._files and m._map attributes are not changed to the deleted list
1236 1236 # because that affects the m.exact() test, which in turn governs whether
1237 1237 # or not the file name is printed, and how. Simply limit the original
1238 1238 # matches to those in the deleted status list.
1239 1239 matchfn = m.matchfn
1240 1240 m.matchfn = lambda f: f in s.deleted and matchfn(f)
1241 1241
1242 1242 removelargefiles(repo.ui, repo, True, m, opts.get('dry_run'),
1243 1243 **pycompat.strkwargs(opts))
1244 1244 # Call into the normal add code, and any files that *should* be added as
1245 1245 # largefiles will be
1246 1246 added, bad = addlargefiles(repo.ui, repo, True, matcher,
1247 1247 **pycompat.strkwargs(opts))
1248 1248 # Now that we've handled largefiles, hand off to the original addremove
1249 1249 # function to take care of the rest. Make sure it doesn't do anything with
1250 1250 # largefiles by passing a matcher that will ignore them.
1251 1251 matcher = composenormalfilematcher(matcher, repo[None].manifest(), added)
1252 1252 return orig(repo, matcher, prefix, opts)
1253 1253
1254 1254 # Calling purge with --all will cause the largefiles to be deleted.
1255 1255 # Override repo.status to prevent this from happening.
1256 1256 def overridepurge(orig, ui, repo, *dirs, **opts):
1257 1257 # XXX Monkey patching a repoview will not work. The assigned attribute will
1258 1258 # be set on the unfiltered repo, but we will only lookup attributes in the
1259 1259 # unfiltered repo if the lookup in the repoview object itself fails. As the
1260 1260 # monkey patched method exists on the repoview class the lookup will not
1261 1261 # fail. As a result, the original version will shadow the monkey patched
1262 1262 # one, defeating the monkey patch.
1263 1263 #
1264 1264 # As a work around we use an unfiltered repo here. We should do something
1265 1265 # cleaner instead.
1266 1266 repo = repo.unfiltered()
1267 1267 oldstatus = repo.status
1268 1268 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1269 1269 clean=False, unknown=False, listsubrepos=False):
1270 1270 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1271 1271 listsubrepos)
1272 1272 lfdirstate = lfutil.openlfdirstate(ui, repo)
1273 1273 unknown = [f for f in r.unknown if lfdirstate[f] == '?']
1274 1274 ignored = [f for f in r.ignored if lfdirstate[f] == '?']
1275 1275 return scmutil.status(r.modified, r.added, r.removed, r.deleted,
1276 1276 unknown, ignored, r.clean)
1277 1277 repo.status = overridestatus
1278 1278 orig(ui, repo, *dirs, **opts)
1279 1279 repo.status = oldstatus
1280 1280
1281 1281 def overriderollback(orig, ui, repo, **opts):
1282 1282 with repo.wlock():
1283 1283 before = repo.dirstate.parents()
1284 1284 orphans = set(f for f in repo.dirstate
1285 1285 if lfutil.isstandin(f) and repo.dirstate[f] != 'r')
1286 1286 result = orig(ui, repo, **opts)
1287 1287 after = repo.dirstate.parents()
1288 1288 if before == after:
1289 1289 return result # no need to restore standins
1290 1290
1291 1291 pctx = repo['.']
1292 1292 for f in repo.dirstate:
1293 1293 if lfutil.isstandin(f):
1294 1294 orphans.discard(f)
1295 1295 if repo.dirstate[f] == 'r':
1296 1296 repo.wvfs.unlinkpath(f, ignoremissing=True)
1297 1297 elif f in pctx:
1298 1298 fctx = pctx[f]
1299 1299 repo.wwrite(f, fctx.data(), fctx.flags())
1300 1300 else:
1301 1301 # content of standin is not so important in 'a',
1302 1302 # 'm' or 'n' (coming from the 2nd parent) cases
1303 1303 lfutil.writestandin(repo, f, '', False)
1304 1304 for standin in orphans:
1305 1305 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1306 1306
1307 1307 lfdirstate = lfutil.openlfdirstate(ui, repo)
1308 1308 orphans = set(lfdirstate)
1309 1309 lfiles = lfutil.listlfiles(repo)
1310 1310 for file in lfiles:
1311 1311 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1312 1312 orphans.discard(file)
1313 1313 for lfile in orphans:
1314 1314 lfdirstate.drop(lfile)
1315 1315 lfdirstate.write()
1316 1316 return result
1317 1317
1318 1318 def overridetransplant(orig, ui, repo, *revs, **opts):
1319 1319 resuming = opts.get(r'continue')
1320 1320 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1321 1321 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1322 1322 try:
1323 1323 result = orig(ui, repo, *revs, **opts)
1324 1324 finally:
1325 1325 repo._lfstatuswriters.pop()
1326 1326 repo._lfcommithooks.pop()
1327 1327 return result
1328 1328
1329 1329 def overridecat(orig, ui, repo, file1, *pats, **opts):
1330 1330 opts = pycompat.byteskwargs(opts)
1331 1331 ctx = scmutil.revsingle(repo, opts.get('rev'))
1332 1332 err = 1
1333 1333 notbad = set()
1334 1334 m = scmutil.match(ctx, (file1,) + pats, opts)
1335 1335 origmatchfn = m.matchfn
1336 1336 def lfmatchfn(f):
1337 1337 if origmatchfn(f):
1338 1338 return True
1339 1339 lf = lfutil.splitstandin(f)
1340 1340 if lf is None:
1341 1341 return False
1342 1342 notbad.add(lf)
1343 1343 return origmatchfn(lf)
1344 1344 m.matchfn = lfmatchfn
1345 1345 origbadfn = m.bad
1346 1346 def lfbadfn(f, msg):
1347 1347 if not f in notbad:
1348 1348 origbadfn(f, msg)
1349 1349 m.bad = lfbadfn
1350 1350
1351 1351 origvisitdirfn = m.visitdir
1352 1352 def lfvisitdirfn(dir):
1353 1353 if dir == lfutil.shortname:
1354 1354 return True
1355 1355 ret = origvisitdirfn(dir)
1356 1356 if ret:
1357 1357 return ret
1358 1358 lf = lfutil.splitstandin(dir)
1359 1359 if lf is None:
1360 1360 return False
1361 1361 return origvisitdirfn(lf)
1362 1362 m.visitdir = lfvisitdirfn
1363 1363
1364 1364 for f in ctx.walk(m):
1365 1365 with cmdutil.makefileobj(ctx, opts.get('output'), pathname=f) as fp:
1366 1366 lf = lfutil.splitstandin(f)
1367 1367 if lf is None or origmatchfn(f):
1368 1368 # duplicating unreachable code from commands.cat
1369 1369 data = ctx[f].data()
1370 1370 if opts.get('decode'):
1371 1371 data = repo.wwritedata(f, data)
1372 1372 fp.write(data)
1373 1373 else:
1374 1374 hash = lfutil.readasstandin(ctx[f])
1375 1375 if not lfutil.inusercache(repo.ui, hash):
1376 1376 store = storefactory.openstore(repo)
1377 1377 success, missing = store.get([(lf, hash)])
1378 1378 if len(success) != 1:
1379 1379 raise error.Abort(
1380 1380 _('largefile %s is not in cache and could not be '
1381 1381 'downloaded') % lf)
1382 1382 path = lfutil.usercachepath(repo.ui, hash)
1383 1383 with open(path, "rb") as fpin:
1384 1384 for chunk in util.filechunkiter(fpin):
1385 1385 fp.write(chunk)
1386 1386 err = 0
1387 1387 return err
1388 1388
1389 1389 def mergeupdate(orig, repo, node, branchmerge, force,
1390 1390 *args, **kwargs):
1391 1391 matcher = kwargs.get(r'matcher', None)
1392 1392 # note if this is a partial update
1393 1393 partial = matcher and not matcher.always()
1394 1394 with repo.wlock():
1395 1395 # branch | | |
1396 1396 # merge | force | partial | action
1397 1397 # -------+-------+---------+--------------
1398 1398 # x | x | x | linear-merge
1399 1399 # o | x | x | branch-merge
1400 1400 # x | o | x | overwrite (as clean update)
1401 1401 # o | o | x | force-branch-merge (*1)
1402 1402 # x | x | o | (*)
1403 1403 # o | x | o | (*)
1404 1404 # x | o | o | overwrite (as revert)
1405 1405 # o | o | o | (*)
1406 1406 #
1407 1407 # (*) don't care
1408 1408 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1409 1409
1410 1410 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1411 1411 unsure, s = lfdirstate.status(matchmod.always(repo.root,
1412 1412 repo.getcwd()),
1413 1413 subrepos=[], ignored=False,
1414 1414 clean=True, unknown=False)
1415 1415 oldclean = set(s.clean)
1416 1416 pctx = repo['.']
1417 1417 dctx = repo[node]
1418 1418 for lfile in unsure + s.modified:
1419 1419 lfileabs = repo.wvfs.join(lfile)
1420 1420 if not repo.wvfs.exists(lfileabs):
1421 1421 continue
1422 1422 lfhash = lfutil.hashfile(lfileabs)
1423 1423 standin = lfutil.standin(lfile)
1424 1424 lfutil.writestandin(repo, standin, lfhash,
1425 1425 lfutil.getexecutable(lfileabs))
1426 1426 if (standin in pctx and
1427 1427 lfhash == lfutil.readasstandin(pctx[standin])):
1428 1428 oldclean.add(lfile)
1429 1429 for lfile in s.added:
1430 1430 fstandin = lfutil.standin(lfile)
1431 1431 if fstandin not in dctx:
1432 1432 # in this case, content of standin file is meaningless
1433 1433 # (in dctx, lfile is unknown, or normal file)
1434 1434 continue
1435 1435 lfutil.updatestandin(repo, lfile, fstandin)
1436 1436 # mark all clean largefiles as dirty, just in case the update gets
1437 1437 # interrupted before largefiles and lfdirstate are synchronized
1438 1438 for lfile in oldclean:
1439 1439 lfdirstate.normallookup(lfile)
1440 1440 lfdirstate.write()
1441 1441
1442 1442 oldstandins = lfutil.getstandinsstate(repo)
1443 1443 # Make sure the merge runs on disk, not in-memory. largefiles is not a
1444 1444 # good candidate for in-memory merge (large files, custom dirstate,
1445 1445 # matcher usage).
1446 1446 kwargs[r'wc'] = repo[None]
1447 1447 result = orig(repo, node, branchmerge, force, *args, **kwargs)
1448 1448
1449 1449 newstandins = lfutil.getstandinsstate(repo)
1450 1450 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1451 1451
1452 1452 # to avoid leaving all largefiles as dirty and thus rehash them, mark
1453 1453 # all the ones that didn't change as clean
1454 1454 for lfile in oldclean.difference(filelist):
1455 1455 lfdirstate.normal(lfile)
1456 1456 lfdirstate.write()
1457 1457
1458 1458 if branchmerge or force or partial:
1459 1459 filelist.extend(s.deleted + s.removed)
1460 1460
1461 1461 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1462 1462 normallookup=partial)
1463 1463
1464 1464 return result
1465 1465
1466 1466 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1467 1467 result = orig(repo, files, *args, **kwargs)
1468 1468
1469 1469 filelist = []
1470 1470 for f in files:
1471 1471 lf = lfutil.splitstandin(f)
1472 1472 if lf is not None:
1473 1473 filelist.append(lf)
1474 1474 if filelist:
1475 1475 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1476 1476 printmessage=False, normallookup=True)
1477 1477
1478 1478 return result
1479 1479
1480 1480 def upgraderequirements(orig, repo):
1481 1481 reqs = orig(repo)
1482 1482 if 'largefiles' in repo.requirements:
1483 1483 reqs.add('largefiles')
1484 1484 return reqs
1485 1485
1486 1486 _lfscheme = 'largefile://'
1487 1487 def openlargefile(orig, ui, url_, data=None):
1488 1488 if url_.startswith(_lfscheme):
1489 1489 if data:
1490 1490 msg = "cannot use data on a 'largefile://' url"
1491 1491 raise error.ProgrammingError(msg)
1492 1492 lfid = url_[len(_lfscheme):]
1493 1493 return storefactory.getlfile(ui, lfid)
1494 1494 else:
1495 1495 return orig(ui, url_, data=data)
General Comments 0
You need to be logged in to leave comments. Login now