# HG changeset patch # User Artem Tikhomirov # Date 1308191031 -7200 # Node ID df9d2854d3d65f9db28600cb226eb32a6b19ddd3 # Parent 4817d4ccc50e4e46585d345d08d386d5344b0246 Initial access to subrepositories diff -r 4817d4ccc50e -r df9d2854d3d6 cmdline/org/tmatesoft/hg/console/Main.java --- a/cmdline/org/tmatesoft/hg/console/Main.java Wed Jun 15 18:06:39 2011 +0200 +++ b/cmdline/org/tmatesoft/hg/console/Main.java Thu Jun 16 04:23:51 2011 +0200 @@ -41,6 +41,8 @@ import org.tmatesoft.hg.repo.HgRepository; import org.tmatesoft.hg.repo.HgStatusCollector; import org.tmatesoft.hg.repo.HgStatusInspector; +import org.tmatesoft.hg.repo.HgSubrepoLocation; +import org.tmatesoft.hg.repo.HgSubrepoLocation.Kind; import org.tmatesoft.hg.repo.HgWorkingCopyStatusCollector; import org.tmatesoft.hg.repo.HgChangelog.RawChangeset; import org.tmatesoft.hg.util.Pair; @@ -70,7 +72,8 @@ public static void main(String[] args) throws Exception { Main m = new Main(args); - m.testReadWorkingCopy(); + m.testSubrepos(); +// m.testReadWorkingCopy(); // m.testParents(); // m.testEffectiveFileLog(); // m.testCatAtCsetRevision(); @@ -86,7 +89,24 @@ // m.dumpCompleteManifestHigh(); // m.bunchOfTests(); } - + + private void testSubrepos() throws Exception { + for (HgSubrepoLocation l : hgRepo.getSubrepositories()) { + System.out.println(l.getLocation()); + System.out.println(l.getSource()); + System.out.println(l.getType()); + System.out.println(l.isCommitted() ? l.getRevision() : "not yet committed"); + if (l.getType() == Kind.Hg) { + HgRepository r = l.getRepo(); + System.out.printf("%s has %d revisions\n", l.getLocation(), r.getChangelog().getLastRevision() + 1); + if (r.getChangelog().getLastRevision() >= 0) { + final RawChangeset c = r.getChangelog().range(TIP, TIP).get(0); + System.out.printf("TIP: %s %s %s\n", c.user(), c.dateString(), c.comment()); + } + } + } + } + private void testReadWorkingCopy() throws Exception { for (String fname : cmdLineOpts.getList("")) { HgDataFile fn = hgRepo.getFileNode(fname); diff -r 4817d4ccc50e -r df9d2854d3d6 src/org/tmatesoft/hg/internal/SubrepoManager.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/internal/SubrepoManager.java Thu Jun 16 04:23:51 2011 +0200 @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2011 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.internal; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.tmatesoft.hg.repo.HgRepository; +import org.tmatesoft.hg.repo.HgSubrepoLocation; + +/** + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +public class SubrepoManager /* XXX RepoChangeNotifier, RepoChangeListener */{ + + private final HgRepository repo; + private List subRepos; + + public SubrepoManager(HgRepository hgRepo) { + assert hgRepo != null; + repo = hgRepo; + } + + private List readActualState() { + File hgsubFile = new File(repo.getWorkingDir(), ".hgsub"); + if (!hgsubFile.canRead()) { + return Collections.emptyList(); + } + try { + Map state; // path -> revision + File hgstateFile = new File(repo.getWorkingDir(), ".hgsubstate"); + if (hgstateFile.canRead()) { + state = readState(new BufferedReader(new FileReader(hgstateFile))); + } else { + state = Collections.emptyMap(); + } + BufferedReader br = new BufferedReader(new FileReader(hgsubFile)); + return readConfig(br, state); + } catch (IOException ex) { + ex.printStackTrace(); // XXX log. Generally, shall not happen + } + return Collections.emptyList(); + } + + private List readConfig(BufferedReader br, Map substate) throws IOException { + try { + String line; + LinkedList res = new LinkedList(); + while ((line = br.readLine()) != null) { + int sep = line.indexOf('='); + if (sep == -1) { + continue; + } + // since both key and value are referenced from HgSubrepoLocation, doesn't make sense + // to have separate String instances (new String(line.substring())) + String key = line.substring(0, sep).trim(); + String value = line.substring(sep + 1).trim(); + if (value.length() == 0) { + // XXX log bad line? + continue; + } + HgSubrepoLocation.Kind kind = HgSubrepoLocation.Kind.Hg; + int kindEnd = value.indexOf(']', 1); + if (value.charAt(0) == '[' && kindEnd != -1) { + String kindStr = value.substring(1, kindEnd); + value = value.substring(kindEnd + 1); + if ("svn".equals(kindStr)) { + kind = HgSubrepoLocation.Kind.SVN; + } else if ("git".equals(kindStr)) { + kind = HgSubrepoLocation.Kind.Git; + } + } + // TODO respect paths mappings in config file + HgSubrepoLocation loc = new HgSubrepoLocation(repo, key, value, kind, substate.get(key)); + res.add(loc); + } + return Arrays.asList(res.toArray(new HgSubrepoLocation[res.size()])); + } finally { + br.close(); + } + } + + private Map readState(BufferedReader br) throws IOException { + HashMap rv = new HashMap(); + try { + String line; + while ((line = br.readLine()) != null) { + int sep = line.trim().indexOf(' '); + if (sep != -1) { + rv.put(line.substring(sep+1).trim(), line.substring(0, sep).trim()); + } + } + } finally { + br.close(); + } + return rv; + } + + public List all(/*int revision, or TIP|WC*/) { + if (subRepos == null) { + subRepos = readActualState(); + } + return subRepos; + } +} diff -r 4817d4ccc50e -r df9d2854d3d6 src/org/tmatesoft/hg/repo/HgRepository.java --- a/src/org/tmatesoft/hg/repo/HgRepository.java Wed Jun 15 18:06:39 2011 +0200 +++ b/src/org/tmatesoft/hg/repo/HgRepository.java Thu Jun 16 04:23:51 2011 +0200 @@ -36,6 +36,7 @@ import org.tmatesoft.hg.internal.Filter; import org.tmatesoft.hg.internal.RequiresFile; import org.tmatesoft.hg.internal.RevlogStream; +import org.tmatesoft.hg.internal.SubrepoManager; import org.tmatesoft.hg.util.CancelledException; import org.tmatesoft.hg.util.Pair; import org.tmatesoft.hg.util.Path; @@ -75,6 +76,7 @@ private HgTags tags; private HgBranches branches; private HgMergeState mergeState; + private SubrepoManager subRepos; // XXX perhaps, shall enable caching explicitly private final HashMap> streamsCache = new HashMap>(); @@ -241,6 +243,18 @@ public File getWorkingDir() { return workingDir; } + + /** + * Provides access to sub-repositories defined in this repository. Enumerated sub-repositories are those directly + * known, not recursive collection of all nested sub-repositories. + * @return list of all known sub-repositories in this repository, or empty list if none found. + */ + public List getSubrepositories() { + if (subRepos == null) { + subRepos = new SubrepoManager(this); + } + return subRepos.all(); + } // shall be of use only for internal classes /*package-local*/ File getRepositoryRoot() { diff -r 4817d4ccc50e -r df9d2854d3d6 src/org/tmatesoft/hg/repo/HgSubrepoLocation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/repo/HgSubrepoLocation.java Thu Jun 16 04:23:51 2011 +0200 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2011 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; + +import java.io.File; + +import org.tmatesoft.hg.core.HgBadStateException; +import org.tmatesoft.hg.core.HgException; +import org.tmatesoft.hg.internal.Experimental; +import org.tmatesoft.hg.util.Path; + +/** + * WORK IN PROGRESS, DO NOT USE + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +@Experimental(reason="Work in progress") +public class HgSubrepoLocation { + + private final HgRepository owner; + private final Kind kind; + private final Path location; + private final String source; + private final String revInfo; + + public enum Kind { Hg, SVN, Git, } + + public HgSubrepoLocation(HgRepository parentRepo, String repoLocation, String actualLocation, Kind type, String revision) { + owner = parentRepo; + location = Path.create(repoLocation); + source = actualLocation; + kind = type; + revInfo = revision; + } + + // as defined in .hgsub, key value + public Path getLocation() { + return location; + } + + // value from .hgsub + public String getSource() { + return source; + } + + public Kind getType() { + return kind; + } + + public String getRevision() { + return revInfo; + } + + /** + * @return whether this sub repository is known only locally + */ + public boolean isCommitted() { + return revInfo != null; + } + + /** + * @return true when there are local changes in the sub repository + */ + public boolean hasChanges() { + throw HgRepository.notImplemented(); + } + +// public boolean isLocal() { +// } + + public HgRepository getOwner() { + return owner; + } + + public HgRepository getRepo() throws HgException { + if (kind != Kind.Hg) { + throw new HgBadStateException(); + } + return new HgLookup().detect(new File(owner.getWorkingDir(), source)); + } +}