# HG changeset patch # User Artem Tikhomirov # Date 1305771285 -7200 # Node ID 8de327242aa0aa03a42040bcdecaace473378487 # Parent d63583b47bfa66e315cf00bce872f9a274755d59 Basic information about branches diff -r d63583b47bfa -r 8de327242aa0 cmdline/org/tmatesoft/hg/console/Main.java --- a/cmdline/org/tmatesoft/hg/console/Main.java Tue May 17 05:43:09 2011 +0200 +++ b/cmdline/org/tmatesoft/hg/console/Main.java Thu May 19 04:14:45 2011 +0200 @@ -28,6 +28,7 @@ import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.internal.ByteArrayChannel; import org.tmatesoft.hg.internal.DigestHelper; +import org.tmatesoft.hg.repo.HgBranches; import org.tmatesoft.hg.repo.HgDataFile; import org.tmatesoft.hg.repo.HgInternals; import org.tmatesoft.hg.repo.HgManifest; @@ -60,7 +61,8 @@ public static void main(String[] args) throws Exception { Main m = new Main(args); - m.inflaterLengthException(); + m.dumpBranches(); +// m.inflaterLengthException(); // m.dumpIgnored(); // m.dumpDirstate(); // m.testStatusInternals(); @@ -70,6 +72,20 @@ // m.bunchOfTests(); } + private void dumpBranches() { + HgBranches b = hgRepo.getBranches(); + for (HgBranches.BranchInfo bi : b.getAllBranches()) { + System.out.print(bi.getName()); + if (bi.isClosed()) { + System.out.print("!"); + } + System.out.print(" "); + System.out.print(bi.getStart()); + System.out.print(" "); + System.out.println(bi.getHeads()); + } + } + private void inflaterLengthException() throws Exception { HgDataFile f1 = hgRepo.getFileNode("src/com/tmate/hgkit/console/Bundle.java"); HgDataFile f2 = hgRepo.getFileNode("test-repos.jar"); diff -r d63583b47bfa -r 8de327242aa0 src/org/tmatesoft/hg/repo/HgBranches.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/repo/HgBranches.java Thu May 19 04:14:45 2011 +0200 @@ -0,0 +1,134 @@ +/* + * 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.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.tmatesoft.hg.core.Nodeid; +import org.tmatesoft.hg.repo.HgChangelog.RawChangeset; +import org.tmatesoft.hg.util.ProgressSupport; + +/** + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +public class HgBranches { + + private final Map branches = new TreeMap(); + private final HgRepository repo; + + HgBranches(HgRepository hgRepo) { + repo = hgRepo; + } + + void collect(final ProgressSupport ps) { + ps.start(1 + repo.getChangelog().getRevisionCount() * 2); + final HgChangelog.ParentWalker pw = repo.getChangelog().new ParentWalker(); + pw.init(); + ps.worked(repo.getChangelog().getRevisionCount()); + final HashMap branchStart = new HashMap(); + final HashMap branchLastSeen = new HashMap(); + final HashMap> branchHeads = new HashMap>(); + final HashSet closedBranches = new HashSet(); + HgChangelog.Inspector insp = new HgChangelog.Inspector() { + + public void next(int revisionNumber, Nodeid nodeid, RawChangeset cset) { + String branchName = cset.branch(); + if (!branchStart.containsKey(branchName)) { + branchStart.put(branchName, nodeid); + branchHeads.put(branchName, new LinkedList()); + } + branchLastSeen.remove(branchName); + if ("1".equals(cset.extras().get("close"))) { + branchHeads.get(branchName).add(nodeid); // XXX what if it still has children? + closedBranches.add(branchName); + } else { + if (pw.hasChildren(nodeid)) { + // children may be in another branch + // and unless we later came across another element from this branch, + // we need to record all these as valid heads + // XXX what about next case: head1 with children in different branch, and head2 without children + // head1 would get lost + branchLastSeen.put(branchName, nodeid); + } else { + // no more children known for this node, it's (one of the) head of the branch + branchHeads.get(branchName).add(nodeid); + } + } + ps.worked(1); + } + }; + repo.getChangelog().all(insp); + for (String bn : branchLastSeen.keySet()) { + branchHeads.get(bn).add(branchLastSeen.get(bn)); + } + for (String bn : branchStart.keySet()) { + Nodeid[] heads = branchHeads.get(bn).toArray(new Nodeid[0]); + BranchInfo bi = new BranchInfo(bn, branchStart.get(bn), heads, closedBranches.contains(bn)); + branches.put(bn, bi); + } + ps.done(); + } + + public List getAllBranches() { + return new LinkedList(branches.values()); + + } + + public BranchInfo getBranch(String name) { + return branches.get(name); + } + + public static class BranchInfo { + private final String name; + private final List heads; + private final boolean closed; + private final Nodeid start; + + BranchInfo(String branchName, Nodeid first, Nodeid[] branchHeads, boolean isClosed) { + name = branchName; + start = first; + heads = Collections.unmodifiableList(new ArrayList(Arrays.asList(branchHeads))); + closed = isClosed; + } + + public String getName() { + return name; + } + public boolean isClosed() { + return closed; + } + public List getHeads() { + return heads; + } +// public Nodeid getTip() { +// } + public Nodeid getStart() { + // first node where branch appears + return start; + } + } +} diff -r d63583b47bfa -r 8de327242aa0 src/org/tmatesoft/hg/repo/HgManifest.java --- a/src/org/tmatesoft/hg/repo/HgManifest.java Tue May 17 05:43:09 2011 +0200 +++ b/src/org/tmatesoft/hg/repo/HgManifest.java Thu May 19 04:14:45 2011 +0200 @@ -149,6 +149,10 @@ public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) { if (changelog2manifest != null) { + // next assertion is not an error, rather assumption check, which is too development-related to be explicit exception - + // I just wonder if there are manifests that have two entries pointing to single changeset. It seems unrealistic, though - + // changeset records one and only one manifest nodeid + assert changelog2manifest[linkRevision] == -1 : String.format("revision:%d, link:%d, already linked to revision:%d", revisionNumber, linkRevision, changelog2manifest[linkRevision]); changelog2manifest[linkRevision] = revisionNumber; } else { if (revisionNumber != linkRevision) { @@ -186,7 +190,7 @@ } for (int u : undefinedChangelogRevision) { Nodeid manifest = repo.getChangelog().range(u, u).get(0).manifest(); - // FIXME calculate those missing effectively (e.g. cache and sort nodeids to spead lookup + // FIXME calculate those missing effectively (e.g. cache and sort nodeids to speed lookup // right away in the #next (may refactor ParentWalker's sequential and sorted into dedicated helper and reuse here) changelog2manifest[u] = repo.getManifest().getLocalRevision(manifest); } diff -r d63583b47bfa -r 8de327242aa0 src/org/tmatesoft/hg/repo/HgRepository.java --- a/src/org/tmatesoft/hg/repo/HgRepository.java Tue May 17 05:43:09 2011 +0200 +++ b/src/org/tmatesoft/hg/repo/HgRepository.java Thu May 19 04:14:45 2011 +0200 @@ -34,6 +34,7 @@ import org.tmatesoft.hg.util.FileWalker; import org.tmatesoft.hg.util.Path; import org.tmatesoft.hg.util.PathRewrite; +import org.tmatesoft.hg.util.ProgressSupport; @@ -65,13 +66,15 @@ private HgChangelog changelog; private HgManifest manifest; private HgTags tags; + private HgBranches branches; + // XXX perhaps, shall enable caching explicitly private final HashMap> streamsCache = new HashMap>(); private final org.tmatesoft.hg.internal.Internals impl = new org.tmatesoft.hg.internal.Internals(); private HgIgnore ignore; private ConfigFile configFile; - + HgRepository(String repositoryPath) { repoDir = null; repoLocation = repositoryPath; @@ -151,6 +154,14 @@ return tags; } + public final HgBranches getBranches() { + if (branches == null) { + branches = new HgBranches(this); + branches.collect(ProgressSupport.Factory.get(null)); + } + return branches; + } + public HgDataFile getFileNode(String path) { String nPath = normalizePath.rewrite(path); String storagePath = dataPathHelper.rewrite(nPath);