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@64: import java.util.ArrayList; tikhomirov@64: import java.util.Collections; tikhomirov@64: import java.util.List; tikhomirov@64: tikhomirov@64: import org.tmatesoft.hg.core.LogCommand.FileRevision; tikhomirov@129: import org.tmatesoft.hg.repo.HgChangelog.Changeset; tikhomirov@74: import org.tmatesoft.hg.repo.HgRepository; tikhomirov@94: import org.tmatesoft.hg.repo.HgStatusCollector; tikhomirov@64: import org.tmatesoft.hg.util.PathPool; tikhomirov@64: tikhomirov@64: tikhomirov@64: /** tikhomirov@64: * TODO rename to Changeset along with original Changeset moved to .repo and renamed to HgChangeset? 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@64: private final PathPool pathHelper; tikhomirov@64: tikhomirov@64: // tikhomirov@64: private Changeset changeset; tikhomirov@64: private Nodeid nodeid; tikhomirov@64: tikhomirov@64: // tikhomirov@64: private List modifiedFiles, addedFiles; tikhomirov@64: private List deletedFiles; tikhomirov@64: private int revNumber; 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@129: /*package-local*/HgChangeset(HgStatusCollector statusCollector, PathPool pathPool) { tikhomirov@64: statusHelper = statusCollector; tikhomirov@64: pathHelper = pathPool; tikhomirov@64: } tikhomirov@64: tikhomirov@64: /*package-local*/ tikhomirov@64: void init(int localRevNumber, Nodeid nid, Changeset rawChangeset) { tikhomirov@64: revNumber = localRevNumber; tikhomirov@64: nodeid = nid; tikhomirov@64: changeset = rawChangeset; tikhomirov@65: modifiedFiles = addedFiles = null; tikhomirov@65: deletedFiles = null; tikhomirov@64: } 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@64: public String getDate() { tikhomirov@64: return changeset.dateString(); 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@64: public List getModifiedFiles() { tikhomirov@64: if (modifiedFiles == null) { tikhomirov@64: initFileChanges(); tikhomirov@64: } tikhomirov@64: return modifiedFiles; tikhomirov@64: } tikhomirov@64: tikhomirov@64: 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@125: return !Nodeid.NULL.equals(getSecondParentRevision()); tikhomirov@125: } tikhomirov@124: tikhomirov@124: public Nodeid getFirstParentRevision() { tikhomirov@124: // XXX may read once for both p1 and p2 tikhomirov@124: // or use ParentWalker to minimize reads even more. tikhomirov@124: byte[] p1 = new byte[20]; tikhomirov@124: statusHelper.getRepo().getChangelog().parents(revNumber, new int[2], p1, null); tikhomirov@124: return Nodeid.fromBinary(p1, 0); tikhomirov@124: } tikhomirov@124: tikhomirov@124: public Nodeid getSecondParentRevision() { tikhomirov@124: byte[] p2 = new byte[20]; tikhomirov@124: statusHelper.getRepo().getChangelog().parents(revNumber, new int[2], null, p2); tikhomirov@124: return Nodeid.fromBinary(p2, 0); tikhomirov@124: } tikhomirov@124: tikhomirov@64: @Override tikhomirov@129: public HgChangeset clone() { tikhomirov@64: try { tikhomirov@129: HgChangeset copy = (HgChangeset) super.clone(); tikhomirov@64: copy.changeset = changeset.clone(); 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@64: ArrayList modified = new ArrayList(); tikhomirov@64: 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@64: throw new IllegalArgumentException(); tikhomirov@64: } tikhomirov@93: modified.add(new FileRevision(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@64: throw new IllegalArgumentException(); tikhomirov@64: } tikhomirov@93: added.add(new FileRevision(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: }