changeset 501:d2f6ab541330

Change the way extensions are accessed (with ExtensionsManager now), add preliminary Rebase extension support
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Mon, 29 Oct 2012 19:04:13 +0100
parents 465316bf97e8
children 37d97854c6ad
files cmdline/org/tmatesoft/hg/console/Main.java src/org/tmatesoft/hg/internal/Internals.java src/org/tmatesoft/hg/internal/PhasesHelper.java src/org/tmatesoft/hg/repo/HgBookmarks.java src/org/tmatesoft/hg/repo/HgDirstate.java src/org/tmatesoft/hg/repo/HgRepository.java src/org/tmatesoft/hg/repo/ext/HgExtensionsManager.java src/org/tmatesoft/hg/repo/ext/MqManager.java src/org/tmatesoft/hg/repo/ext/Rebase.java test/org/tmatesoft/hg/test/TestMqExtension.java
diffstat 10 files changed, 271 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- a/cmdline/org/tmatesoft/hg/console/Main.java	Mon Oct 29 18:16:21 2012 +0100
+++ b/cmdline/org/tmatesoft/hg/console/Main.java	Mon Oct 29 19:04:13 2012 +0100
@@ -74,7 +74,10 @@
 import org.tmatesoft.hg.repo.HgStatusInspector;
 import org.tmatesoft.hg.repo.HgSubrepoLocation;
 import org.tmatesoft.hg.repo.HgSubrepoLocation.Kind;
+import org.tmatesoft.hg.repo.ext.HgExtensionsManager;
+import org.tmatesoft.hg.repo.ext.HgExtensionsManager.HgExt;
 import org.tmatesoft.hg.repo.ext.MqManager;
+import org.tmatesoft.hg.repo.ext.Rebase;
 import org.tmatesoft.hg.repo.ext.MqManager.PatchRecord;
 import org.tmatesoft.hg.repo.HgWorkingCopyStatusCollector;
 import org.tmatesoft.hg.repo.HgRevisionMap;
@@ -112,7 +115,8 @@
 
 	public static void main(String[] args) throws Exception {
 		Main m = new Main(args);
-		m.dumpBookmarks();
+		m.tryExtensions();
+//		m.dumpBookmarks();
 //		m.readConfigFile();
 //		m.dumpCommitLastMessage();
 //		m.buildFileLog();
@@ -136,6 +140,21 @@
 //		m.bunchOfTests();
 	}
 	
+	private void tryExtensions() throws Exception {
+		HgExtensionsManager em = hgRepo.getExtensions();
+		if (!em.isEnabled(HgExt.Rebase)) {
+			System.out.println("Rebase is not enabled");
+			return;
+		}
+		Rebase re = em.getRebaseExtension();
+		if (!re.refresh().isRebaseInProgress()) {
+			System.out.println("No rebase is in progress");
+			return;
+		}
+		System.out.printf("%s %s %s\n", re.getWorkingDirParent().shortNotation(), re.getTarget().shortNotation(), re.getExternalParent().shortNotation());
+		System.out.printf("collapse:%b, keep:%b, keepbranches:%b\n", re.isCollapse(), re.isKeepOriginalRevisions(), re.isKeepBranchNames());
+	}
+	
 	// TODO as test
 	private void dumpBookmarks() throws Exception {
 		HgBookmarks bm = hgRepo.getBookmarks();
--- a/src/org/tmatesoft/hg/internal/Internals.java	Mon Oct 29 18:16:21 2012 +0100
+++ b/src/org/tmatesoft/hg/internal/Internals.java	Mon Oct 29 19:04:13 2012 +0100
@@ -42,7 +42,7 @@
  * @author Artem Tikhomirov
  * @author TMate Software Ltd.
  */
-public final class Internals {
+public final class Internals implements SessionContext.Source {
 	
 	/**
 	 * Allows to specify Mercurial installation directory to detect installation-wide configurations.
@@ -95,7 +95,7 @@
 		dataAccess = new DataAccessProvider(ctx);
 		RepoInitializer repoInit = new RepoInitializer().initRequiresFromFile(repoDir);
 		requiresFlags = repoInit.getRequires();
-		dataPathHelper = repoInit.buildDataFilesHelper(getContext());
+		dataPathHelper = repoInit.buildDataFilesHelper(getSessionContext());
 		repoPathHelper = repoInit.buildStoreFilesHelper();
 	}
 	
@@ -135,7 +135,7 @@
 		return new File(repoDir, storagePath.toString());
 	}
 	
-	public SessionContext getContext() {
+	public SessionContext getSessionContext() {
 		return repo.getSessionContext();
 	}
 	
--- a/src/org/tmatesoft/hg/internal/PhasesHelper.java	Mon Oct 29 18:16:21 2012 +0100
+++ b/src/org/tmatesoft/hg/internal/PhasesHelper.java	Mon Oct 29 19:04:13 2012 +0100
@@ -135,13 +135,13 @@
 					continue;
 				}
 				if (lc.length != 2) {
-					repo.getContext().getLog().dump(getClass(), Warn, "Bad line in phaseroots:%s", line);
+					repo.getSessionContext().getLog().dump(getClass(), Warn, "Bad line in phaseroots:%s", line);
 					continue;
 				}
 				int phaseIndex = Integer.parseInt(lc[0]);
 				Nodeid rootRev = Nodeid.fromAscii(lc[1]);
 				if (!getRepo().getChangelog().isKnown(rootRev)) {
-					repo.getContext().getLog().dump(getClass(), Warn, "Phase(%d) root node %s doesn't exist in the repository, ignored.", phaseIndex, rootRev);
+					repo.getSessionContext().getLog().dump(getClass(), Warn, "Phase(%d) root node %s doesn't exist in the repository, ignored.", phaseIndex, rootRev);
 					continue;
 				}
 				HgPhase phase = HgPhase.parse(phaseIndex);
@@ -160,7 +160,7 @@
 				try {
 					br.close();
 				} catch (IOException ex) {
-					repo.getContext().getLog().dump(getClass(), Info, ex, null);
+					repo.getSessionContext().getLog().dump(getClass(), Info, ex, null);
 					// ignore the exception otherwise 
 				}
 			}
--- a/src/org/tmatesoft/hg/repo/HgBookmarks.java	Mon Oct 29 18:16:21 2012 +0100
+++ b/src/org/tmatesoft/hg/repo/HgBookmarks.java	Mon Oct 29 19:04:13 2012 +0100
@@ -43,7 +43,7 @@
 	}
 	
 	/*package-local*/ void read() throws HgInvalidControlFileException {
-		final LogFacility log = internalRepo.getContext().getLog();
+		final LogFacility log = internalRepo.getSessionContext().getLog();
 		final HgRepository repo = internalRepo.getRepo();
 		File all = internalRepo.getFileFromRepoDir(HgRepositoryFiles.Bookmarks.getName());
 		LinkedHashMap<String, Nodeid> bm = new LinkedHashMap<String, Nodeid>();
--- a/src/org/tmatesoft/hg/repo/HgDirstate.java	Mon Oct 29 18:16:21 2012 +0100
+++ b/src/org/tmatesoft/hg/repo/HgDirstate.java	Mon Oct 29 19:04:13 2012 +0100
@@ -145,7 +145,7 @@
 				} else if (state == 'm') {
 					merged.put(r.name1, r);
 				} else {
-					repo.getContext().getLog().dump(getClass(), Severity.Warn, "Dirstate record for file %s (size: %d, tstamp:%d) has unknown state '%c'", r.name1, r.size(), r.time, state);
+					repo.getSessionContext().getLog().dump(getClass(), Severity.Warn, "Dirstate record for file %s (size: %d, tstamp:%d) has unknown state '%c'", r.name1, r.size(), r.time, state);
 				}
 			}
 		} catch (IOException ex) {
@@ -215,7 +215,7 @@
 				branch = b == null || b.length() == 0 ? HgRepository.DEFAULT_BRANCH_NAME : b;
 				r.close();
 			} catch (FileNotFoundException ex) {
-				internalRepo.getContext().getLog().dump(HgDirstate.class, Debug, ex, null); // log verbose debug, exception might be legal here 
+				internalRepo.getSessionContext().getLog().dump(HgDirstate.class, Debug, ex, null); // log verbose debug, exception might be legal here 
 				// IGNORE
 			} catch (IOException ex) {
 				throw new HgInvalidControlFileException("Error reading file with branch information", ex, branchFile);
--- a/src/org/tmatesoft/hg/repo/HgRepository.java	Mon Oct 29 18:16:21 2012 +0100
+++ b/src/org/tmatesoft/hg/repo/HgRepository.java	Mon Oct 29 19:04:13 2012 +0100
@@ -39,6 +39,7 @@
 import org.tmatesoft.hg.internal.Internals;
 import org.tmatesoft.hg.internal.RevlogStream;
 import org.tmatesoft.hg.internal.SubrepoManager;
+import org.tmatesoft.hg.repo.ext.HgExtensionsManager;
 import org.tmatesoft.hg.util.CancelledException;
 import org.tmatesoft.hg.util.Pair;
 import org.tmatesoft.hg.util.Path;
@@ -114,6 +115,7 @@
 	private HgMergeState mergeState;
 	private SubrepoManager subRepos;
 	private HgBookmarks bookmarks;
+	private HgExtensionsManager extManager;
 
 	// XXX perhaps, shall enable caching explicitly
 	private final HashMap<Path, SoftReference<RevlogStream>> streamsCache = new HashMap<Path, SoftReference<RevlogStream>>();
@@ -471,6 +473,18 @@
 		}
 		return bookmarks;
 	}
+	
+	public HgExtensionsManager getExtensions() {
+		if (extManager == null) {
+			class EM extends HgExtensionsManager {
+				EM() {
+					super(HgRepository.this.getImplHelper());
+				}
+			}
+			extManager = new EM();
+		}
+		return extManager;
+	}
 
 	/**
 	 * @return session environment of the repository
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/tmatesoft/hg/repo/ext/HgExtensionsManager.java	Mon Oct 29 19:04:13 2012 +0100
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2012 TMate Software Ltd
+ *  
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * For information on how to redistribute this software under
+ * the terms of a license other than GNU General Public License
+ * contact TMate Software at support@hg4j.com
+ */
+package org.tmatesoft.hg.repo.ext;
+
+import org.tmatesoft.hg.internal.Internals;
+import org.tmatesoft.hg.repo.HgRepoConfig;
+
+/**
+ * 
+ * @author Artem Tikhomirov
+ * @author TMate Software Ltd.
+ */
+public abstract class HgExtensionsManager {
+	
+	public enum HgExt { 
+		MQ ("mq"), Rebase("rebase");
+		
+		private final String mercurialExtName;
+
+		private HgExt(String nativeName) {
+			mercurialExtName = nativeName;
+		}
+		
+		String getNativeName() {
+			return mercurialExtName;
+		}
+	}
+	
+	private final Internals repo;
+	private MqManager mqExt;
+	private Rebase rebaseExt;
+
+	protected HgExtensionsManager(Internals internalRepo) {
+		repo = internalRepo;
+	}
+
+	public boolean isEnabled(HgExt e) {
+		HgRepoConfig cfg = repo.getRepo().getConfiguration();
+		return cfg.getExtensions().isEnabled(e.getNativeName());
+	}
+
+	public Rebase getRebaseExtension() {
+		if (rebaseExt == null && isEnabled(HgExt.Rebase)) {
+			rebaseExt = new Rebase(repo);
+		}
+		return rebaseExt;
+	}
+	
+	public MqManager getMQ() {
+		if (mqExt == null && isEnabled(HgExt.MQ)) {
+			mqExt = new MqManager(repo);
+		}
+		return mqExt;
+	}
+}
--- a/src/org/tmatesoft/hg/repo/ext/MqManager.java	Mon Oct 29 18:16:21 2012 +0100
+++ b/src/org/tmatesoft/hg/repo/ext/MqManager.java	Mon Oct 29 19:04:13 2012 +0100
@@ -30,10 +30,8 @@
 import org.tmatesoft.hg.core.Nodeid;
 import org.tmatesoft.hg.internal.Internals;
 import org.tmatesoft.hg.internal.LineReader;
-import org.tmatesoft.hg.repo.HgInternals;
 import org.tmatesoft.hg.repo.HgInvalidControlFileException;
 import org.tmatesoft.hg.repo.HgInvalidFileException;
-import org.tmatesoft.hg.repo.HgRepository;
 import org.tmatesoft.hg.util.LogFacility;
 import org.tmatesoft.hg.util.Path;
 
@@ -41,6 +39,10 @@
  * Mercurial Queues Support. 
  * Access to MqExtension functionality.
  * 
+ * FIXME check we don't hold any mq files for too long, close them, use
+ * the same lock mechanism as mq does (if any). Check if MQ uses Mercurial's store lock
+ * 
+ * @since 1.1
  * @author Artem Tikhomirov
  * @author TMate Software Ltd.
  */
@@ -48,32 +50,32 @@
 	
 	private static final String PATCHES_DIR = "patches";
 
-	private final HgRepository repo;
+	private final Internals repo;
 	private List<PatchRecord> applied = Collections.emptyList();
 	private List<PatchRecord> allKnown = Collections.emptyList();
 	private List<String> queueNames = Collections.emptyList();
 	private String activeQueue = PATCHES_DIR;
 
-	public MqManager(HgRepository hgRepo) {
-		repo = hgRepo;
+	/*package-local*/ MqManager(Internals internalRepo) {
+		repo = internalRepo;
 	}
 	
 	/**
 	 * Updates manager with up-to-date state of the mercurial queues.
+	 * @return <code>this</code> for convenience
 	 */
-	public void refresh() throws HgInvalidControlFileException {
+	public MqManager refresh() throws HgInvalidControlFileException {
 		applied = allKnown = Collections.emptyList();
 		queueNames = Collections.emptyList();
-		Internals repoImpl = HgInternals.getImplementationRepo(repo);
 		final LogFacility log = repo.getSessionContext().getLog();
 		try {
-			File queues = repoImpl.getFileFromRepoDir("patches.queues");
+			File queues = repo.getFileFromRepoDir("patches.queues");
 			if (queues.isFile()) {
 				LineReader lr = new LineReader(queues, log).trimLines(true).skipEmpty(true);
 				lr.read(new LineReader.SimpleLineCollector(), queueNames = new LinkedList<String>());
 			}
 			final String queueLocation; // path under .hg to patch queue information (status, series and diff files)
-			File activeQueueFile = repoImpl.getFileFromRepoDir("patches.queue");
+			File activeQueueFile = repo.getFileFromRepoDir("patches.queue");
 			// file is there only if it's not default queue ('patches') that is active
 			if (activeQueueFile.isFile()) {
 				ArrayList<String> contents = new ArrayList<String>();
@@ -100,8 +102,8 @@
 					return Path.create(sb);
 				}
 			};
-			final File fileStatus = repoImpl.getFileFromRepoDir(queueLocation + "status");
-			final File fileSeries = repoImpl.getFileFromRepoDir(queueLocation + "series");
+			final File fileStatus = repo.getFileFromRepoDir(queueLocation + "status");
+			final File fileSeries = repo.getFileFromRepoDir(queueLocation + "series");
 			if (fileStatus.isFile()) {
 				new LineReader(fileStatus, log).read(new LineReader.LineConsumer<List<PatchRecord>>() {
 	
@@ -140,6 +142,7 @@
 			th.setStackTrace(ex.getStackTrace());
 			throw th;
 		}
+		return this;
 	}
 	
 	/**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/tmatesoft/hg/repo/ext/Rebase.java	Mon Oct 29 19:04:13 2012 +0100
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2012 TMate Software Ltd
+ *  
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * For information on how to redistribute this software under
+ * the terms of a license other than GNU General Public License
+ * contact TMate Software at support@hg4j.com
+ */
+package org.tmatesoft.hg.repo.ext;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import org.tmatesoft.hg.core.HgBadNodeidFormatException;
+import org.tmatesoft.hg.core.HgIOException;
+import org.tmatesoft.hg.core.Nodeid;
+import org.tmatesoft.hg.internal.Internals;
+import org.tmatesoft.hg.internal.LineReader;
+import org.tmatesoft.hg.repo.HgInvalidFileException;
+import org.tmatesoft.hg.repo.HgInvalidStateException;
+
+/**
+ * Support for standard Rebase extension.
+ * 
+ * @see http://mercurial.selenic.com/wiki/RebaseExtension
+ * @since 1.1
+ * @author Artem Tikhomirov
+ * @author TMate Software Ltd.
+ */
+public class Rebase {
+	private Internals repo;
+	private Nodeid workingDirParent;
+	private Nodeid destRevision;
+	private Nodeid externalParent;
+	private Map<Nodeid, Nodeid> state;
+	private boolean collapse;
+	private boolean keepOriginalRevisions;
+	private boolean keepBranchNames;
+	
+	/*package-local*/ Rebase(Internals internalRepo) {
+		repo = internalRepo;
+	}
+
+	public Rebase refresh() throws HgIOException {
+		workingDirParent = null;
+		destRevision = null;
+		externalParent = null;
+		state = null;
+		File f = repo.getFileFromRepoDir("rebasestate");
+		if (!f.exists()) {
+			return this;
+		}
+		state = new HashMap<Nodeid, Nodeid>();
+		try {
+			LineReader lr = new LineReader(f, repo.getSessionContext().getLog());
+			ArrayList<String> contents = new ArrayList<String>();
+			lr.read(new LineReader.SimpleLineCollector(), contents);
+			Iterator<String> it = contents.iterator();
+			workingDirParent = Nodeid.fromAscii(it.next());
+			destRevision = Nodeid.fromAscii(it.next());
+			externalParent = Nodeid.fromAscii(it.next());
+			collapse = "1".equals(it.next());
+			keepOriginalRevisions = "1".equals(it.next());
+			keepBranchNames = "1".equals(it.next());
+			final String nullmerge = "-2";
+			while (it.hasNext()) {
+				String line = it.next();
+				int x = line.indexOf(':');
+				if (x == -1) {
+					throw new HgInvalidStateException(line);
+				}
+				Nodeid oldRev = Nodeid.fromAscii(line.substring(0, x));
+				Nodeid newRev;
+				if (line.regionMatches(x+1, nullmerge, 0, nullmerge.length())) {
+					newRev = null;
+				} else {
+					newRev = Nodeid.fromAscii(line.substring(x+1));
+				}
+				state.put(oldRev, newRev);
+			}
+		} catch (NoSuchElementException ex) {
+			throw new HgIOException("Bad format of rebase state file", f);
+		} catch (HgBadNodeidFormatException ex) {
+			throw new HgIOException("Bad format of rebase state file", ex, f);
+		} catch (HgInvalidFileException ex) {
+			throw new HgIOException("Bad format of rebase state file", ex, f);
+		}
+		return this;
+	}
+	
+	/**
+	 * Tells whether rebase process was interrupted to manually resolve a merge 
+	 * and can be resumed or aborted.
+	 * 
+	 * @return <code>true</code> when rebase is in progress 
+	 */
+	public boolean isRebaseInProgress() {
+		return state != null;
+	}
+
+	public Nodeid getWorkingDirParent() {
+		assert isRebaseInProgress();
+		return workingDirParent;
+	}
+	
+	public Nodeid getTarget() {
+		assert isRebaseInProgress();
+		return destRevision;
+	}
+	
+	public Nodeid getExternalParent() {
+		assert isRebaseInProgress();
+		assert collapse;
+		return externalParent;
+	}
+	
+	public boolean isCollapse() {
+		return collapse;
+	}
+	
+	public boolean isKeepOriginalRevisions() {
+		return keepOriginalRevisions;
+	}
+
+	public boolean isKeepBranchNames() {
+		return keepBranchNames;
+	}
+}
--- a/test/org/tmatesoft/hg/test/TestMqExtension.java	Mon Oct 29 18:16:21 2012 +0100
+++ b/test/org/tmatesoft/hg/test/TestMqExtension.java	Mon Oct 29 19:04:13 2012 +0100
@@ -23,7 +23,9 @@
 
 import org.junit.Test;
 import org.tmatesoft.hg.repo.HgRepository;
+import org.tmatesoft.hg.repo.ext.HgExtensionsManager;
 import org.tmatesoft.hg.repo.ext.MqManager;
+import org.tmatesoft.hg.repo.ext.HgExtensionsManager.HgExt;
 import org.tmatesoft.hg.repo.ext.MqManager.PatchRecord;
 
 /**
@@ -37,7 +39,9 @@
 	@Test
 	public void testMqManager() throws Exception {
 		HgRepository repo = Configuration.get().find("test-mq");
-		MqManager mqManager = new MqManager(repo);
+		HgExtensionsManager extManager = repo.getExtensions();
+		assertTrue(extManager.isEnabled(HgExt.MQ));
+		MqManager mqManager = extManager.getMQ();
 		mqManager.refresh();
 		OutputParser.Stub output = new OutputParser.Stub();
 		ExecHelper eh = new ExecHelper(output, repo.getWorkingDir());