tikhomirov@64: /* tikhomirov@64: * Copyright (c) 2011 TMate Software Ltd tikhomirov@64: * tikhomirov@64: * This program is free software; you can redistribute it and/or modify tikhomirov@64: * it under the terms of the GNU General Public License as published by tikhomirov@64: * the Free Software Foundation; version 2 of the License. tikhomirov@64: * tikhomirov@64: * This program is distributed in the hope that it will be useful, tikhomirov@64: * but WITHOUT ANY WARRANTY; without even the implied warranty of tikhomirov@64: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the tikhomirov@64: * GNU General Public License for more details. tikhomirov@64: * tikhomirov@64: * For information on how to redistribute this software under tikhomirov@64: * the terms of a license other than GNU General Public License tikhomirov@102: * contact TMate Software at support@hg4j.com tikhomirov@64: */ tikhomirov@64: package org.tmatesoft.hg.core; tikhomirov@64: tikhomirov@195: import static org.tmatesoft.hg.core.Nodeid.NULL; tikhomirov@195: tikhomirov@64: import java.util.ArrayList; tikhomirov@64: import java.util.Collections; tikhomirov@64: import java.util.List; tikhomirov@64: tikhomirov@154: import org.tmatesoft.hg.repo.HgChangelog.RawChangeset; tikhomirov@195: import org.tmatesoft.hg.repo.HgChangelog; tikhomirov@74: import org.tmatesoft.hg.repo.HgRepository; tikhomirov@94: import org.tmatesoft.hg.repo.HgStatusCollector; tikhomirov@133: import org.tmatesoft.hg.util.Path; tikhomirov@64: tikhomirov@64: tikhomirov@64: /** tikhomirov@131: * Record in the Mercurial changelog, describing single commit. tikhomirov@131: * tikhomirov@64: * Not thread-safe, don't try to read from different threads tikhomirov@64: * tikhomirov@64: * @author Artem Tikhomirov tikhomirov@64: * @author TMate Software Ltd. tikhomirov@64: */ tikhomirov@129: public class HgChangeset implements Cloneable { tikhomirov@94: private final HgStatusCollector statusHelper; tikhomirov@142: private final Path.Source pathHelper; tikhomirov@64: tikhomirov@195: private HgChangelog.ParentWalker parentHelper; tikhomirov@195: tikhomirov@64: // tikhomirov@154: private RawChangeset changeset; tikhomirov@64: private Nodeid nodeid; tikhomirov@64: tikhomirov@64: // tikhomirov@231: private List modifiedFiles, addedFiles; tikhomirov@64: private List deletedFiles; tikhomirov@64: private int revNumber; tikhomirov@195: private byte[] parent1, parent2; tikhomirov@64: tikhomirov@64: // XXX consider CommandContext with StatusCollector, PathPool etc. Commands optionally get CC through a cons or create new tikhomirov@64: // and pass it around tikhomirov@142: /*package-local*/HgChangeset(HgStatusCollector statusCollector, Path.Source pathFactory) { tikhomirov@64: statusHelper = statusCollector; tikhomirov@142: pathHelper = pathFactory; tikhomirov@64: } tikhomirov@195: tikhomirov@195: /*package-local*/ void init(int localRevNumber, Nodeid nid, RawChangeset rawChangeset) { tikhomirov@64: revNumber = localRevNumber; tikhomirov@64: nodeid = nid; tikhomirov@196: changeset = rawChangeset.clone(); tikhomirov@65: modifiedFiles = addedFiles = null; tikhomirov@65: deletedFiles = null; tikhomirov@195: parent1 = parent2 = null; tikhomirov@195: // keep references to parentHelper, statusHelper and pathHelper tikhomirov@64: } tikhomirov@195: tikhomirov@195: /*package-local*/ void setParentHelper(HgChangelog.ParentWalker pw) { tikhomirov@195: parentHelper = pw; tikhomirov@195: if (parentHelper != null) { tikhomirov@195: if (parentHelper.getRepo() != statusHelper.getRepo()) { tikhomirov@195: throw new IllegalArgumentException(); tikhomirov@195: } tikhomirov@195: } tikhomirov@195: } tikhomirov@195: tikhomirov@65: public int getRevision() { tikhomirov@65: return revNumber; tikhomirov@65: } tikhomirov@65: public Nodeid getNodeid() { tikhomirov@65: return nodeid; tikhomirov@65: } tikhomirov@64: public String getUser() { tikhomirov@64: return changeset.user(); tikhomirov@64: } tikhomirov@64: public String getComment() { tikhomirov@64: return changeset.comment(); tikhomirov@64: } tikhomirov@64: public String getBranch() { tikhomirov@64: return changeset.branch(); tikhomirov@64: } tikhomirov@211: tikhomirov@211: /** tikhomirov@211: * @return used to be String, now {@link HgDate}, use {@link HgDate#toString()} to get same result as before tikhomirov@211: */ tikhomirov@211: public HgDate getDate() { tikhomirov@211: return new HgDate(changeset.date().getTime(), changeset.timezone()); tikhomirov@64: } tikhomirov@65: public Nodeid getManifestRevision() { tikhomirov@65: return changeset.manifest(); tikhomirov@65: } tikhomirov@64: tikhomirov@64: public List getAffectedFiles() { tikhomirov@87: // reports files as recorded in changelog. Note, merge revisions may have no tikhomirov@87: // files listed, and thus this method would return empty list, while tikhomirov@87: // #getModifiedFiles() would return list with merged file(s) (because it uses status to get 'em, not tikhomirov@87: // what #files() gives). tikhomirov@64: ArrayList rv = new ArrayList(changeset.files().size()); tikhomirov@64: for (String name : changeset.files()) { tikhomirov@64: rv.add(pathHelper.path(name)); tikhomirov@64: } tikhomirov@64: return rv; tikhomirov@64: } tikhomirov@64: tikhomirov@231: public List getModifiedFiles() { tikhomirov@64: if (modifiedFiles == null) { tikhomirov@64: initFileChanges(); tikhomirov@64: } tikhomirov@64: return modifiedFiles; tikhomirov@64: } tikhomirov@64: tikhomirov@231: public List getAddedFiles() { tikhomirov@64: if (addedFiles == null) { tikhomirov@64: initFileChanges(); tikhomirov@64: } tikhomirov@64: return addedFiles; tikhomirov@64: } tikhomirov@64: tikhomirov@64: public List getRemovedFiles() { tikhomirov@64: if (deletedFiles == null) { tikhomirov@64: initFileChanges(); tikhomirov@64: } tikhomirov@64: return deletedFiles; tikhomirov@64: } tikhomirov@64: tikhomirov@125: public boolean isMerge() { tikhomirov@195: // p1 == -1 and p2 != -1 is legitimate case tikhomirov@195: return !NULL.equals(getFirstParentRevision()) && !NULL.equals(getSecondParentRevision()); tikhomirov@125: } tikhomirov@124: tikhomirov@124: public Nodeid getFirstParentRevision() { tikhomirov@195: if (parentHelper != null) { tikhomirov@195: return parentHelper.safeFirstParent(nodeid); tikhomirov@195: } tikhomirov@195: // read once for both p1 and p2 tikhomirov@195: if (parent1 == null) { tikhomirov@195: parent1 = new byte[20]; tikhomirov@195: parent2 = new byte[20]; tikhomirov@195: statusHelper.getRepo().getChangelog().parents(revNumber, new int[2], parent1, parent2); tikhomirov@195: } tikhomirov@195: return Nodeid.fromBinary(parent1, 0); tikhomirov@124: } tikhomirov@124: tikhomirov@124: public Nodeid getSecondParentRevision() { tikhomirov@195: if (parentHelper != null) { tikhomirov@195: return parentHelper.safeSecondParent(nodeid); tikhomirov@195: } tikhomirov@195: if (parent2 == null) { tikhomirov@195: parent1 = new byte[20]; tikhomirov@195: parent2 = new byte[20]; tikhomirov@195: statusHelper.getRepo().getChangelog().parents(revNumber, new int[2], parent1, parent2); tikhomirov@195: } tikhomirov@195: return Nodeid.fromBinary(parent2, 0); tikhomirov@124: } tikhomirov@124: tikhomirov@64: @Override tikhomirov@129: public HgChangeset clone() { tikhomirov@64: try { tikhomirov@129: HgChangeset copy = (HgChangeset) super.clone(); tikhomirov@196: // copy.changeset references this.changeset, doesn't need own copy tikhomirov@64: return copy; tikhomirov@64: } catch (CloneNotSupportedException ex) { tikhomirov@64: throw new InternalError(ex.toString()); tikhomirov@64: } tikhomirov@64: } tikhomirov@64: tikhomirov@64: private /*synchronized*/ void initFileChanges() { tikhomirov@64: ArrayList deleted = new ArrayList(); tikhomirov@231: ArrayList modified = new ArrayList(); tikhomirov@231: ArrayList added = new ArrayList(); tikhomirov@94: HgStatusCollector.Record r = new HgStatusCollector.Record(); tikhomirov@64: statusHelper.change(revNumber, r); tikhomirov@64: final HgRepository repo = statusHelper.getRepo(); tikhomirov@93: for (Path s : r.getModified()) { tikhomirov@64: Nodeid nid = r.nodeidAfterChange(s); tikhomirov@64: if (nid == null) { tikhomirov@148: throw new HgBadStateException(); tikhomirov@64: } tikhomirov@231: modified.add(new HgFileRevision(repo, nid, s)); tikhomirov@64: } tikhomirov@93: for (Path s : r.getAdded()) { tikhomirov@64: Nodeid nid = r.nodeidAfterChange(s); tikhomirov@64: if (nid == null) { tikhomirov@148: throw new HgBadStateException(); tikhomirov@64: } tikhomirov@231: added.add(new HgFileRevision(repo, nid, s)); tikhomirov@64: } tikhomirov@93: for (Path s : r.getRemoved()) { tikhomirov@124: // with Path from getRemoved, may just copy tikhomirov@93: deleted.add(s); tikhomirov@64: } tikhomirov@64: modified.trimToSize(); tikhomirov@64: added.trimToSize(); tikhomirov@64: deleted.trimToSize(); tikhomirov@64: modifiedFiles = Collections.unmodifiableList(modified); tikhomirov@64: addedFiles = Collections.unmodifiableList(added); tikhomirov@64: deletedFiles = Collections.unmodifiableList(deleted); tikhomirov@64: } tikhomirov@64: }