Package org.tmatesoft.hg.core

Source Code of org.tmatesoft.hg.core.HgChangeset$ShareDataStruct

/*
* Copyright (c) 2011-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.core;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.tmatesoft.hg.internal.PhasesHelper;
import org.tmatesoft.hg.repo.HgChangelog;
import org.tmatesoft.hg.repo.HgChangelog.RawChangeset;
import org.tmatesoft.hg.repo.HgInternals;
import org.tmatesoft.hg.repo.HgPhase;
import org.tmatesoft.hg.repo.HgInvalidStateException;
import org.tmatesoft.hg.repo.HgRepository;
import org.tmatesoft.hg.repo.HgRuntimeException;
import org.tmatesoft.hg.repo.HgStatusCollector;
import org.tmatesoft.hg.repo.HgParentChildMap;
import org.tmatesoft.hg.util.CancelledException;
import org.tmatesoft.hg.util.Path;


/**
* Record in the Mercurial changelog, describing single commit.
*
* Not thread-safe, don't try to read from different threads
*
* @author Artem Tikhomirov
* @author TMate Software Ltd.
*/
public class HgChangeset implements Cloneable {

  // these get initialized
  private RawChangeset changeset;
  private int revNumber;
  private Nodeid nodeid;

  class ShareDataStruct {
    ShareDataStruct(HgStatusCollector statusCollector, Path.Source pathFactory) {
      statusHelper = statusCollector;
      pathHelper = pathFactory;
    }
    public final HgStatusCollector statusHelper;
    public final Path.Source pathHelper;

    public HgParentChildMap<HgChangelog> parentHelper;
    public PhasesHelper phaseHelper;
  };

  // Helpers/utilities shared among few instances of HgChangeset
  private final ShareDataStruct shared;

  // these are built on demand
  private List<HgFileRevision> modifiedFiles, addedFiles;
  private List<Path> deletedFiles;
  private byte[] parent1, parent2;
 

  // XXX consider CommandContext with StatusCollector, PathPool etc. Commands optionally get CC through a cons or create new
  // and pass it around
  /*package-local*/HgChangeset(HgStatusCollector statusCollector, Path.Source pathFactory) {
    shared = new ShareDataStruct(statusCollector, pathFactory);
  }

  /*package-local*/ void init(int localRevNumber, Nodeid nid, RawChangeset rawChangeset) {
    revNumber = localRevNumber;
    nodeid = nid;
    changeset = rawChangeset.clone();
    modifiedFiles = addedFiles = null;
    deletedFiles = null;
    parent1 = parent2 = null;
    // keep references to shared (and everything in there: parentHelper, statusHelper, phaseHelper and pathHelper)
  }

  /*package-local*/ void setParentHelper(HgParentChildMap<HgChangelog> pw) {
    if (pw != null) {
      if (pw.getRepo() != shared.statusHelper.getRepo()) {
        throw new IllegalArgumentException();
      }
    }
    shared.parentHelper = pw;
  }

  /**
   * Index of the changeset in local repository. Note, this number is relevant only for local repositories/operations, use
   * {@link Nodeid nodeid} to uniquely identify a revision.
   *  
   * @return index of the changeset revision
   */
  public int getRevisionIndex() {
    return revNumber;
  }

  /**
   * Unique identity of this changeset revision
   * @return revision identifier, never <code>null</code>
   */
  public Nodeid getNodeid() {
    return nodeid;
  }

  /**
   * Name of the user who made this commit
   * @return author of the commit, never <code>null</code>
   */
  public String getUser() {
    return changeset.user();
  }
 
  /**
   * Commit description
   * @return content of the corresponding field in changeset record; empty string if none specified.
   */
  public String getComment() {
    return changeset.comment();
  }

  /**
   * Name of the branch this commit was made in. Returns "default" for main branch.
   * @return name of the branch, non-empty string
   */
  public String getBranch() {
    return changeset.branch();
  }

  /**
   * @return used to be String, now {@link HgDate}, use {@link HgDate#toString()} to get same result as before
   */
  public HgDate getDate() {
    return new HgDate(changeset.date().getTime(), changeset.timezone());
  }

  /**
   * Indicates revision of manifest that tracks state of repository at the moment of this commit.
   * Note, may be {@link Nodeid#NULL} in certain scenarios (e.g. first changeset in an empty repository, usually by bogus tools)
   * 
   * @return revision identifier, never <code>null</code>
   */
  public Nodeid getManifestRevision() {
    return changeset.manifest();
  }

  /**
   * Lists names of files affected by this commit, as recorded in the changeset itself. Unlike {@link #getAddedFiles()},
   * {@link #getModifiedFiles()} and {@link #getRemovedFiles()}, this method doesn't analyze actual changes done
   * in the commit, rather extracts value from the changeset record.
   *
   * List returned by this method may be empty, while aforementioned methods may produce non-empty result.
   *  
   * @return list of filenames, never <code>null</code>
   */
  public List<Path> getAffectedFiles() {
    // reports files as recorded in changelog. Note, merge revisions may have no
    // files listed, and thus this method would return empty list, while
    // #getModifiedFiles() would return list with merged file(s) (because it uses status to get 'em, not
    // what #files() gives).
    ArrayList<Path> rv = new ArrayList<Path>(changeset.files().size());
    for (String name : changeset.files()) {
      rv.add(shared.pathHelper.path(name));
    }
    return rv;
  }

  /**
   * Figures out files and specific revisions thereof that were modified in this commit
   * 
   * @return revisions of files modified in this commit
   * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em>
   */
  public List<HgFileRevision> getModifiedFiles() throws HgRuntimeException {
    if (modifiedFiles == null) {
      initFileChanges();
    }
    return modifiedFiles;
  }

  /**
   * Figures out files added in this commit
   *
   * @return revisions of files added in this commit
   * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em>
   */
  public List<HgFileRevision> getAddedFiles() throws HgRuntimeException {
    if (addedFiles == null) {
      initFileChanges();
    }
    return addedFiles;
  }

  /**
   * Figures out files that were deleted as part of this commit
   *
   * @return revisions of files deleted in this commit
   * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em>
   */
  public List<Path> getRemovedFiles() throws HgRuntimeException {
    if (deletedFiles == null) {
      initFileChanges();
    }
    return deletedFiles;
  }

  public boolean isMerge() throws HgRuntimeException {
    // p1 == -1 and p2 != -1 is legitimate case
    return !(getFirstParentRevision().isNull() || getSecondParentRevision().isNull());
  }
 
  /**
   * @return never <code>null</code>
   * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em>
   */
  public Nodeid getFirstParentRevision() throws HgRuntimeException {
    if (shared.parentHelper != null) {
      return shared.parentHelper.safeFirstParent(nodeid);
    }
    // read once for both p1 and p2
    if (parent1 == null) {
      parent1 = new byte[20];
      parent2 = new byte[20];
      getRepo().getChangelog().parents(revNumber, new int[2], parent1, parent2);
    }
    return Nodeid.fromBinary(parent1, 0);
  }
 
  /**
   * @return never <code>null</code>
   * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em>
   */
  public Nodeid getSecondParentRevision() throws HgRuntimeException {
    if (shared.parentHelper != null) {
      return shared.parentHelper.safeSecondParent(nodeid);
    }
    if (parent2 == null) {
      parent1 = new byte[20];
      parent2 = new byte[20];
      getRepo().getChangelog().parents(revNumber, new int[2], parent1, parent2);
    }
    return Nodeid.fromBinary(parent2, 0);
  }

  /** 
   * Tells the phase this changeset belongs to.
   * @return one of {@link HgPhase} values
   * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em>
   */
  public HgPhase getPhase() throws HgRuntimeException {
    if (shared.phaseHelper == null) {
      // XXX would be handy to obtain ProgressSupport (perhaps, from statusHelper?)
      // and pass it to #init(), so that  there could be indication of file being read and cache being built
      synchronized (shared) {
        // ensure field is initialized only once
        if (shared.phaseHelper == null) {
          shared.phaseHelper = new PhasesHelper(HgInternals.getImplementationRepo(getRepo()), shared.parentHelper);
        }
      }
    }
    return shared.phaseHelper.getPhase(this);
  }

  /**
   * Create a copy of this changeset
   */
  @Override
  public HgChangeset clone() {
    try {
      HgChangeset copy = (HgChangeset) super.clone();
      // copy.changeset references this.changeset, doesn't need own copy
      return copy;
    } catch (CloneNotSupportedException ex) {
      throw new InternalError(ex.toString());
    }
  }
 
  @Override
  public String toString() {
    return String.format("HgChangeset(%d:%s)", revNumber, nodeid.shortNotation());

  }
 
  private HgRepository getRepo() {
    return shared.statusHelper.getRepo();
  }

  private /*synchronized*/ void initFileChanges() throws HgRuntimeException {
    ArrayList<Path> deleted = new ArrayList<Path>();
    ArrayList<HgFileRevision> modified = new ArrayList<HgFileRevision>();
    ArrayList<HgFileRevision> added = new ArrayList<HgFileRevision>();
    HgStatusCollector.Record r = new HgStatusCollector.Record();
    try {
      shared.statusHelper.change(revNumber, r);
    } catch (CancelledException ex) {
      // Record can't cancel
      throw new HgInvalidStateException("Internal error");
    }
    final HgRepository repo = getRepo();
    for (Path s : r.getModified()) {
      Nodeid nid = r.nodeidAfterChange(s);
      if (nid == null) {
        throw new HgInvalidStateException(String.format("For the file %s recorded as modified in changeset %d couldn't find revision after change", s, revNumber));
      }
      modified.add(new HgFileRevision(repo, nid, null, s, null));
    }
    final Map<Path, Path> copied = r.getCopied();
    for (Path s : r.getAdded()) {
      Nodeid nid = r.nodeidAfterChange(s);
      if (nid == null) {
        throw new HgInvalidStateException(String.format("For the file %s recorded as added in changeset %d couldn't find revision after change", s, revNumber));
      }
      added.add(new HgFileRevision(repo, nid, null, s, copied.get(s)));
    }
    for (Path s : r.getRemoved()) {
      // with Path from getRemoved, may just copy
      deleted.add(s);
    }
    modified.trimToSize();
    added.trimToSize();
    deleted.trimToSize();
    modifiedFiles = Collections.unmodifiableList(modified);
    addedFiles = Collections.unmodifiableList(added);
    deletedFiles = Collections.unmodifiableList(deleted);
  }
}
TOP

Related Classes of org.tmatesoft.hg.core.HgChangeset$ShareDataStruct

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.