Mercurial > hg4j
diff src/org/tmatesoft/hg/repo/HgWorkingCopyStatusCollector.java @ 282:e51dd9a14b6f
Yet another WC status fix, where dirstate parent and base revision are treated right (dirstate parent other than tip and explicit baseRevision are not the same)
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Sat, 03 Sep 2011 01:21:03 +0200 |
parents | 35125450c804 |
children | 7232b94f2ae3 |
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/repo/HgWorkingCopyStatusCollector.java Fri Sep 02 13:59:21 2011 +0200 +++ b/src/org/tmatesoft/hg/repo/HgWorkingCopyStatusCollector.java Sat Sep 03 01:21:03 2011 +0200 @@ -59,6 +59,7 @@ private HgDirstate dirstate; private HgStatusCollector baseRevisionCollector; private PathPool pathPool; + private ManifestRevision dirstateParentManifest; public HgWorkingCopyStatusCollector(HgRepository hgRepo) { this(hgRepo, new HgInternals(hgRepo).createWorkingDirWalker(null)); @@ -101,45 +102,57 @@ return dirstate; } - // may be invoked few times + private ManifestRevision getManifest(int changelogLocalRev) { + ManifestRevision mr; + if (baseRevisionCollector != null) { + mr = baseRevisionCollector.raw(changelogLocalRev); + } else { + mr = new ManifestRevision(null, null); + repo.getManifest().walk(changelogLocalRev, changelogLocalRev, mr); + } + return mr; + } + + private ManifestRevision getDirstateParentManifest() { + // WC not necessarily points to TIP, but may be result of update to any previous revision. + // In such case, we need to compare local files not to their TIP content, but to specific version at the time of selected revision + if (dirstateParentManifest == null) { + Nodeid dirstateParent = getDirstate().parents()[0]; + int changelogLocalRev = repo.getChangelog().getLocalRevision(dirstateParent); + dirstateParentManifest = getManifest(changelogLocalRev); + } + return dirstateParentManifest; + } + + // may be invoked few times, TIP or WORKING_COPY indicate comparison shall be run against working copy parent // NOTE, use of TIP constant requires certain care. TIP here doesn't mean latest cset, but actual working copy parent. - // XXX this shall be changed, though, and use of TIP throughout code shall be revised - - // consider case when repository is updated to one of its previous revisions. TIP points to last change, but a lot of - // commands need to work with revision that is in dirstate now. public void walk(int baseRevision, HgStatusInspector inspector) { - if (HgInternals.wrongLocalRevision(baseRevision) || baseRevision == BAD_REVISION || baseRevision == WORKING_COPY) { + if (HgInternals.wrongLocalRevision(baseRevision) || baseRevision == BAD_REVISION) { throw new IllegalArgumentException(String.valueOf(baseRevision)); } - final HgIgnore hgIgnore = repo.getIgnore(); - TreeSet<String> knownEntries = getDirstate().all(); - if (baseRevision == TIP) { - // WC not necessarily points to TIP, but may be result of update to any previous revision. - // In such case, we need to compare local files not to their TIP content, but to specific version at the time of selected revision - Nodeid dirstateParentRev = getDirstate().parents()[0]; - Nodeid lastCsetRev = repo.getChangelog().getRevision(HgRepository.TIP); - if (lastCsetRev.equals(dirstateParentRev)) { - baseRevision = repo.getChangelog().getLastRevision(); - } else { - // can do it right away, but explicit check above might save few cycles (unless getLocalRevision(Nodeid) is effective) - baseRevision = repo.getChangelog().getLocalRevision(dirstateParentRev); - } - } - final boolean isTipBase = baseRevision == repo.getChangelog().getLastRevision(); - ManifestRevision collect = null; + ManifestRevision collect = null; // non null indicates we compare against base revision Set<String> baseRevFiles = Collections.emptySet(); // files from base revision not affected by status calculation - if (!isTipBase) { - if (baseRevisionCollector != null) { - collect = baseRevisionCollector.raw(baseRevision); - } else { - collect = new ManifestRevision(null, null); - repo.getManifest().walk(baseRevision, baseRevision, collect); - } + if (baseRevision != TIP && baseRevision != WORKING_COPY) { + collect = getManifest(baseRevision); baseRevFiles = new TreeSet<String>(collect.files()); } if (inspector instanceof HgStatusCollector.Record) { HgStatusCollector sc = baseRevisionCollector == null ? new HgStatusCollector(repo) : baseRevisionCollector; - ((HgStatusCollector.Record) inspector).init(baseRevision, BAD_REVISION, sc); + // nodeidAfterChange(dirstate's parent) doesn't make too much sense, + // because the change might be actually in working copy. Nevertheless, + // as long as no nodeids can be provided for WC, seems reasonable to report + // latest known nodeid change (although at the moment this is not used and + // is done mostly not to leave stale initialization in the Record) + int rev1,rev2 = getDirstateParentManifest().changesetLocalRev(); + if (baseRevision == TIP || baseRevision == WORKING_COPY) { + rev1 = rev2 - 1; // just use revision prior to dirstate's parent + } else { + rev1 = baseRevision; + } + ((HgStatusCollector.Record) inspector).init(rev1, rev2, sc); } + final HgIgnore hgIgnore = repo.getIgnore(); + TreeSet<String> knownEntries = getDirstate().all(); repoWalker.reset(); final PathPool pp = getPathPool(); while (repoWalker.hasNext()) { @@ -245,7 +258,8 @@ // size is the same or unknown, and, perhaps, different timestamp // check actual content to avoid false modified files HgDataFile df = repo.getFileNode(fname); - if (!areTheSame(f, df, HgRepository.TIP)) { + Nodeid rev = getDirstateParentManifest().nodeid(fname.toString()); + if (!areTheSame(f, df, rev)) { inspector.modified(df.getPath()); } else { inspector.clean(df.getPath()); @@ -304,33 +318,40 @@ inspector.added(fname); } else { // was known; check whether clean or modified - if ((r = getDirstate().checkNormal(fname)) != null) { + Nodeid nidFromDirstate = getDirstateParentManifest().nodeid(fname.toString()); + if ((r = getDirstate().checkNormal(fname)) != null && nid1.equals(nidFromDirstate)) { + // regular file, was the same up to WC initialization. Check if was modified since, and, if not, report right away + // same code as in #checkLocalStatusAgainstFile final boolean timestampEqual = getFileModificationTime(f) == r.time, sizeEqual = r.size == f.length(); + boolean handled = false; if (timestampEqual && sizeEqual) { inspector.clean(fname); - baseRevNames.remove(fname.toString()); // consumed, processed, handled. - return; + handled = true; } else if (!sizeEqual && r.size >= 0) { inspector.modified(fname); + handled = true; + } else if (!todoCheckFlagsEqual(f, flags)) { + // seems like flags have changed, no reason to check content further + inspector.modified(fname); + handled = true; + } + if (handled) { baseRevNames.remove(fname.toString()); // consumed, processed, handled. return; } - // otherwise, shall check actual content (size not the same, or unknown (-1 or -2), or timestamp is different) + // otherwise, shall check actual content (size not the same, or unknown (-1 or -2), or timestamp is different, + // or nodeid in dirstate is different, but local change might have brought it back to baseRevision state) // FALL THROUGH } - if (r != null /*Normal dirstate, but needs extra check*/ || (r = getDirstate().checkMerged(fname)) != null || (r = getDirstate().checkAdded(fname)) != null) { + if (r != null || (r = getDirstate().checkMerged(fname)) != null || (r = getDirstate().checkAdded(fname)) != null) { + // check actual content to see actual changes // when added - seems to be the case of a file added once again, hence need to check if content is different // either clean or modified - if (r.size != -1 /*XXX what about ==-2?*/&& r.size != f.length() || !todoCheckFlagsEqual(f, flags)) { - inspector.modified(fname); + HgDataFile fileNode = repo.getFileNode(fname); + if (areTheSame(f, fileNode, nid1)) { + inspector.clean(fname); } else { - // check actual content to see actual changes - HgDataFile fileNode = repo.getFileNode(fname); - if (areTheSame(f, fileNode, fileNode.getLocalRevision(nid1))) { - inspector.clean(fname); - } else { - inspector.modified(fname); - } + inspector.modified(fname); } baseRevNames.remove(fname.toString()); // consumed, processed, handled. } else if (getDirstate().checkRemoved(fname) != null) { @@ -349,11 +370,12 @@ // The question is whether original Hg treats this case (same content, different parents and hence nodeids) as 'modified' or 'clean' } - private boolean areTheSame(File f, HgDataFile dataFile, int localRevision) { + private boolean areTheSame(File f, HgDataFile dataFile, Nodeid revision) { // XXX consider adding HgDataDile.compare(File/byte[]/whatever) operation to optimize comparison ByteArrayChannel bac = new ByteArrayChannel(); boolean ioFailed = false; try { + int localRevision = dataFile.getLocalRevision(revision); // need content with metadata striped off - although theoretically chances are metadata may be different, // WC doesn't have it anyway dataFile.content(localRevision, bac);