# HG changeset patch # User Artem Tikhomirov # Date 1294083772 -3600 # Node ID 02ee376bee79d23d7611444b87348d52bde903e4 # Parent 571e1b2cc3f7697f28e3cd3a1be3fda810a51978 status operation against current working directory diff -r 571e1b2cc3f7 -r 02ee376bee79 design.txt --- a/design.txt Mon Dec 27 01:43:08 2010 +0100 +++ b/design.txt Mon Jan 03 20:42:52 2011 +0100 @@ -25,15 +25,15 @@ +buildfile + run samples *input stream impl + lifecycle. Step forward with FileChannel and ByteBuffer, although questionable accomplishment (looks bit complicated, cumbersome) + dirstate.mtime ++calculate sha1 digest for file to see I can deal with nodeid +*.hgignored processing -calculate sha1 digest for file to see I can deal with nodeid DataAccess - collect debug info (buffer misses, file size/total read operations) to find out better strategy to buffer size detection. delta merge Changeset to get index (local revision number) -.hgignored processing RevisionWalker (on manifest) and WorkingCopyWalker (io.File) talking to ? and/or dirstate Revlog.Inspector to get nodeid array of meaningful data exact size (nor heading 00 bytes, nor 12 extra bytes from the spec) - +Status operation from GUI - guess, usually on a file/subfolder, hence API should allow for starting path (unlike cmdline, seems useless to implement include/exclide patterns - GUI users hardly enter them, ever) ??? encodings of fncache, .hgignore, dirstate diff -r 571e1b2cc3f7 -r 02ee376bee79 src/com/tmate/hgkit/console/Status.java --- a/src/com/tmate/hgkit/console/Status.java Mon Dec 27 01:43:08 2010 +0100 +++ b/src/com/tmate/hgkit/console/Status.java Mon Jan 03 20:42:52 2011 +0100 @@ -3,6 +3,8 @@ */ package com.tmate.hgkit.console; +import static com.tmate.hgkit.ll.HgRepository.TIP; + import com.tmate.hgkit.fs.RepositoryLookup; import com.tmate.hgkit.ll.HgRepository; import com.tmate.hgkit.ll.LocalHgRepo; @@ -23,5 +25,64 @@ } System.out.println(hgRepo.getLocation()); ((LocalHgRepo) hgRepo).loadDirstate().dump(); + //hgRepo.status(TIP, TIP, new StatusDump()); + final StatusDump dump = new StatusDump(); + dump.showIgnored = false; + dump.showClean = false; + ((LocalHgRepo) hgRepo).statusLocal(TIP, dump); + } + + private static class StatusDump implements HgRepository.StatusInspector { + public boolean hideStatusPrefix = false; // hg status -n option + public boolean showCopied = true; // -C + public boolean showIgnored = true; // -i + public boolean showClean = true; // -c + + public void modified(String fname) { + print('M', fname); + } + + public void added(String fname) { + print('A', fname); + } + + public void copied(String fnameOrigin, String fnameAdded) { + added(fnameAdded); + if (showCopied) { + print(' ', fnameOrigin); + } + } + + public void removed(String fname) { + print('R', fname); + } + + public void clean(String fname) { + if (showClean) { + print('C', fname); + } + } + + public void missing(String fname) { + print('!', fname); + } + + public void unknown(String fname) { + print('?', fname); + } + + public void ignored(String fname) { + if (showIgnored) { + print('I', fname); + } + } + + private void print(char status, String fname) { + if (!hideStatusPrefix) { + System.out.print(status); + System.out.print(' '); + } + System.out.println(fname); + } } } diff -r 571e1b2cc3f7 -r 02ee376bee79 src/com/tmate/hgkit/ll/HgDirstate.java --- a/src/com/tmate/hgkit/ll/HgDirstate.java Mon Dec 27 01:43:08 2010 +0100 +++ b/src/com/tmate/hgkit/ll/HgDirstate.java Mon Jan 03 20:42:52 2011 +0100 @@ -6,8 +6,9 @@ import java.io.File; import java.io.IOException; import java.util.Collections; -import java.util.LinkedList; -import java.util.List; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.TreeSet; import com.tmate.hgkit.fs.DataAccess; import com.tmate.hgkit.fs.DataAccessProvider; @@ -21,10 +22,10 @@ private final LocalHgRepo repo; private final File dirstateFile; - private List normal; - private List added; - private List removed; - private List merged; + private Map normal; + private Map added; + private Map removed; + private Map merged; public HgDirstate(LocalHgRepo hgRepo, File dirstate) { this.repo = hgRepo; @@ -32,7 +33,7 @@ } private void read() { - normal = added = removed = merged = Collections.emptyList(); + normal = added = removed = merged = Collections.emptyMap(); if (!dirstateFile.exists()) { return; } @@ -41,10 +42,11 @@ if (da.isEmpty()) { return; } - normal = new LinkedList(); - added = new LinkedList(); - removed = new LinkedList(); - merged = new LinkedList(); + // not sure linked is really needed here, just for ease of debug + normal = new LinkedHashMap(); + added = new LinkedHashMap(); + removed = new LinkedHashMap(); + merged = new LinkedHashMap(); try { // XXX skip(40) if we don't need these? byte[] parents = new byte[40]; @@ -71,13 +73,13 @@ } Record r = new Record(fmode, size, time, fn1, fn2); if (state == 'n') { - normal.add(r); + normal.put(r.name1, r); } else if (state == 'a') { - added.add(r); + added.put(r.name1, r); } else if (state == 'r') { - removed.add(r); + removed.put(r.name1, r); } else if (state == 'm') { - merged.add(r); + merged.put(r.name1, r); } else { // FIXME log error? } @@ -89,13 +91,44 @@ } } + // new, modifiable collection + /*package-local*/ TreeSet all() { + read(); + TreeSet rv = new TreeSet(); + @SuppressWarnings("unchecked") + Map[] all = new Map[] { normal, added, removed, merged }; + for (int i = 0; i < all.length; i++) { + for (Record r : all[i].values()) { + rv.add(r.name1); + } + } + return rv; + } + + /*package-local*/ Record checkNormal(String fname) { + return normal.get(fname); + } + + /*package-local*/ Record checkAdded(String fname) { + return added.get(fname); + } + /*package-local*/ Record checkRemoved(String fname) { + return removed.get(fname); + } + /*package-local*/ Record checkMerged(String fname) { + return merged.get(fname); + } + + + + public void dump() { read(); @SuppressWarnings("unchecked") - List[] all = new List[] { normal, added, removed, merged }; + Map[] all = new Map[] { normal, added, removed, merged }; char[] x = new char[] {'n', 'a', 'r', 'm' }; for (int i = 0; i < all.length; i++) { - for (Record r : all[i]) { + for (Record r : all[i].values()) { System.out.printf("%c %3o%6d %30tc\t\t%s", x[i], r.mode, r.size, (long) r.time * 1000, r.name1); if (r.name2 != null) { System.out.printf(" --> %s", r.name2); @@ -106,7 +139,7 @@ } } - private static class Record { + /*package-local*/ static class Record { final int mode; final int size; final int time; diff -r 571e1b2cc3f7 -r 02ee376bee79 src/com/tmate/hgkit/ll/HgRepository.java --- a/src/com/tmate/hgkit/ll/HgRepository.java Mon Dec 27 01:43:08 2010 +0100 +++ b/src/com/tmate/hgkit/ll/HgRepository.java Mon Jan 03 20:42:52 2011 +0100 @@ -58,4 +58,17 @@ * Perhaps, should be separate interface, like ContentLookup */ protected abstract RevlogStream resolve(String repositoryPath); + + public abstract void status(int rev1, int rev2 /*WorkingDir - TIP, TIP?*/, StatusInspector inspector); + + public interface StatusInspector { + void modified(String fname); + void added(String fname); + void copied(String fnameOrigin, String fnameAdded); // if copied files of no interest, should delegate to self.added(fnameAdded); + void removed(String fname); + void clean(String fname); + void missing(String fname); + void unknown(String fname); // not tracked + void ignored(String fname); + } } diff -r 571e1b2cc3f7 -r 02ee376bee79 src/com/tmate/hgkit/ll/LocalHgRepo.java --- a/src/com/tmate/hgkit/ll/LocalHgRepo.java Mon Dec 27 01:43:08 2010 +0100 +++ b/src/com/tmate/hgkit/ll/LocalHgRepo.java Mon Jan 03 20:42:52 2011 +0100 @@ -11,6 +11,7 @@ import java.lang.ref.SoftReference; import java.util.Arrays; import java.util.HashMap; +import java.util.LinkedList; import java.util.TreeSet; import com.tmate.hgkit.fs.DataAccessProvider; @@ -43,7 +44,66 @@ public String getLocation() { return repoLocation; } - + + @Override + public void status(int rev1, int rev2, StatusInspector inspector) { + throw HgRepository.notImplemented(); + } + + public void statusLocal(int rev1, StatusInspector inspector) { + LinkedList folders = new LinkedList(); + final File rootDir = repoDir.getParentFile(); + folders.add(rootDir); + final HgDirstate dirstate = loadDirstate(); + final HgIgnore hgignore = loadIgnore(); + TreeSet knownEntries = dirstate.all(); + do { + File d = folders.removeFirst(); + for (File f : d.listFiles()) { + if (f.isDirectory()) { + if (!".hg".equals(f.getName())) { + folders.addLast(f); + } + } else { + // FIXME path relative to rootDir + String fname = normalize(f.getPath().substring(rootDir.getPath().length() + 1)); + if (hgignore.isIgnored(fname)) { + inspector.ignored(fname); + } else { + if (knownEntries.remove(fname)) { + // modified, added, removed, clean + HgDirstate.Record r; + if ((r = dirstate.checkNormal(fname)) != null) { + // either clean or modified + if (f.lastModified() / 1000 == r.time && r.size == f.length()) { + inspector.clean(fname); + } else { + // FIXME check actual content to avoid false modified files + inspector.modified(fname); + } + } else if ((r = dirstate.checkAdded(fname)) != null) { + if (r.name2 == null) { + inspector.added(fname); + } else { + inspector.copied(fname, r.name2); + } + } else if ((r = dirstate.checkRemoved(fname)) != null) { + inspector.removed(fname); + } else if ((r = dirstate.checkMerged(fname)) != null) { + inspector.modified(fname); + } + } else { + inspector.unknown(fname); + } + } + } + } + } while (!folders.isEmpty()); + for (String m : knownEntries) { + inspector.missing(m); + } + } + // XXX package-local, unless there are cases when required from outside (guess, working dir/revision walkers may hide dirstate access and no public visibility needed) public final HgDirstate loadDirstate() { // XXX may cache in SoftReference if creation is expensive