34 class lfilesrepo(repo.__class__): |
35 class lfilesrepo(repo.__class__): |
35 # the mark to examine whether "repo" object enables largefiles or not |
36 # the mark to examine whether "repo" object enables largefiles or not |
36 _largefilesenabled = True |
37 _largefilesenabled = True |
37 |
38 |
38 lfstatus = False |
39 lfstatus = False |
|
40 |
39 def status_nolfiles(self, *args, **kwargs): |
41 def status_nolfiles(self, *args, **kwargs): |
40 return super(lfilesrepo, self).status(*args, **kwargs) |
42 return super(lfilesrepo, self).status(*args, **kwargs) |
41 |
43 |
42 # When lfstatus is set, return a context that gives the names |
44 # When lfstatus is set, return a context that gives the names |
43 # of largefiles instead of their corresponding standins and |
45 # of largefiles instead of their corresponding standins and |
44 # identifies the largefiles as always binary, regardless of |
46 # identifies the largefiles as always binary, regardless of |
45 # their actual contents. |
47 # their actual contents. |
46 def __getitem__(self, changeid): |
48 def __getitem__(self, changeid): |
47 ctx = super(lfilesrepo, self).__getitem__(changeid) |
49 ctx = super(lfilesrepo, self).__getitem__(changeid) |
48 if self.lfstatus: |
50 if self.lfstatus: |
|
51 |
49 class lfilesctx(ctx.__class__): |
52 class lfilesctx(ctx.__class__): |
50 def files(self): |
53 def files(self): |
51 filenames = super(lfilesctx, self).files() |
54 filenames = super(lfilesctx, self).files() |
52 return [lfutil.splitstandin(f) or f for f in filenames] |
55 return [lfutil.splitstandin(f) or f for f in filenames] |
|
56 |
53 def manifest(self): |
57 def manifest(self): |
54 man1 = super(lfilesctx, self).manifest() |
58 man1 = super(lfilesctx, self).manifest() |
|
59 |
55 class lfilesmanifest(man1.__class__): |
60 class lfilesmanifest(man1.__class__): |
56 def __contains__(self, filename): |
61 def __contains__(self, filename): |
57 orig = super(lfilesmanifest, self).__contains__ |
62 orig = super(lfilesmanifest, self).__contains__ |
58 return (orig(filename) or |
63 return orig(filename) or orig( |
59 orig(lfutil.standin(filename))) |
64 lfutil.standin(filename) |
|
65 ) |
|
66 |
60 man1.__class__ = lfilesmanifest |
67 man1.__class__ = lfilesmanifest |
61 return man1 |
68 return man1 |
|
69 |
62 def filectx(self, path, fileid=None, filelog=None): |
70 def filectx(self, path, fileid=None, filelog=None): |
63 orig = super(lfilesctx, self).filectx |
71 orig = super(lfilesctx, self).filectx |
64 try: |
72 try: |
65 if filelog is not None: |
73 if filelog is not None: |
66 result = orig(path, fileid, filelog) |
74 result = orig(path, fileid, filelog) |
68 result = orig(path, fileid) |
76 result = orig(path, fileid) |
69 except error.LookupError: |
77 except error.LookupError: |
70 # Adding a null character will cause Mercurial to |
78 # Adding a null character will cause Mercurial to |
71 # identify this as a binary file. |
79 # identify this as a binary file. |
72 if filelog is not None: |
80 if filelog is not None: |
73 result = orig(lfutil.standin(path), fileid, |
81 result = orig( |
74 filelog) |
82 lfutil.standin(path), fileid, filelog |
|
83 ) |
75 else: |
84 else: |
76 result = orig(lfutil.standin(path), fileid) |
85 result = orig(lfutil.standin(path), fileid) |
77 olddata = result.data |
86 olddata = result.data |
78 result.data = lambda: olddata() + '\0' |
87 result.data = lambda: olddata() + '\0' |
79 return result |
88 return result |
|
89 |
80 ctx.__class__ = lfilesctx |
90 ctx.__class__ = lfilesctx |
81 return ctx |
91 return ctx |
82 |
92 |
83 # Figure out the status of big files and insert them into the |
93 # Figure out the status of big files and insert them into the |
84 # appropriate list in the result. Also removes standin files |
94 # appropriate list in the result. Also removes standin files |
85 # from the listing. Revert to the original status if |
95 # from the listing. Revert to the original status if |
86 # self.lfstatus is False. |
96 # self.lfstatus is False. |
87 # XXX large file status is buggy when used on repo proxy. |
97 # XXX large file status is buggy when used on repo proxy. |
88 # XXX this needs to be investigated. |
98 # XXX this needs to be investigated. |
89 @localrepo.unfilteredmethod |
99 @localrepo.unfilteredmethod |
90 def status(self, node1='.', node2=None, match=None, ignored=False, |
100 def status( |
91 clean=False, unknown=False, listsubrepos=False): |
101 self, |
|
102 node1='.', |
|
103 node2=None, |
|
104 match=None, |
|
105 ignored=False, |
|
106 clean=False, |
|
107 unknown=False, |
|
108 listsubrepos=False, |
|
109 ): |
92 listignored, listclean, listunknown = ignored, clean, unknown |
110 listignored, listclean, listunknown = ignored, clean, unknown |
93 orig = super(lfilesrepo, self).status |
111 orig = super(lfilesrepo, self).status |
94 if not self.lfstatus: |
112 if not self.lfstatus: |
95 return orig(node1, node2, match, listignored, listclean, |
113 return orig( |
96 listunknown, listsubrepos) |
114 node1, |
|
115 node2, |
|
116 match, |
|
117 listignored, |
|
118 listclean, |
|
119 listunknown, |
|
120 listsubrepos, |
|
121 ) |
97 |
122 |
98 # some calls in this function rely on the old version of status |
123 # some calls in this function rely on the old version of status |
99 self.lfstatus = False |
124 self.lfstatus = False |
100 ctx1 = self[node1] |
125 ctx1 = self[node1] |
101 ctx2 = self[node2] |
126 ctx2 = self[node2] |
147 return newfiles |
179 return newfiles |
148 |
180 |
149 m = copy.copy(match) |
181 m = copy.copy(match) |
150 m._files = tostandins(m._files) |
182 m._files = tostandins(m._files) |
151 |
183 |
152 result = orig(node1, node2, m, ignored, clean, unknown, |
184 result = orig( |
153 listsubrepos) |
185 node1, node2, m, ignored, clean, unknown, listsubrepos |
|
186 ) |
154 if working: |
187 if working: |
155 |
188 |
156 def sfindirstate(f): |
189 def sfindirstate(f): |
157 sf = lfutil.standin(f) |
190 sf = lfutil.standin(f) |
158 dirstate = self.dirstate |
191 dirstate = self.dirstate |
159 return sf in dirstate or dirstate.hasdir(sf) |
192 return sf in dirstate or dirstate.hasdir(sf) |
160 |
193 |
161 match._files = [f for f in match._files |
194 match._files = [f for f in match._files if sfindirstate(f)] |
162 if sfindirstate(f)] |
|
163 # Don't waste time getting the ignored and unknown |
195 # Don't waste time getting the ignored and unknown |
164 # files from lfdirstate |
196 # files from lfdirstate |
165 unsure, s = lfdirstate.status(match, subrepos=[], |
197 unsure, s = lfdirstate.status( |
166 ignored=False, |
198 match, |
167 clean=listclean, |
199 subrepos=[], |
168 unknown=False) |
200 ignored=False, |
|
201 clean=listclean, |
|
202 unknown=False, |
|
203 ) |
169 (modified, added, removed, deleted, clean) = ( |
204 (modified, added, removed, deleted, clean) = ( |
170 s.modified, s.added, s.removed, s.deleted, s.clean) |
205 s.modified, |
|
206 s.added, |
|
207 s.removed, |
|
208 s.deleted, |
|
209 s.clean, |
|
210 ) |
171 if parentworking: |
211 if parentworking: |
172 for lfile in unsure: |
212 for lfile in unsure: |
173 standin = lfutil.standin(lfile) |
213 standin = lfutil.standin(lfile) |
174 if standin not in ctx1: |
214 if standin not in ctx1: |
175 # from second parent |
215 # from second parent |
176 modified.append(lfile) |
216 modified.append(lfile) |
177 elif (lfutil.readasstandin(ctx1[standin]) |
217 elif lfutil.readasstandin( |
178 != lfutil.hashfile(self.wjoin(lfile))): |
218 ctx1[standin] |
|
219 ) != lfutil.hashfile(self.wjoin(lfile)): |
179 modified.append(lfile) |
220 modified.append(lfile) |
180 else: |
221 else: |
181 if listclean: |
222 if listclean: |
182 clean.append(lfile) |
223 clean.append(lfile) |
183 lfdirstate.normal(lfile) |
224 lfdirstate.normal(lfile) |
188 |
229 |
189 for lfile in tocheck: |
230 for lfile in tocheck: |
190 standin = lfutil.standin(lfile) |
231 standin = lfutil.standin(lfile) |
191 if standin in ctx1: |
232 if standin in ctx1: |
192 abslfile = self.wjoin(lfile) |
233 abslfile = self.wjoin(lfile) |
193 if ((lfutil.readasstandin(ctx1[standin]) != |
234 if ( |
194 lfutil.hashfile(abslfile)) or |
235 lfutil.readasstandin(ctx1[standin]) |
195 (checkexec and |
236 != lfutil.hashfile(abslfile) |
196 ('x' in ctx1.flags(standin)) != |
237 ) or ( |
197 bool(lfutil.getexecutable(abslfile)))): |
238 checkexec |
|
239 and ('x' in ctx1.flags(standin)) |
|
240 != bool(lfutil.getexecutable(abslfile)) |
|
241 ): |
198 modified.append(lfile) |
242 modified.append(lfile) |
199 elif listclean: |
243 elif listclean: |
200 clean.append(lfile) |
244 clean.append(lfile) |
201 else: |
245 else: |
202 added.append(lfile) |
246 added.append(lfile) |
203 |
247 |
204 # at this point, 'removed' contains largefiles |
248 # at this point, 'removed' contains largefiles |
205 # marked as 'R' in the working context. |
249 # marked as 'R' in the working context. |
206 # then, largefiles not managed also in the target |
250 # then, largefiles not managed also in the target |
207 # context should be excluded from 'removed'. |
251 # context should be excluded from 'removed'. |
208 removed = [lfile for lfile in removed |
252 removed = [ |
209 if lfutil.standin(lfile) in ctx1] |
253 lfile |
|
254 for lfile in removed |
|
255 if lfutil.standin(lfile) in ctx1 |
|
256 ] |
210 |
257 |
211 # Standins no longer found in lfdirstate have been deleted |
258 # Standins no longer found in lfdirstate have been deleted |
212 for standin in ctx1.walk(lfutil.getstandinmatcher(self)): |
259 for standin in ctx1.walk(lfutil.getstandinmatcher(self)): |
213 lfile = lfutil.splitstandin(standin) |
260 lfile = lfutil.splitstandin(standin) |
214 if not match(lfile): |
261 if not match(lfile): |
227 # Largefiles are not really removed when they're |
274 # Largefiles are not really removed when they're |
228 # still in the normal dirstate. Likewise, normal |
275 # still in the normal dirstate. Likewise, normal |
229 # files are not really removed if they are still in |
276 # files are not really removed if they are still in |
230 # lfdirstate. This happens in merges where files |
277 # lfdirstate. This happens in merges where files |
231 # change type. |
278 # change type. |
232 removed = [f for f in removed |
279 removed = [f for f in removed if f not in self.dirstate] |
233 if f not in self.dirstate] |
280 result[2] = [f for f in result[2] if f not in lfdirstate] |
234 result[2] = [f for f in result[2] |
|
235 if f not in lfdirstate] |
|
236 |
281 |
237 lfiles = set(lfdirstate) |
282 lfiles = set(lfdirstate) |
238 # Unknown files |
283 # Unknown files |
239 result[4] = set(result[4]).difference(lfiles) |
284 result[4] = set(result[4]).difference(lfiles) |
240 # Ignored files |
285 # Ignored files |
241 result[5] = set(result[5]).difference(lfiles) |
286 result[5] = set(result[5]).difference(lfiles) |
242 # combine normal files and largefiles |
287 # combine normal files and largefiles |
243 normals = [[fn for fn in filelist |
288 normals = [ |
244 if not lfutil.isstandin(fn)] |
289 [fn for fn in filelist if not lfutil.isstandin(fn)] |
245 for filelist in result] |
290 for filelist in result |
246 lfstatus = (modified, added, removed, deleted, [], [], |
291 ] |
247 clean) |
292 lfstatus = ( |
248 result = [sorted(list1 + list2) |
293 modified, |
249 for (list1, list2) in zip(normals, lfstatus)] |
294 added, |
250 else: # not against working directory |
295 removed, |
251 result = [[lfutil.splitstandin(f) or f for f in items] |
296 deleted, |
252 for items in result] |
297 [], |
|
298 [], |
|
299 clean, |
|
300 ) |
|
301 result = [ |
|
302 sorted(list1 + list2) |
|
303 for (list1, list2) in zip(normals, lfstatus) |
|
304 ] |
|
305 else: # not against working directory |
|
306 result = [ |
|
307 [lfutil.splitstandin(f) or f for f in items] |
|
308 for items in result |
|
309 ] |
253 |
310 |
254 if wlock: |
311 if wlock: |
255 lfdirstate.write() |
312 lfdirstate.write() |
256 |
313 |
257 finally: |
314 finally: |
261 self.lfstatus = True |
318 self.lfstatus = True |
262 return scmutil.status(*result) |
319 return scmutil.status(*result) |
263 |
320 |
264 def commitctx(self, ctx, *args, **kwargs): |
321 def commitctx(self, ctx, *args, **kwargs): |
265 node = super(lfilesrepo, self).commitctx(ctx, *args, **kwargs) |
322 node = super(lfilesrepo, self).commitctx(ctx, *args, **kwargs) |
|
323 |
266 class lfilesctx(ctx.__class__): |
324 class lfilesctx(ctx.__class__): |
267 def markcommitted(self, node): |
325 def markcommitted(self, node): |
268 orig = super(lfilesctx, self).markcommitted |
326 orig = super(lfilesctx, self).markcommitted |
269 return lfutil.markcommitted(orig, self, node) |
327 return lfutil.markcommitted(orig, self, node) |
|
328 |
270 ctx.__class__ = lfilesctx |
329 ctx.__class__ = lfilesctx |
271 return node |
330 return node |
272 |
331 |
273 # Before commit, largefile standins have not had their |
332 # Before commit, largefile standins have not had their |
274 # contents updated to reflect the hash of their largefile. |
333 # contents updated to reflect the hash of their largefile. |
275 # Do that here. |
334 # Do that here. |
276 def commit(self, text="", user=None, date=None, match=None, |
335 def commit( |
277 force=False, editor=False, extra=None): |
336 self, |
|
337 text="", |
|
338 user=None, |
|
339 date=None, |
|
340 match=None, |
|
341 force=False, |
|
342 editor=False, |
|
343 extra=None, |
|
344 ): |
278 if extra is None: |
345 if extra is None: |
279 extra = {} |
346 extra = {} |
280 orig = super(lfilesrepo, self).commit |
347 orig = super(lfilesrepo, self).commit |
281 |
348 |
282 with self.wlock(): |
349 with self.wlock(): |
283 lfcommithook = self._lfcommithooks[-1] |
350 lfcommithook = self._lfcommithooks[-1] |
284 match = lfcommithook(self, match) |
351 match = lfcommithook(self, match) |
285 result = orig(text=text, user=user, date=date, match=match, |
352 result = orig( |
286 force=force, editor=editor, extra=extra) |
353 text=text, |
|
354 user=user, |
|
355 date=date, |
|
356 match=match, |
|
357 force=force, |
|
358 editor=editor, |
|
359 extra=extra, |
|
360 ) |
287 return result |
361 return result |
288 |
362 |
289 def push(self, remote, force=False, revs=None, newbranch=False): |
363 def push(self, remote, force=False, revs=None, newbranch=False): |
290 if remote.local(): |
364 if remote.local(): |
291 missing = set(self.requirements) - remote.local().supported |
365 missing = set(self.requirements) - remote.local().supported |
292 if missing: |
366 if missing: |
293 msg = _("required features are not" |
367 msg = _( |
294 " supported in the destination:" |
368 "required features are not" |
295 " %s") % (', '.join(sorted(missing))) |
369 " supported in the destination:" |
|
370 " %s" |
|
371 ) % (', '.join(sorted(missing))) |
296 raise error.Abort(msg) |
372 raise error.Abort(msg) |
297 return super(lfilesrepo, self).push(remote, force=force, revs=revs, |
373 return super(lfilesrepo, self).push( |
298 newbranch=newbranch) |
374 remote, force=force, revs=revs, newbranch=newbranch |
|
375 ) |
299 |
376 |
300 # TODO: _subdirlfs should be moved into "lfutil.py", because |
377 # TODO: _subdirlfs should be moved into "lfutil.py", because |
301 # it is referred only from "lfutil.updatestandinsbymatch" |
378 # it is referred only from "lfutil.updatestandinsbymatch" |
302 def _subdirlfs(self, files, lfiles): |
379 def _subdirlfs(self, files, lfiles): |
303 ''' |
380 ''' |
375 if lfrevs is None: |
453 if lfrevs is None: |
376 lfrevs = pushop.outgoing.missing |
454 lfrevs = pushop.outgoing.missing |
377 if lfrevs: |
455 if lfrevs: |
378 toupload = set() |
456 toupload = set() |
379 addfunc = lambda fn, lfhash: toupload.add(lfhash) |
457 addfunc = lambda fn, lfhash: toupload.add(lfhash) |
380 lfutil.getlfilestoupload(pushop.repo, lfrevs, |
458 lfutil.getlfilestoupload(pushop.repo, lfrevs, addfunc) |
381 addfunc) |
|
382 lfcommands.uploadlfiles(ui, pushop.repo, pushop.remote, toupload) |
459 lfcommands.uploadlfiles(ui, pushop.repo, pushop.remote, toupload) |
|
460 |
383 repo.prepushoutgoinghooks.add("largefiles", prepushoutgoinghook) |
461 repo.prepushoutgoinghooks.add("largefiles", prepushoutgoinghook) |
384 |
462 |
385 def checkrequireslfiles(ui, repo, **kwargs): |
463 def checkrequireslfiles(ui, repo, **kwargs): |
386 if 'largefiles' not in repo.requirements and any( |
464 if 'largefiles' not in repo.requirements and any( |
387 lfutil.shortname+'/' in f[0] for f in repo.store.datafiles()): |
465 lfutil.shortname + '/' in f[0] for f in repo.store.datafiles() |
|
466 ): |
388 repo.requirements.add('largefiles') |
467 repo.requirements.add('largefiles') |
389 repo._writerequirements() |
468 repo._writerequirements() |
390 |
469 |
391 ui.setconfig('hooks', 'changegroup.lfiles', checkrequireslfiles, |
470 ui.setconfig( |
392 'largefiles') |
471 'hooks', 'changegroup.lfiles', checkrequireslfiles, 'largefiles' |
|
472 ) |
393 ui.setconfig('hooks', 'commit.lfiles', checkrequireslfiles, 'largefiles') |
473 ui.setconfig('hooks', 'commit.lfiles', checkrequireslfiles, 'largefiles') |