270 try: |
270 try: |
271 return self._fileinfo(path)[1] |
271 return self._fileinfo(path)[1] |
272 except error.LookupError: |
272 except error.LookupError: |
273 return '' |
273 return '' |
274 |
274 |
275 def sub(self, path, allowcreate=True): |
|
276 '''return a subrepo for the stored revision of path, never wdir()''' |
|
277 return subrepo.subrepo(self, path, allowcreate=allowcreate) |
|
278 |
|
279 def nullsub(self, path, pctx): |
|
280 return subrepo.nullsubrepo(self, path, pctx) |
|
281 |
|
282 def workingsub(self, path): |
|
283 '''return a subrepo for the stored revision, or wdir if this is a wdir |
|
284 context. |
|
285 ''' |
|
286 return subrepo.subrepo(self, path, allowwdir=True) |
|
287 |
|
288 def match(self, pats=None, include=None, exclude=None, default='glob', |
|
289 listsubrepos=False, badfn=None): |
|
290 r = self._repo |
|
291 return matchmod.match(r.root, r.getcwd(), pats, |
|
292 include, exclude, default, |
|
293 auditor=r.nofsauditor, ctx=self, |
|
294 listsubrepos=listsubrepos, badfn=badfn) |
|
295 |
|
296 def diff(self, ctx2=None, match=None, changes=None, opts=None, |
|
297 losedatafn=None, pathfn=None, copy=None, |
|
298 copysourcematch=None, hunksfilterfn=None): |
|
299 """Returns a diff generator for the given contexts and matcher""" |
|
300 if ctx2 is None: |
|
301 ctx2 = self.p1() |
|
302 if ctx2 is not None: |
|
303 ctx2 = self._repo[ctx2] |
|
304 return patch.diff(self._repo, ctx2, self, match=match, changes=changes, |
|
305 opts=opts, losedatafn=losedatafn, pathfn=pathfn, |
|
306 copy=copy, copysourcematch=copysourcematch, |
|
307 hunksfilterfn=hunksfilterfn) |
|
308 |
|
309 def dirs(self): |
|
310 return self._manifest.dirs() |
|
311 |
|
312 def hasdir(self, dir): |
|
313 return self._manifest.hasdir(dir) |
|
314 |
|
315 def status(self, other=None, match=None, listignored=False, |
|
316 listclean=False, listunknown=False, listsubrepos=False): |
|
317 """return status of files between two nodes or node and working |
|
318 directory. |
|
319 |
|
320 If other is None, compare this node with working directory. |
|
321 |
|
322 returns (modified, added, removed, deleted, unknown, ignored, clean) |
|
323 """ |
|
324 |
|
325 ctx1 = self |
|
326 ctx2 = self._repo[other] |
|
327 |
|
328 # This next code block is, admittedly, fragile logic that tests for |
|
329 # reversing the contexts and wouldn't need to exist if it weren't for |
|
330 # the fast (and common) code path of comparing the working directory |
|
331 # with its first parent. |
|
332 # |
|
333 # What we're aiming for here is the ability to call: |
|
334 # |
|
335 # workingctx.status(parentctx) |
|
336 # |
|
337 # If we always built the manifest for each context and compared those, |
|
338 # then we'd be done. But the special case of the above call means we |
|
339 # just copy the manifest of the parent. |
|
340 reversed = False |
|
341 if (not isinstance(ctx1, changectx) |
|
342 and isinstance(ctx2, changectx)): |
|
343 reversed = True |
|
344 ctx1, ctx2 = ctx2, ctx1 |
|
345 |
|
346 match = self._repo.narrowmatch(match) |
|
347 match = ctx2._matchstatus(ctx1, match) |
|
348 r = scmutil.status([], [], [], [], [], [], []) |
|
349 r = ctx2._buildstatus(ctx1, r, match, listignored, listclean, |
|
350 listunknown) |
|
351 |
|
352 if reversed: |
|
353 # Reverse added and removed. Clear deleted, unknown and ignored as |
|
354 # these make no sense to reverse. |
|
355 r = scmutil.status(r.modified, r.removed, r.added, [], [], [], |
|
356 r.clean) |
|
357 |
|
358 if listsubrepos: |
|
359 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2): |
|
360 try: |
|
361 rev2 = ctx2.subrev(subpath) |
|
362 except KeyError: |
|
363 # A subrepo that existed in node1 was deleted between |
|
364 # node1 and node2 (inclusive). Thus, ctx2's substate |
|
365 # won't contain that subpath. The best we can do ignore it. |
|
366 rev2 = None |
|
367 submatch = matchmod.subdirmatcher(subpath, match) |
|
368 s = sub.status(rev2, match=submatch, ignored=listignored, |
|
369 clean=listclean, unknown=listunknown, |
|
370 listsubrepos=True) |
|
371 for rfiles, sfiles in zip(r, s): |
|
372 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles) |
|
373 |
|
374 for l in r: |
|
375 l.sort() |
|
376 |
|
377 return r |
|
378 |
|
379 class changectx(basectx): |
|
380 """A changecontext object makes access to data related to a particular |
|
381 changeset convenient. It represents a read-only context already present in |
|
382 the repo.""" |
|
383 def __init__(self, repo, rev, node): |
|
384 super(changectx, self).__init__(repo) |
|
385 self._rev = rev |
|
386 self._node = node |
|
387 |
|
388 def __hash__(self): |
|
389 try: |
|
390 return hash(self._rev) |
|
391 except AttributeError: |
|
392 return id(self) |
|
393 |
|
394 def __nonzero__(self): |
|
395 return self._rev != nullrev |
|
396 |
|
397 __bool__ = __nonzero__ |
|
398 |
|
399 @propertycache |
|
400 def _changeset(self): |
|
401 return self._repo.changelog.changelogrevision(self.rev()) |
|
402 |
|
403 @propertycache |
|
404 def _manifest(self): |
|
405 return self._manifestctx.read() |
|
406 |
|
407 @property |
|
408 def _manifestctx(self): |
|
409 return self._repo.manifestlog[self._changeset.manifest] |
|
410 |
|
411 @propertycache |
|
412 def _manifestdelta(self): |
|
413 return self._manifestctx.readdelta() |
|
414 |
|
415 @propertycache |
|
416 def _parents(self): |
|
417 repo = self._repo |
|
418 p1, p2 = repo.changelog.parentrevs(self._rev) |
|
419 if p2 == nullrev: |
|
420 return [repo[p1]] |
|
421 return [repo[p1], repo[p2]] |
|
422 |
|
423 def changeset(self): |
|
424 c = self._changeset |
|
425 return ( |
|
426 c.manifest, |
|
427 c.user, |
|
428 c.date, |
|
429 c.files, |
|
430 c.description, |
|
431 c.extra, |
|
432 ) |
|
433 def manifestnode(self): |
|
434 return self._changeset.manifest |
|
435 |
|
436 def user(self): |
|
437 return self._changeset.user |
|
438 def date(self): |
|
439 return self._changeset.date |
|
440 def files(self): |
|
441 return self._changeset.files |
|
442 @propertycache |
275 @propertycache |
443 def _copies(self): |
276 def _copies(self): |
444 source = self._repo.ui.config('experimental', 'copies.read-from') |
|
445 p1copies = self._changeset.p1copies |
|
446 p2copies = self._changeset.p2copies |
|
447 # If config says to get copy metadata only from changeset, then return |
|
448 # that, defaulting to {} if there was no copy metadata. |
|
449 # In compatibility mode, we return copy data from the changeset if |
|
450 # it was recorded there, and otherwise we fall back to getting it from |
|
451 # the filelogs (below). |
|
452 if (source == 'changeset-only' or |
|
453 (source == 'compatibility' and p1copies is not None)): |
|
454 return p1copies or {}, p2copies or {} |
|
455 |
|
456 # Otherwise (config said to read only from filelog, or we are in |
|
457 # compatiblity mode and there is not data in the changeset), we get |
|
458 # the copy metadata from the filelogs. |
|
459 p1copies = {} |
277 p1copies = {} |
460 p2copies = {} |
278 p2copies = {} |
461 p1 = self.p1() |
279 p1 = self.p1() |
462 p2 = self.p2() |
280 p2 = self.p2() |
463 narrowmatch = self._repo.narrowmatch() |
281 narrowmatch = self._repo.narrowmatch() |
475 return p1copies, p2copies |
293 return p1copies, p2copies |
476 def p1copies(self): |
294 def p1copies(self): |
477 return self._copies[0] |
295 return self._copies[0] |
478 def p2copies(self): |
296 def p2copies(self): |
479 return self._copies[1] |
297 return self._copies[1] |
|
298 |
|
299 def sub(self, path, allowcreate=True): |
|
300 '''return a subrepo for the stored revision of path, never wdir()''' |
|
301 return subrepo.subrepo(self, path, allowcreate=allowcreate) |
|
302 |
|
303 def nullsub(self, path, pctx): |
|
304 return subrepo.nullsubrepo(self, path, pctx) |
|
305 |
|
306 def workingsub(self, path): |
|
307 '''return a subrepo for the stored revision, or wdir if this is a wdir |
|
308 context. |
|
309 ''' |
|
310 return subrepo.subrepo(self, path, allowwdir=True) |
|
311 |
|
312 def match(self, pats=None, include=None, exclude=None, default='glob', |
|
313 listsubrepos=False, badfn=None): |
|
314 r = self._repo |
|
315 return matchmod.match(r.root, r.getcwd(), pats, |
|
316 include, exclude, default, |
|
317 auditor=r.nofsauditor, ctx=self, |
|
318 listsubrepos=listsubrepos, badfn=badfn) |
|
319 |
|
320 def diff(self, ctx2=None, match=None, changes=None, opts=None, |
|
321 losedatafn=None, pathfn=None, copy=None, |
|
322 copysourcematch=None, hunksfilterfn=None): |
|
323 """Returns a diff generator for the given contexts and matcher""" |
|
324 if ctx2 is None: |
|
325 ctx2 = self.p1() |
|
326 if ctx2 is not None: |
|
327 ctx2 = self._repo[ctx2] |
|
328 return patch.diff(self._repo, ctx2, self, match=match, changes=changes, |
|
329 opts=opts, losedatafn=losedatafn, pathfn=pathfn, |
|
330 copy=copy, copysourcematch=copysourcematch, |
|
331 hunksfilterfn=hunksfilterfn) |
|
332 |
|
333 def dirs(self): |
|
334 return self._manifest.dirs() |
|
335 |
|
336 def hasdir(self, dir): |
|
337 return self._manifest.hasdir(dir) |
|
338 |
|
339 def status(self, other=None, match=None, listignored=False, |
|
340 listclean=False, listunknown=False, listsubrepos=False): |
|
341 """return status of files between two nodes or node and working |
|
342 directory. |
|
343 |
|
344 If other is None, compare this node with working directory. |
|
345 |
|
346 returns (modified, added, removed, deleted, unknown, ignored, clean) |
|
347 """ |
|
348 |
|
349 ctx1 = self |
|
350 ctx2 = self._repo[other] |
|
351 |
|
352 # This next code block is, admittedly, fragile logic that tests for |
|
353 # reversing the contexts and wouldn't need to exist if it weren't for |
|
354 # the fast (and common) code path of comparing the working directory |
|
355 # with its first parent. |
|
356 # |
|
357 # What we're aiming for here is the ability to call: |
|
358 # |
|
359 # workingctx.status(parentctx) |
|
360 # |
|
361 # If we always built the manifest for each context and compared those, |
|
362 # then we'd be done. But the special case of the above call means we |
|
363 # just copy the manifest of the parent. |
|
364 reversed = False |
|
365 if (not isinstance(ctx1, changectx) |
|
366 and isinstance(ctx2, changectx)): |
|
367 reversed = True |
|
368 ctx1, ctx2 = ctx2, ctx1 |
|
369 |
|
370 match = self._repo.narrowmatch(match) |
|
371 match = ctx2._matchstatus(ctx1, match) |
|
372 r = scmutil.status([], [], [], [], [], [], []) |
|
373 r = ctx2._buildstatus(ctx1, r, match, listignored, listclean, |
|
374 listunknown) |
|
375 |
|
376 if reversed: |
|
377 # Reverse added and removed. Clear deleted, unknown and ignored as |
|
378 # these make no sense to reverse. |
|
379 r = scmutil.status(r.modified, r.removed, r.added, [], [], [], |
|
380 r.clean) |
|
381 |
|
382 if listsubrepos: |
|
383 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2): |
|
384 try: |
|
385 rev2 = ctx2.subrev(subpath) |
|
386 except KeyError: |
|
387 # A subrepo that existed in node1 was deleted between |
|
388 # node1 and node2 (inclusive). Thus, ctx2's substate |
|
389 # won't contain that subpath. The best we can do ignore it. |
|
390 rev2 = None |
|
391 submatch = matchmod.subdirmatcher(subpath, match) |
|
392 s = sub.status(rev2, match=submatch, ignored=listignored, |
|
393 clean=listclean, unknown=listunknown, |
|
394 listsubrepos=True) |
|
395 for rfiles, sfiles in zip(r, s): |
|
396 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles) |
|
397 |
|
398 for l in r: |
|
399 l.sort() |
|
400 |
|
401 return r |
|
402 |
|
403 class changectx(basectx): |
|
404 """A changecontext object makes access to data related to a particular |
|
405 changeset convenient. It represents a read-only context already present in |
|
406 the repo.""" |
|
407 def __init__(self, repo, rev, node): |
|
408 super(changectx, self).__init__(repo) |
|
409 self._rev = rev |
|
410 self._node = node |
|
411 |
|
412 def __hash__(self): |
|
413 try: |
|
414 return hash(self._rev) |
|
415 except AttributeError: |
|
416 return id(self) |
|
417 |
|
418 def __nonzero__(self): |
|
419 return self._rev != nullrev |
|
420 |
|
421 __bool__ = __nonzero__ |
|
422 |
|
423 @propertycache |
|
424 def _changeset(self): |
|
425 return self._repo.changelog.changelogrevision(self.rev()) |
|
426 |
|
427 @propertycache |
|
428 def _manifest(self): |
|
429 return self._manifestctx.read() |
|
430 |
|
431 @property |
|
432 def _manifestctx(self): |
|
433 return self._repo.manifestlog[self._changeset.manifest] |
|
434 |
|
435 @propertycache |
|
436 def _manifestdelta(self): |
|
437 return self._manifestctx.readdelta() |
|
438 |
|
439 @propertycache |
|
440 def _parents(self): |
|
441 repo = self._repo |
|
442 p1, p2 = repo.changelog.parentrevs(self._rev) |
|
443 if p2 == nullrev: |
|
444 return [repo[p1]] |
|
445 return [repo[p1], repo[p2]] |
|
446 |
|
447 def changeset(self): |
|
448 c = self._changeset |
|
449 return ( |
|
450 c.manifest, |
|
451 c.user, |
|
452 c.date, |
|
453 c.files, |
|
454 c.description, |
|
455 c.extra, |
|
456 ) |
|
457 def manifestnode(self): |
|
458 return self._changeset.manifest |
|
459 |
|
460 def user(self): |
|
461 return self._changeset.user |
|
462 def date(self): |
|
463 return self._changeset.date |
|
464 def files(self): |
|
465 return self._changeset.files |
|
466 @propertycache |
|
467 def _copies(self): |
|
468 source = self._repo.ui.config('experimental', 'copies.read-from') |
|
469 p1copies = self._changeset.p1copies |
|
470 p2copies = self._changeset.p2copies |
|
471 # If config says to get copy metadata only from changeset, then return |
|
472 # that, defaulting to {} if there was no copy metadata. |
|
473 # In compatibility mode, we return copy data from the changeset if |
|
474 # it was recorded there, and otherwise we fall back to getting it from |
|
475 # the filelogs (below). |
|
476 if (source == 'changeset-only' or |
|
477 (source == 'compatibility' and p1copies is not None)): |
|
478 return p1copies or {}, p2copies or {} |
|
479 |
|
480 # Otherwise (config said to read only from filelog, or we are in |
|
481 # compatiblity mode and there is not data in the changeset), we get |
|
482 # the copy metadata from the filelogs. |
|
483 return super(changectx, self)._copies |
480 def description(self): |
484 def description(self): |
481 return self._changeset.description |
485 return self._changeset.description |
482 def branch(self): |
486 def branch(self): |
483 return encoding.tolocal(self._changeset.extra.get("branch")) |
487 return encoding.tolocal(self._changeset.extra.get("branch")) |
484 def closesbranch(self): |
488 def closesbranch(self): |