# HG changeset patch # User Artem Tikhomirov # Date 1296017207 -3600 # Node ID 61eedab3eb3e5bbc5e8f3a5ceb526bd33798adb1 # Parent 25f2e5d1cd8b3f1f3cbbe22ee4aaedb6f61362ee Status between two revisions to recognize copy/rename diff -r 25f2e5d1cd8b -r 61eedab3eb3e cmdline/org/tmatesoft/hg/console/Log.java --- a/cmdline/org/tmatesoft/hg/console/Log.java Wed Jan 26 01:07:26 2011 +0100 +++ b/cmdline/org/tmatesoft/hg/console/Log.java Wed Jan 26 05:46:47 2011 +0100 @@ -182,9 +182,9 @@ } Nodeid p1 = changelogWalker.safeFirstParent(csetNodeid); Nodeid p2 = changelogWalker.safeSecondParent(csetNodeid); - int p1x = p1 == Nodeid.NULL ? -1 : repo.getChangelog().getLocalRevisionNumber(p1); - int p2x = p2 == Nodeid.NULL ? -1 : repo.getChangelog().getLocalRevisionNumber(p2); - int mx = repo.getManifest().getLocalRevisionNumber(cset.getManifestRevision()); + int p1x = p1 == Nodeid.NULL ? -1 : repo.getChangelog().getLocalRevision(p1); + int p2x = p2 == Nodeid.NULL ? -1 : repo.getChangelog().getLocalRevision(p2); + int mx = repo.getManifest().getLocalRevision(cset.getManifestRevision()); f.format("parent: %d:%s\nparent: %d:%s\nmanifest: %d:%s\n", p1x, p1, p2x, p2, mx, cset.getManifestRevision()); } f.format("user: %s\ndate: %s\n", cset.getUser(), cset.getDate()); diff -r 25f2e5d1cd8b -r 61eedab3eb3e cmdline/org/tmatesoft/hg/console/Status.java --- a/cmdline/org/tmatesoft/hg/console/Status.java Wed Jan 26 01:07:26 2011 +0100 +++ b/cmdline/org/tmatesoft/hg/console/Status.java Wed Jan 26 05:46:47 2011 +0100 @@ -29,6 +29,7 @@ import org.tmatesoft.hg.repo.HgRepository; import org.tmatesoft.hg.repo.Internals; import org.tmatesoft.hg.repo.StatusCollector; +import org.tmatesoft.hg.repo.StatusCollector.Record; import org.tmatesoft.hg.repo.WorkingCopyStatusCollector; /** @@ -51,13 +52,18 @@ // // new Internals(hgRepo).dumpDirstate(); // - mardu(hgRepo); + //statusWorkingCopy(hgRepo); + statusRevVsWorkingCopy(hgRepo); } - - private static void mardu(HgRepository hgRepo) { + + private static void statusWorkingCopy(HgRepository hgRepo) { WorkingCopyStatusCollector wcc = new WorkingCopyStatusCollector(hgRepo); StatusCollector.Record r = new StatusCollector.Record(); wcc.walk(TIP, r); + mardu(r); + } + + private static void mardu(Record r) { sortAndPrint('M', r.getModified()); sortAndPrint('A', r.getAdded(), r.getCopied()); sortAndPrint('R', r.getRemoved()); @@ -66,6 +72,13 @@ // sortAndPrint('C', r.getClean()); sortAndPrint('!', r.getMissing()); } + + private static void statusRevVsWorkingCopy(HgRepository hgRepo) { + WorkingCopyStatusCollector wcc = new WorkingCopyStatusCollector(hgRepo); + StatusCollector.Record r = new StatusCollector.Record(); + wcc.walk(3, r); + mardu(r); + } private static void bunchOfTests(HgRepository hgRepo) throws Exception { Internals debug = new Internals(hgRepo); diff -r 25f2e5d1cd8b -r 61eedab3eb3e src/org/tmatesoft/hg/core/LogCommand.java --- a/src/org/tmatesoft/hg/core/LogCommand.java Wed Jan 26 01:07:26 2011 +0100 +++ b/src/org/tmatesoft/hg/core/LogCommand.java Wed Jan 26 05:46:47 2011 +0100 @@ -179,7 +179,7 @@ // even if we do not follow history, report file rename do { FileRevision src = new FileRevision(repo, fileNode.getCopySourceRevision(), fileNode.getCopySourceName()); - FileRevision dst = new FileRevision(repo, fileNode.getRevisionNumber(0), fileNode.getPath()); + FileRevision dst = new FileRevision(repo, fileNode.getRevision(0), fileNode.getPath()); ((FileHistoryHandler) handler).copy(src, dst); if (limit > 0 && count >= limit) { // if limit reach, follow is useless. diff -r 25f2e5d1cd8b -r 61eedab3eb3e src/org/tmatesoft/hg/internal/RevlogDump.java --- a/src/org/tmatesoft/hg/internal/RevlogDump.java Wed Jan 26 01:07:26 2011 +0100 +++ b/src/org/tmatesoft/hg/internal/RevlogDump.java Wed Jan 26 05:46:47 2011 +0100 @@ -44,10 +44,10 @@ // String filename = "store/data/hello.c.i"; // String filename = "store/data/docs/readme.i"; boolean dumpData = true; - if (args.length > 2) { + if (args.length > 1) { repo = args[0]; filename = args[1]; - dumpData = "dumpData".equals(args[2]); + dumpData = args.length > 2 ? "dumpData".equals(args[2]) : false; } // DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream(new File(repo + filename)))); diff -r 25f2e5d1cd8b -r 61eedab3eb3e src/org/tmatesoft/hg/internal/RevlogStream.java --- a/src/org/tmatesoft/hg/internal/RevlogStream.java Wed Jan 26 01:07:26 2011 +0100 +++ b/src/org/tmatesoft/hg/internal/RevlogStream.java Wed Jan 26 05:46:47 2011 +0100 @@ -110,6 +110,29 @@ ex.printStackTrace(); throw new IllegalStateException(); } finally { + daIndex.done(); + } + } + + public int linkRevision(int revision) { + final int last = revisionCount() - 1; + if (revision == TIP) { + revision = last; + } + if (revision < 0 || revision > last) { + throw new IllegalArgumentException(Integer.toString(revision)); + } + DataAccess daIndex = getIndexStream(); + try { + int recordOffset = inline ? (int) index.get(revision).offset : revision * REVLOGV1_RECORD_SIZE; + daIndex.seek(recordOffset + 20); + int linkRev = daIndex.readInt(); + return linkRev; + } catch (IOException ex) { + ex.printStackTrace(); + throw new IllegalStateException(); + } finally { + daIndex.done(); } } @@ -189,19 +212,19 @@ daIndex.seek(inline ? (int) index.get(i).offset : i * REVLOGV1_RECORD_SIZE); for (; i <= end; i++ ) { - long l = daIndex.readLong(); + long l = daIndex.readLong(); // 0 @SuppressWarnings("unused") long offset = l >>> 16; @SuppressWarnings("unused") int flags = (int) (l & 0X0FFFF); - int compressedLen = daIndex.readInt(); - int actualLen = daIndex.readInt(); - int baseRevision = daIndex.readInt(); - int linkRevision = daIndex.readInt(); + int compressedLen = daIndex.readInt(); // +8 + int actualLen = daIndex.readInt(); // +12 + int baseRevision = daIndex.readInt(); // +16 + int linkRevision = daIndex.readInt(); // +20 int parent1Revision = daIndex.readInt(); int parent2Revision = daIndex.readInt(); // Hg has 32 bytes here, uses 20 for nodeid, and keeps 12 last bytes empty - daIndex.readBytes(nodeidBuf, 0, 20); + daIndex.readBytes(nodeidBuf, 0, 20); // +32 daIndex.skip(12); byte[] data = null; if (needData) { diff -r 25f2e5d1cd8b -r 61eedab3eb3e src/org/tmatesoft/hg/repo/HgDataFile.java --- a/src/org/tmatesoft/hg/repo/HgDataFile.java Wed Jan 26 01:07:26 2011 +0100 +++ b/src/org/tmatesoft/hg/repo/HgDataFile.java Wed Jan 26 05:46:47 2011 +0100 @@ -57,7 +57,7 @@ } public int length(Nodeid nodeid) { - return content.dataLength(getLocalRevisionNumber(nodeid)); + return content.dataLength(getLocalRevision(nodeid)); } public byte[] content() { @@ -147,6 +147,16 @@ content.iterate(start, end, false, insp); getRepo().getChangelog().range(inspector, commitRevisions); } + + // for a given local revision of the file, find out local revision in the changelog + public int getChangesetLocalRevision(int revision) { + return content.linkRevision(revision); + } + + public Nodeid getChangesetRevision(Nodeid nid) { + int changelogRevision = getChangesetLocalRevision(getLocalRevision(nid)); + return getRepo().getChangelog().getRevision(changelogRevision); + } public boolean isCopy() { if (metadata == null) { @@ -171,8 +181,17 @@ } throw new UnsupportedOperationException(); } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(getClass().getSimpleName()); + sb.append('('); + sb.append(getPath()); + sb.append(')'); + return sb.toString(); + } - public static final class MetadataEntry { + private static final class MetadataEntry { private final String entry; private final int valueStart; /*package-local*/MetadataEntry(String key, String value) { diff -r 25f2e5d1cd8b -r 61eedab3eb3e src/org/tmatesoft/hg/repo/Revlog.java --- a/src/org/tmatesoft/hg/repo/Revlog.java Wed Jan 26 01:07:26 2011 +0100 +++ b/src/org/tmatesoft/hg/repo/Revlog.java Wed Jan 26 05:46:47 2011 +0100 @@ -60,12 +60,12 @@ return content.revisionCount(); } - public Nodeid getRevisionNumber(int revision) { + public Nodeid getRevision(int revision) { // XXX cache nodeids? return Nodeid.fromBinary(content.nodeid(revision), 0); } - public int getLocalRevisionNumber(Nodeid nid) { + public int getLocalRevision(Nodeid nid) { int revision = content.findLocalRevisionNumber(nid); if (revision == BAD_REVISION) { throw new IllegalArgumentException(String.format("%s doesn't represent a revision of %s", nid.toString(), this /*XXX HgDataFile.getPath might be more suitable here*/)); @@ -91,7 +91,7 @@ * @param nodeid */ public byte[] content(Nodeid nodeid) { - return content(getLocalRevisionNumber(nodeid)); + return content(getLocalRevision(nodeid)); } /** diff -r 25f2e5d1cd8b -r 61eedab3eb3e src/org/tmatesoft/hg/repo/StatusCollector.java --- a/src/org/tmatesoft/hg/repo/StatusCollector.java Wed Jan 26 01:07:26 2011 +0100 +++ b/src/org/tmatesoft/hg/repo/StatusCollector.java Wed Jan 26 05:46:47 2011 +0100 @@ -29,6 +29,7 @@ import java.util.TreeSet; import org.tmatesoft.hg.core.Nodeid; +import org.tmatesoft.hg.core.Path; /** @@ -124,7 +125,28 @@ inspector.modified(fname); } } else { - inspector.added(fname); + HgDataFile df = repo.getFileNode(fname); + boolean isCopy = false; + while (df.isCopy()) { + Path original = df.getCopySourceName(); + if (r1Files.contains(original.toString())) { + df = repo.getFileNode(original); + int changelogRevision = df.getChangesetLocalRevision(0); + if (changelogRevision <= rev1) { + // copy/rename source was known prior to rev1 + // (both r1Files.contains is true and original was created earlier than rev1) + // without r1Files.contains changelogRevision <= rev1 won't suffice as the file + // might get removed somewhere in between (changelogRevision < R < rev1) + inspector.copied(original.toString(), fname); + isCopy = true; + } + break; + } + df = repo.getFileNode(original); // try more steps away + } + if (!isCopy) { + inspector.added(fname); + } } } for (String left : r1Files) { diff -r 25f2e5d1cd8b -r 61eedab3eb3e test/org/tmatesoft/hg/test/StatusOutputParser.java --- a/test/org/tmatesoft/hg/test/StatusOutputParser.java Wed Jan 26 01:07:26 2011 +0100 +++ b/test/org/tmatesoft/hg/test/StatusOutputParser.java Wed Jan 26 05:46:47 2011 +0100 @@ -87,7 +87,7 @@ } // last added is copy destination // to get or to remove it - depends on what StatusCollector does in this case - copied.put(added.get(added.size() - 1), fname); + copied.put(added.get(added.size() - 1), toJavaImplConvention(fname)); break; } } @@ -141,11 +141,15 @@ if (l == null) { l = new LinkedList(); } + l.add(toJavaImplConvention(s)); + return l; + } + + private String toJavaImplConvention(String s) { if (winPathSeparator) { // Java impl always give slashed path, while Hg uses local, os-specific convention s = s.replace('\\', '/'); } - l.add(s); - return l; + return s; } } diff -r 25f2e5d1cd8b -r 61eedab3eb3e test/org/tmatesoft/hg/test/TestStatus.java --- a/test/org/tmatesoft/hg/test/TestStatus.java Wed Jan 26 01:07:26 2011 +0100 +++ b/test/org/tmatesoft/hg/test/TestStatus.java Wed Jan 26 05:46:47 2011 +0100 @@ -46,7 +46,7 @@ HgRepository repo = new Lookup().detectFromWorkingDir(); TestStatus test = new TestStatus(repo); test.testLowLevel(); - test.testStatusCommand(); +// test.testStatusCommand(); } public TestStatus(HgRepository hgRepo) { @@ -73,6 +73,13 @@ r = new StatusCollector.Record(); new StatusCollector(repo).change(revision, r); report("status -A --change " + revision, r, statusParser); + // + statusParser.reset(); + int rev2 = 80; + final String range = String.valueOf(revision) + ":" + String.valueOf(rev2); + eh.run("hg", "status", "-A", "--rev", range); + r = new StatusCollector(repo).status(revision, rev2); + report("Status -A -rev " + range, r, statusParser); } public void testStatusCommand() throws Exception {