diff src/org/tmatesoft/hg/internal/DirstateBuilder.java @ 526:2f9ed6bcefa2

Initial support for Revert command with accompanying minor refactoring
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Tue, 15 Jan 2013 17:07:19 +0100
parents 0be5be8d57e9
children 95bdcf75e71e
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/internal/DirstateBuilder.java	Fri Jan 11 18:12:39 2013 +0100
+++ b/src/org/tmatesoft/hg/internal/DirstateBuilder.java	Tue Jan 15 17:07:19 2013 +0100
@@ -16,15 +16,23 @@
  */
 package org.tmatesoft.hg.internal;
 
+import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
 import java.nio.channels.WritableByteChannel;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
 
+import org.tmatesoft.hg.core.HgIOException;
 import org.tmatesoft.hg.core.Nodeid;
 import org.tmatesoft.hg.repo.HgDirstate;
+import org.tmatesoft.hg.repo.HgDirstate.EntryKind;
+import org.tmatesoft.hg.repo.HgDirstate.Record;
+import org.tmatesoft.hg.repo.HgInvalidStateException;
 import org.tmatesoft.hg.repo.HgManifest.Flags;
+import org.tmatesoft.hg.repo.HgRepositoryFiles;
 import org.tmatesoft.hg.util.Path;
 
 /**
@@ -36,12 +44,17 @@
  * @author TMate Software Ltd.
  */
 public class DirstateBuilder {
-	private List<HgDirstate.Record> normal = new ArrayList<HgDirstate.Record>();
+	private Map<Path, HgDirstate.Record> normal = new TreeMap<Path, HgDirstate.Record>();
+	private Map<Path, HgDirstate.Record> added = new TreeMap<Path, HgDirstate.Record>();
+	private Map<Path, HgDirstate.Record> removed = new TreeMap<Path, HgDirstate.Record>();
+	private Map<Path, HgDirstate.Record> merged = new TreeMap<Path, HgDirstate.Record>();
 	private Nodeid parent1, parent2;
+	private final Internals hgRepo;
 	private final EncodingHelper encodingHelper;
 	
-	public DirstateBuilder(EncodingHelper encHelper) {
-		encodingHelper = encHelper;
+	public DirstateBuilder(Internals internalRepo) {
+		hgRepo = internalRepo;
+		encodingHelper = internalRepo.buildFileNameEncodingHelper();
 	}
 	
 	public void parents(Nodeid p1, Nodeid p2) {
@@ -60,8 +73,21 @@
 		// right away.
 		int fmode = flags == Flags.RegularFile ? 0666 : 0777; // FIXME actual unix flags
 		int mtime = (int) (System.currentTimeMillis() / 1000);
-		normal.add(new HgDirstate.Record(fmode, bytesWritten, mtime,fname, null));
-		
+		forget(fname);
+		normal.put(fname, new HgDirstate.Record(fmode, bytesWritten, mtime, fname, null));
+	}
+	
+	public void recordUncertain(Path fname) {
+		// `hg revert` puts "n 0 -1 unset" for the reverted file, so shall we
+		forget(fname);
+		normal.put(fname, new HgDirstate.Record(0, -1, -1, fname, null));
+	}
+	
+	private void forget(Path fname) {
+		normal.remove(fname);
+		added.remove(fname);
+		removed.remove(fname);
+		merged.remove(fname);
 	}
 
 	public void serialize(WritableByteChannel dest) throws IOException {
@@ -77,24 +103,65 @@
 		}
 		bb.clear();
 		// entries
-		for (HgDirstate.Record r : normal) {
-			// normal entry is 1+4+4+4+4+fname.length bytes
-			byte[] fname = encodingHelper.toDirstate(r.name().toString());
-			bb = ensureCapacity(bb, 17 + fname.length);
-			bb.put((byte) 'n');
-			bb.putInt(r.mode());
-			bb.putInt(r.size());
-			bb.putInt(r.modificationTime());
-			bb.putInt(fname.length);
-			bb.put(fname);
-			bb.flip();
-			written = dest.write(bb);
-			if (written != bb.limit()) {
-				throw new IOException("Incomplete write");
+		@SuppressWarnings("unchecked")
+		Map<Path, HgDirstate.Record>[] all = new Map[] {normal, added, removed, merged};
+		for (Map<Path, HgDirstate.Record> m : all) {
+			for (HgDirstate.Record r : m.values()) {
+				// regular entry is 1+4+4+4+4+fname.length bytes
+				// it might get extended with copy origin, prepended with 0 byte 
+				byte[] fname = encodingHelper.toDirstate(r.name());
+				byte[] copyOrigin = r.copySource() == null ? null : encodingHelper.toDirstate(r.copySource());
+				int length = fname.length + (copyOrigin == null ? 0 : (1 + copyOrigin.length)); 
+				bb = ensureCapacity(bb, 17 + length);
+				bb.put((byte) 'n');
+				bb.putInt(r.mode());
+				bb.putInt(r.size());
+				bb.putInt(r.modificationTime());
+				bb.putInt(length);
+				bb.put(fname);
+				if (copyOrigin != null) {
+					bb.put((byte) 0);
+					bb.put(copyOrigin);
+				}
+				bb.flip();
+				written = dest.write(bb);
+				if (written != bb.limit()) {
+					throw new IOException("Incomplete write");
+				}
+				bb.clear();
 			}
-			bb.clear();
 		}
 	}
+	
+	public void serialize() throws HgIOException {
+		File dirstateFile = hgRepo.getRepositoryFile(HgRepositoryFiles.Dirstate);
+		try {
+			FileChannel dirstate = new FileOutputStream(dirstateFile).getChannel();
+			serialize(dirstate);
+			dirstate.close();
+		} catch (IOException ex) {
+			throw new HgIOException("Can't write down new directory state", ex, dirstateFile);
+		}
+	}
+	
+	public void fillFrom(DirstateReader dirstate) {
+		// TODO preserve order, if reasonable and possible 
+		dirstate.readInto(new HgDirstate.Inspector() {
+			
+			public boolean next(EntryKind kind, Record entry) {
+				switch (kind) {
+				case Normal: normal.put(entry.name(), entry); break;
+				case Added : added.put(entry.name(), entry); break;
+				case Removed : removed.put(entry.name(), entry); break;
+				case Merged : merged.put(entry.name(), entry); break;
+				default: throw new HgInvalidStateException(String.format("Unexpected entry in the dirstate: %s", kind));
+				}
+				return true;
+			}
+		});
+		parents(dirstate.parents().first(), dirstate.parents().second());
+	}
+
 
 	private static ByteBuffer ensureCapacity(ByteBuffer buf, int cap) {
 		if (buf.capacity() >= cap) {