# HG changeset patch # User Artem Tikhomirov # Date 1351533853 -3600 # Node ID d2f6ab541330a519bf9c368cd59ce790239363a8 # Parent 465316bf97e8e7c533f289198839909899e15437 Change the way extensions are accessed (with ExtensionsManager now), add preliminary Rebase extension support diff -r 465316bf97e8 -r d2f6ab541330 cmdline/org/tmatesoft/hg/console/Main.java --- 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(); diff -r 465316bf97e8 -r d2f6ab541330 src/org/tmatesoft/hg/internal/Internals.java --- 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(); } diff -r 465316bf97e8 -r d2f6ab541330 src/org/tmatesoft/hg/internal/PhasesHelper.java --- 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 } } diff -r 465316bf97e8 -r d2f6ab541330 src/org/tmatesoft/hg/repo/HgBookmarks.java --- 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 bm = new LinkedHashMap(); diff -r 465316bf97e8 -r d2f6ab541330 src/org/tmatesoft/hg/repo/HgDirstate.java --- 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); diff -r 465316bf97e8 -r d2f6ab541330 src/org/tmatesoft/hg/repo/HgRepository.java --- 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> streamsCache = new HashMap>(); @@ -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 diff -r 465316bf97e8 -r d2f6ab541330 src/org/tmatesoft/hg/repo/ext/HgExtensionsManager.java --- /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; + } +} diff -r 465316bf97e8 -r d2f6ab541330 src/org/tmatesoft/hg/repo/ext/MqManager.java --- 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 applied = Collections.emptyList(); private List allKnown = Collections.emptyList(); private List 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 this 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()); } 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 contents = new ArrayList(); @@ -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>() { @@ -140,6 +142,7 @@ th.setStackTrace(ex.getStackTrace()); throw th; } + return this; } /** diff -r 465316bf97e8 -r d2f6ab541330 src/org/tmatesoft/hg/repo/ext/Rebase.java --- /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 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(); + try { + LineReader lr = new LineReader(f, repo.getSessionContext().getLog()); + ArrayList contents = new ArrayList(); + lr.read(new LineReader.SimpleLineCollector(), contents); + Iterator 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 true 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; + } +} diff -r 465316bf97e8 -r d2f6ab541330 test/org/tmatesoft/hg/test/TestMqExtension.java --- 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());