changeset 18:02ee376bee79

status operation against current working directory
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Mon, 03 Jan 2011 20:42:52 +0100
parents 571e1b2cc3f7
children 40532cdc92fc
files design.txt src/com/tmate/hgkit/console/Status.java src/com/tmate/hgkit/ll/HgDirstate.java src/com/tmate/hgkit/ll/HgRepository.java src/com/tmate/hgkit/ll/LocalHgRepo.java
diffstat 5 files changed, 189 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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);
+		}
 	}
 }
--- 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<Record> normal;
-	private List<Record> added;
-	private List<Record> removed;
-	private List<Record> merged;
+	private Map<String, Record> normal;
+	private Map<String, Record> added;
+	private Map<String, Record> removed;
+	private Map<String, Record> 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.<String, Record>emptyMap();
 		if (!dirstateFile.exists()) {
 			return;
 		}
@@ -41,10 +42,11 @@
 		if (da.isEmpty()) {
 			return;
 		}
-		normal = new LinkedList<Record>();
-		added = new LinkedList<Record>();
-		removed = new LinkedList<Record>();
-		merged = new LinkedList<Record>();
+		// not sure linked is really needed here, just for ease of debug
+		normal = new LinkedHashMap<String, Record>();
+		added = new LinkedHashMap<String, Record>();
+		removed = new LinkedHashMap<String, Record>();
+		merged = new LinkedHashMap<String, Record>();
 		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<String> all() {
+		read();
+		TreeSet<String> rv = new TreeSet<String>();
+		@SuppressWarnings("unchecked")
+		Map<String, Record>[] 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<Record>[] all = new List[] { normal, added, removed, merged };
+		Map<String, Record>[] 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;
--- 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);
+	}
 }
--- 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<File> folders = new LinkedList<File>();
+		final File rootDir = repoDir.getParentFile();
+		folders.add(rootDir);
+		final HgDirstate dirstate = loadDirstate();
+		final HgIgnore hgignore = loadIgnore();
+		TreeSet<String> 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