Mercurial > jhg
view src/org/tmatesoft/hg/core/HgCommitCommand.java @ 689:5050ee565bd1
Issue 44: Renames/copies other than for the very first revision of a file are not recognized
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Sat, 27 Jul 2013 22:06:14 +0200 |
parents | 6526d8adbc0f |
children | b4242b7e7dfe |
line wrap: on
line source
/* * Copyright (c) 2013 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 static org.tmatesoft.hg.repo.HgRepository.NO_REVISION; import java.io.IOException; import org.tmatesoft.hg.internal.COWTransaction; import org.tmatesoft.hg.internal.CommitFacility; import org.tmatesoft.hg.internal.CompleteRepoLock; import org.tmatesoft.hg.internal.FileContentSupplier; import org.tmatesoft.hg.internal.Internals; import org.tmatesoft.hg.internal.Transaction; import org.tmatesoft.hg.internal.WorkingCopyContent; import org.tmatesoft.hg.repo.HgChangelog; import org.tmatesoft.hg.repo.HgDataFile; import org.tmatesoft.hg.repo.HgInternals; import org.tmatesoft.hg.repo.HgInvalidControlFileException; import org.tmatesoft.hg.repo.HgRepository; import org.tmatesoft.hg.repo.HgRuntimeException; import org.tmatesoft.hg.repo.HgStatusCollector.Record; import org.tmatesoft.hg.repo.HgWorkingCopyStatusCollector; import org.tmatesoft.hg.util.CancelledException; import org.tmatesoft.hg.util.Outcome; import org.tmatesoft.hg.util.Outcome.Kind; import org.tmatesoft.hg.util.Pair; import org.tmatesoft.hg.util.Path; /** * 'hg commit' counterpart, commit changes * * @since 1.1 * @author Artem Tikhomirov * @author TMate Software Ltd. */ public class HgCommitCommand extends HgAbstractCommand<HgCommitCommand> { private final HgRepository repo; private String message; private String user; // nodeid of newly added revision private Nodeid newRevision; public HgCommitCommand(HgRepository hgRepo) { repo = hgRepo; } public HgCommitCommand message(String msg) { message = msg; return this; } public HgCommitCommand user(String userName) { user = userName; return this; } /** * Tell if changes in the working directory constitute merge commit. May be invoked prior to (and independently from) {@link #execute()} * * @return <code>true</code> if working directory changes are result of a merge * @throws HgLibraryFailureException to indicate unexpected issue with the repository * @throws HgException subclass thereof to indicate other specific issue with repository state */ public boolean isMergeCommit() throws HgException { try { int[] parents = new int[2]; detectParentFromDirstate(parents); return parents[0] != NO_REVISION && parents[1] != NO_REVISION; } catch (HgRuntimeException ex) { throw new HgLibraryFailureException(ex); } } /** * @throws HgException subclass thereof to indicate specific issue with the command arguments or repository state * @throws HgRepositoryLockException if failed to lock the repo for modifications * @throws IOException propagated IO errors from status walker over working directory * @throws CancelledException if execution of the command was cancelled */ public Outcome execute() throws HgException, IOException, CancelledException { if (message == null) { throw new HgBadArgumentException("Shall supply commit message", null); } final CompleteRepoLock repoLock = new CompleteRepoLock(repo); repoLock.acquire(); try { int[] parentRevs = new int[2]; detectParentFromDirstate(parentRevs); if (parentRevs[0] != NO_REVISION && parentRevs[1] != NO_REVISION) { throw new HgBadArgumentException("Sorry, I'm not yet smart enough to perform merge commits", null); } HgWorkingCopyStatusCollector sc = new HgWorkingCopyStatusCollector(repo); Record status = sc.status(HgRepository.WORKING_COPY); if (status.getModified().size() == 0 && status.getAdded().size() == 0 && status.getRemoved().size() == 0) { newRevision = Nodeid.NULL; return new Outcome(Kind.Failure, "nothing to add"); } CommitFacility cf = new CommitFacility(Internals.getInstance(repo), parentRevs[0], parentRevs[1]); for (Path m : status.getModified()) { HgDataFile df = repo.getFileNode(m); cf.add(df, new WorkingCopyContent(df)); } for (Path a : status.getAdded()) { HgDataFile df = repo.getFileNode(a); // TODO need smth explicit, like repo.createNewFileNode(Path) here // XXX might be an interesting exercise not to demand a content supplier, but instead return a "DataRequester" // object, that would indicate interest in data, and this code would "push" it to requester, so that any exception // is handled here, right away, and won't need to travel supplier and CommitFacility. (although try/catch inside // supplier.read (with empty throws declaration) cf.add(df, new FileContentSupplier(repo, a)); } for (Path r : status.getRemoved()) { HgDataFile df = repo.getFileNode(r); cf.forget(df); } cf.branch(detectBranch()); cf.user(detectUser()); Transaction.Factory trFactory = new COWTransaction.Factory(); Transaction tr = trFactory.create(repo); try { newRevision = cf.commit(message, tr); tr.commit(); } catch (RuntimeException ex) { tr.rollback(); throw ex; } catch (HgException ex) { tr.rollback(); throw ex; } return new Outcome(Kind.Success, "Commit ok"); } catch (HgRuntimeException ex) { throw new HgLibraryFailureException(ex); } finally { repoLock.release(); } } public Nodeid getCommittedRevision() { if (newRevision == null) { throw new IllegalStateException("Call #execute() first!"); } return newRevision; } private String detectBranch() throws HgInvalidControlFileException { return repo.getWorkingCopyBranchName(); } private String detectUser() { if (user != null) { return user; } // TODO HgInternals is odd place for getNextCommitUsername() return new HgInternals(repo).getNextCommitUsername(); } private void detectParentFromDirstate(int[] parents) throws HgRuntimeException { Pair<Nodeid, Nodeid> pn = repo.getWorkingCopyParents(); HgChangelog clog = repo.getChangelog(); parents[0] = pn.first().isNull() ? NO_REVISION : clog.getRevisionIndex(pn.first()); parents[1] = pn.second().isNull() ? NO_REVISION : clog.getRevisionIndex(pn.second()); } }