Mercurial > jhg
diff src/org/tmatesoft/hg/core/HgDiffCommand.java @ 629:5f52074707b2
Diff/blame methods as command, their residence in HgDataFile was a mistake
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Wed, 22 May 2013 16:46:15 +0200 |
parents | |
children | 72c979555cb8 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/core/HgDiffCommand.java Wed May 22 16:46:15 2013 +0200 @@ -0,0 +1,171 @@ +/* + * 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.HgInternals.wrongRevisionIndex; +import static org.tmatesoft.hg.repo.HgRepository.NO_REVISION; +import static org.tmatesoft.hg.repo.HgRepository.TIP; + +import org.tmatesoft.hg.internal.BlameHelper; +import org.tmatesoft.hg.internal.Experimental; +import org.tmatesoft.hg.internal.FileHistory; +import org.tmatesoft.hg.internal.FileRevisionHistoryChunk; +import org.tmatesoft.hg.repo.HgDataFile; +import org.tmatesoft.hg.repo.HgInternals; +import org.tmatesoft.hg.repo.HgRepository; +import org.tmatesoft.hg.repo.HgRuntimeException; +import org.tmatesoft.hg.util.CancelledException; +import org.tmatesoft.hg.util.Path; + +/** + * 'hg diff' counterpart, with similar, diff-based, functionality + * + * @since 1.1 + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +@Experimental(reason="#execute* methods might get renamed") +public class HgDiffCommand extends HgAbstractCommand<HgDiffCommand> { + + private final HgRepository repo; + private HgDataFile df; + private int clogRevIndexStart, clogRevIndexEnd; + private int clogRevIndexToParents; + private HgIterateDirection iterateDirection = HgIterateDirection.NewToOld; + + public HgDiffCommand(HgRepository hgRepo) { + repo = hgRepo; + } + + public HgDiffCommand file(Path file) { + df = repo.getFileNode(file); + return this; + } + + public HgDiffCommand file(HgDataFile file) { + df = file; + return this; + } + + public HgDiffCommand range(int changelogRevIndexStart, int changelogRevIndexEnd) { + clogRevIndexStart = changelogRevIndexStart; + clogRevIndexEnd = changelogRevIndexEnd; + return this; + } + + // FIXME javadoc when needed and difference with range + public HgDiffCommand changeset(int changelogRevIndex) { + clogRevIndexToParents = changelogRevIndex; + return this; + } + + // FIXME javadoc when needed + public HgDiffCommand order(HgIterateDirection order) { + iterateDirection = order; + return this; + } + + // FIXME progress and cancellation + + /** + * mimic 'hg diff -r clogRevIndex1 -r clogRevIndex2' + */ + public void executeDiff(HgBlameInspector insp) throws HgCallbackTargetException, CancelledException, HgException { + try { + int fileRevIndex1 = fileRevIndex(df, clogRevIndexStart); + int fileRevIndex2 = fileRevIndex(df, clogRevIndexEnd); + BlameHelper bh = new BlameHelper(insp); + bh.prepare(df, clogRevIndexStart, clogRevIndexEnd); + bh.diff(fileRevIndex1, clogRevIndexStart, fileRevIndex2, clogRevIndexEnd); + } catch (HgRuntimeException ex) { + throw new HgLibraryFailureException(ex); + } + } + + /** + * Walk file history range and report changes (diff) for each revision + */ + public void executeAnnotate(HgBlameInspector insp) throws HgCallbackTargetException, CancelledException, HgException { + if (wrongRevisionIndex(clogRevIndexStart) || wrongRevisionIndex(clogRevIndexEnd)) { + throw new IllegalArgumentException(); + } + // FIXME check file and range are set + try { + // Note, changelogRevIndexEnd may be TIP, while the code below doesn't tolerate constants + // + int lastRevision = repo.getChangelog().getLastRevision(); + if (clogRevIndexEnd == TIP) { + clogRevIndexEnd = lastRevision; + } + HgInternals.checkRevlogRange(clogRevIndexStart, clogRevIndexEnd, lastRevision); + if (!df.exists()) { + return; + } + BlameHelper bh = new BlameHelper(insp); + FileHistory fileHistory = bh.prepare(df, clogRevIndexStart, clogRevIndexEnd); + + int[] fileClogParentRevs = new int[2]; + int[] fileParentRevs = new int[2]; + for (FileRevisionHistoryChunk fhc : fileHistory.iterate(iterateDirection)) { + for (int fri : fhc.fileRevisions(iterateDirection)) { + int clogRevIndex = fhc.changeset(fri); + // the way we built fileHistory ensures we won't walk past [changelogRevIndexStart..changelogRevIndexEnd] + assert clogRevIndex >= clogRevIndexStart; + assert clogRevIndex <= clogRevIndexEnd; + fhc.fillFileParents(fri, fileParentRevs); + fhc.fillCsetParents(fri, fileClogParentRevs); + bh.annotateChange(fri, clogRevIndex, fileParentRevs, fileClogParentRevs); + } + } + } catch (HgRuntimeException ex) { + throw new HgLibraryFailureException(ex); + } + } + + /** + * Annotates changes of the file against its parent(s). + * Unlike {@link #annotate(HgDataFile, int, Inspector, HgIterateDirection)}, doesn't + * walk file history, looks at the specified revision only. Handles both parents (if merge revision). + */ + public void executeAnnotateSingleRevision(HgBlameInspector insp) throws HgCallbackTargetException, CancelledException, HgException { + try { + int changelogRevisionIndex = clogRevIndexToParents; + // TODO detect if file is text/binary (e.g. looking for chars < ' ' and not \t\r\n\f + int fileRevIndex = fileRevIndex(df, changelogRevisionIndex); + int[] fileRevParents = new int[2]; + df.parents(fileRevIndex, fileRevParents, null, null); + if (changelogRevisionIndex == TIP) { + changelogRevisionIndex = df.getChangesetRevisionIndex(fileRevIndex); + } + int[] fileClogParentRevs = new int[2]; + fileClogParentRevs[0] = fileRevParents[0] == NO_REVISION ? NO_REVISION : df.getChangesetRevisionIndex(fileRevParents[0]); + fileClogParentRevs[1] = fileRevParents[1] == NO_REVISION ? NO_REVISION : df.getChangesetRevisionIndex(fileRevParents[1]); + BlameHelper bh = new BlameHelper(insp); + int clogIndexStart = fileClogParentRevs[0] == NO_REVISION ? (fileClogParentRevs[1] == NO_REVISION ? 0 : fileClogParentRevs[1]) : fileClogParentRevs[0]; + bh.prepare(df, clogIndexStart, changelogRevisionIndex); + bh.annotateChange(fileRevIndex, changelogRevisionIndex, fileRevParents, fileClogParentRevs); + } catch (HgRuntimeException ex) { + throw new HgLibraryFailureException(ex); + } + } + + + private static int fileRevIndex(HgDataFile df, int csetRevIndex) throws HgRuntimeException { + Nodeid fileRev = df.getRepo().getManifest().getFileRevision(csetRevIndex, df.getPath()); + return df.getRevisionIndex(fileRev); + } +}