comparison 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
comparison
equal deleted inserted replaced
628:6526d8adbc0f 629:5f52074707b2
1 /*
2 * Copyright (c) 2013 TMate Software Ltd
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * For information on how to redistribute this software under
14 * the terms of a license other than GNU General Public License
15 * contact TMate Software at support@hg4j.com
16 */
17 package org.tmatesoft.hg.core;
18
19 import static org.tmatesoft.hg.repo.HgInternals.wrongRevisionIndex;
20 import static org.tmatesoft.hg.repo.HgRepository.NO_REVISION;
21 import static org.tmatesoft.hg.repo.HgRepository.TIP;
22
23 import org.tmatesoft.hg.internal.BlameHelper;
24 import org.tmatesoft.hg.internal.Experimental;
25 import org.tmatesoft.hg.internal.FileHistory;
26 import org.tmatesoft.hg.internal.FileRevisionHistoryChunk;
27 import org.tmatesoft.hg.repo.HgDataFile;
28 import org.tmatesoft.hg.repo.HgInternals;
29 import org.tmatesoft.hg.repo.HgRepository;
30 import org.tmatesoft.hg.repo.HgRuntimeException;
31 import org.tmatesoft.hg.util.CancelledException;
32 import org.tmatesoft.hg.util.Path;
33
34 /**
35 * 'hg diff' counterpart, with similar, diff-based, functionality
36 *
37 * @since 1.1
38 * @author Artem Tikhomirov
39 * @author TMate Software Ltd.
40 */
41 @Experimental(reason="#execute* methods might get renamed")
42 public class HgDiffCommand extends HgAbstractCommand<HgDiffCommand> {
43
44 private final HgRepository repo;
45 private HgDataFile df;
46 private int clogRevIndexStart, clogRevIndexEnd;
47 private int clogRevIndexToParents;
48 private HgIterateDirection iterateDirection = HgIterateDirection.NewToOld;
49
50 public HgDiffCommand(HgRepository hgRepo) {
51 repo = hgRepo;
52 }
53
54 public HgDiffCommand file(Path file) {
55 df = repo.getFileNode(file);
56 return this;
57 }
58
59 public HgDiffCommand file(HgDataFile file) {
60 df = file;
61 return this;
62 }
63
64 public HgDiffCommand range(int changelogRevIndexStart, int changelogRevIndexEnd) {
65 clogRevIndexStart = changelogRevIndexStart;
66 clogRevIndexEnd = changelogRevIndexEnd;
67 return this;
68 }
69
70 // FIXME javadoc when needed and difference with range
71 public HgDiffCommand changeset(int changelogRevIndex) {
72 clogRevIndexToParents = changelogRevIndex;
73 return this;
74 }
75
76 // FIXME javadoc when needed
77 public HgDiffCommand order(HgIterateDirection order) {
78 iterateDirection = order;
79 return this;
80 }
81
82 // FIXME progress and cancellation
83
84 /**
85 * mimic 'hg diff -r clogRevIndex1 -r clogRevIndex2'
86 */
87 public void executeDiff(HgBlameInspector insp) throws HgCallbackTargetException, CancelledException, HgException {
88 try {
89 int fileRevIndex1 = fileRevIndex(df, clogRevIndexStart);
90 int fileRevIndex2 = fileRevIndex(df, clogRevIndexEnd);
91 BlameHelper bh = new BlameHelper(insp);
92 bh.prepare(df, clogRevIndexStart, clogRevIndexEnd);
93 bh.diff(fileRevIndex1, clogRevIndexStart, fileRevIndex2, clogRevIndexEnd);
94 } catch (HgRuntimeException ex) {
95 throw new HgLibraryFailureException(ex);
96 }
97 }
98
99 /**
100 * Walk file history range and report changes (diff) for each revision
101 */
102 public void executeAnnotate(HgBlameInspector insp) throws HgCallbackTargetException, CancelledException, HgException {
103 if (wrongRevisionIndex(clogRevIndexStart) || wrongRevisionIndex(clogRevIndexEnd)) {
104 throw new IllegalArgumentException();
105 }
106 // FIXME check file and range are set
107 try {
108 // Note, changelogRevIndexEnd may be TIP, while the code below doesn't tolerate constants
109 //
110 int lastRevision = repo.getChangelog().getLastRevision();
111 if (clogRevIndexEnd == TIP) {
112 clogRevIndexEnd = lastRevision;
113 }
114 HgInternals.checkRevlogRange(clogRevIndexStart, clogRevIndexEnd, lastRevision);
115 if (!df.exists()) {
116 return;
117 }
118 BlameHelper bh = new BlameHelper(insp);
119 FileHistory fileHistory = bh.prepare(df, clogRevIndexStart, clogRevIndexEnd);
120
121 int[] fileClogParentRevs = new int[2];
122 int[] fileParentRevs = new int[2];
123 for (FileRevisionHistoryChunk fhc : fileHistory.iterate(iterateDirection)) {
124 for (int fri : fhc.fileRevisions(iterateDirection)) {
125 int clogRevIndex = fhc.changeset(fri);
126 // the way we built fileHistory ensures we won't walk past [changelogRevIndexStart..changelogRevIndexEnd]
127 assert clogRevIndex >= clogRevIndexStart;
128 assert clogRevIndex <= clogRevIndexEnd;
129 fhc.fillFileParents(fri, fileParentRevs);
130 fhc.fillCsetParents(fri, fileClogParentRevs);
131 bh.annotateChange(fri, clogRevIndex, fileParentRevs, fileClogParentRevs);
132 }
133 }
134 } catch (HgRuntimeException ex) {
135 throw new HgLibraryFailureException(ex);
136 }
137 }
138
139 /**
140 * Annotates changes of the file against its parent(s).
141 * Unlike {@link #annotate(HgDataFile, int, Inspector, HgIterateDirection)}, doesn't
142 * walk file history, looks at the specified revision only. Handles both parents (if merge revision).
143 */
144 public void executeAnnotateSingleRevision(HgBlameInspector insp) throws HgCallbackTargetException, CancelledException, HgException {
145 try {
146 int changelogRevisionIndex = clogRevIndexToParents;
147 // TODO detect if file is text/binary (e.g. looking for chars < ' ' and not \t\r\n\f
148 int fileRevIndex = fileRevIndex(df, changelogRevisionIndex);
149 int[] fileRevParents = new int[2];
150 df.parents(fileRevIndex, fileRevParents, null, null);
151 if (changelogRevisionIndex == TIP) {
152 changelogRevisionIndex = df.getChangesetRevisionIndex(fileRevIndex);
153 }
154 int[] fileClogParentRevs = new int[2];
155 fileClogParentRevs[0] = fileRevParents[0] == NO_REVISION ? NO_REVISION : df.getChangesetRevisionIndex(fileRevParents[0]);
156 fileClogParentRevs[1] = fileRevParents[1] == NO_REVISION ? NO_REVISION : df.getChangesetRevisionIndex(fileRevParents[1]);
157 BlameHelper bh = new BlameHelper(insp);
158 int clogIndexStart = fileClogParentRevs[0] == NO_REVISION ? (fileClogParentRevs[1] == NO_REVISION ? 0 : fileClogParentRevs[1]) : fileClogParentRevs[0];
159 bh.prepare(df, clogIndexStart, changelogRevisionIndex);
160 bh.annotateChange(fileRevIndex, changelogRevisionIndex, fileRevParents, fileClogParentRevs);
161 } catch (HgRuntimeException ex) {
162 throw new HgLibraryFailureException(ex);
163 }
164 }
165
166
167 private static int fileRevIndex(HgDataFile df, int csetRevIndex) throws HgRuntimeException {
168 Nodeid fileRev = df.getRepo().getManifest().getFileRevision(csetRevIndex, df.getPath());
169 return df.getRevisionIndex(fileRev);
170 }
171 }