comparison src/org/tmatesoft/hg/core/HgChangesetFileSneaker.java @ 690:b286222158be

Fix file.isCopy() use for status and cat commands
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Thu, 01 Aug 2013 21:45:47 +0200
parents 3ca4ae7bdd38
children 72fc7774b87e
comparison
equal deleted inserted replaced
689:5050ee565bd1 690:b286222158be
1 /* 1 /*
2 * Copyright (c) 2011-2012 TMate Software Ltd 2 * Copyright (c) 2011-2013 TMate Software Ltd
3 * 3 *
4 * This program is free software; you can redistribute it and/or modify 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 5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License. 6 * the Free Software Foundation; version 2 of the License.
7 * 7 *
14 * the terms of a license other than GNU General Public License 14 * the terms of a license other than GNU General Public License
15 * contact TMate Software at support@hg4j.com 15 * contact TMate Software at support@hg4j.com
16 */ 16 */
17 package org.tmatesoft.hg.core; 17 package org.tmatesoft.hg.core;
18 18
19 import java.util.ArrayDeque;
20
19 import org.tmatesoft.hg.internal.ManifestRevision; 21 import org.tmatesoft.hg.internal.ManifestRevision;
20 import org.tmatesoft.hg.repo.HgDataFile; 22 import org.tmatesoft.hg.repo.HgDataFile;
21 import org.tmatesoft.hg.repo.HgInvalidStateException; 23 import org.tmatesoft.hg.repo.HgInvalidStateException;
22 import org.tmatesoft.hg.repo.HgManifest; 24 import org.tmatesoft.hg.repo.HgManifest;
25 import org.tmatesoft.hg.repo.HgManifest.Flags;
23 import org.tmatesoft.hg.repo.HgRepository; 26 import org.tmatesoft.hg.repo.HgRepository;
24 import org.tmatesoft.hg.repo.HgRuntimeException; 27 import org.tmatesoft.hg.repo.HgRuntimeException;
28 import org.tmatesoft.hg.util.Outcome;
29 import org.tmatesoft.hg.util.Pair;
25 import org.tmatesoft.hg.util.Path; 30 import org.tmatesoft.hg.util.Path;
26 import org.tmatesoft.hg.util.Outcome;
27 31
28 /** 32 /**
29 * Primary purpose is to provide information about file revisions at specific changeset. Multiple {@link #check(Path)} calls 33 * Primary purpose is to provide information about file revisions at specific changeset. Multiple {@link #check(Path)} calls
30 * are possible once {@link #changeset(Nodeid)} (and optionally, {@link #followRenames(boolean)}) were set. 34 * are possible once {@link #changeset(Nodeid)} (and optionally, {@link #followRenames(boolean)}) were set.
31 * 35 *
129 if (!dataFile.exists()) { 133 if (!dataFile.exists()) {
130 checkResult = new Outcome(Outcome.Kind.Success, String.format("File named %s is not known in the repository", file)); 134 checkResult = new Outcome(Outcome.Kind.Success, String.format("File named %s is not known in the repository", file));
131 return checkResult; 135 return checkResult;
132 } 136 }
133 Nodeid toExtract = null; 137 Nodeid toExtract = null;
134 HgManifest.Flags extractRevFlags = null;
135 String phaseMsg = "Extract manifest revision failed"; 138 String phaseMsg = "Extract manifest revision failed";
136 try { 139 try {
137 if (cachedManifest == null) { 140 if (cachedManifest == null) {
138 int csetRev = repo.getChangelog().getRevisionIndex(cset); 141 int csetRev = repo.getChangelog().getRevisionIndex(cset);
139 cachedManifest = new ManifestRevision(null, null); // XXX how about context and cached manifest revisions 142 cachedManifest = new ManifestRevision(null, null); // XXX how about context and cached manifest revisions
140 repo.getManifest().walk(csetRev, csetRev, cachedManifest); 143 repo.getManifest().walk(csetRev, csetRev, cachedManifest);
141 // cachedManifest shall be meaningful - changelog.getRevisionIndex() above ensures we've got version that exists. 144 // cachedManifest shall be meaningful - changelog.getRevisionIndex() above ensures we've got version that exists.
142 } 145 }
143 toExtract = cachedManifest.nodeid(file); 146 toExtract = cachedManifest.nodeid(file);
144 extractRevFlags = cachedManifest.flags(file);
145 phaseMsg = "Follow copy/rename failed"; 147 phaseMsg = "Follow copy/rename failed";
146 if (toExtract == null && followRenames) { 148 if (toExtract == null && followRenames) {
147 while (toExtract == null && dataFile.isCopy()) { 149 int csetIndex = repo.getChangelog().getRevisionIndex(cset);
148 renamed = true; 150 int ccFileRevIndex = dataFile.getLastRevision(); // copy candidate
149 file = dataFile.getCopySourceName(); 151 int csetFileEnds = dataFile.getChangesetRevisionIndex(ccFileRevIndex);
150 dataFile = repo.getFileNode(file); 152 if (csetIndex > csetFileEnds) {
151 toExtract = cachedManifest.nodeid(file); 153 return new Outcome(Outcome.Kind.Success, String.format("%s: last known changeset for the file %s is %d. Follow renames is possible towards older changesets only", phaseMsg, file, csetFileEnds));
152 extractRevFlags = cachedManifest.flags(file);
153 } 154 }
155 // XXX code is similar to that in HgStatusCollector#getOriginIfCopy. Why it's different in lastOrigin processing then?
156 // traceback stack keeps record of all files with isCopy(fileRev) == true we've tried to follow, so that we can try earlier file
157 // revisions in case followed fileRev didn't succeed
158 ArrayDeque<Pair<HgDataFile, Integer>> traceback = new ArrayDeque<Pair<HgDataFile, Integer>>();
159 do {
160 int ccCsetIndex = dataFile.getChangesetRevisionIndex(ccFileRevIndex);
161 if (ccCsetIndex <= csetIndex) {
162 // present dataFile is our (distant) origin
163 toExtract = dataFile.getRevision(ccFileRevIndex);
164 renamed = true;
165 break;
166 }
167 if (!dataFile.isCopy(ccFileRevIndex)) {
168 // nothing left to return to when traceback.isEmpty()
169 while (ccFileRevIndex == 0 && !traceback.isEmpty()) {
170 Pair<HgDataFile, Integer> lastTurnPoint = traceback.pop();
171 dataFile = lastTurnPoint.first();
172 ccFileRevIndex = lastTurnPoint.second(); // generally ccFileRevIndex != 0 here, but doesn't hurt to check, hence while
173 // fall through to shift down from the file revision we've already looked at
174 }
175 ccFileRevIndex--;
176 continue;
177 }
178 if (ccFileRevIndex > 0) {
179 // there's no reason to memorize turn point if it's the very first revision
180 // of the file and we won't be able to try any other earlier revision
181 traceback.push(new Pair<HgDataFile, Integer>(dataFile, ccFileRevIndex));
182 }
183 HgFileRevision origin = dataFile.getCopySource(ccFileRevIndex);
184 dataFile = repo.getFileNode(origin.getPath());
185 ccFileRevIndex = dataFile.getRevisionIndex(origin.getRevision());
186 } while (ccFileRevIndex >= 0);
187 // didn't get to csetIndex, no ancestor in file rename history found.
154 } 188 }
155 } catch (HgRuntimeException ex) { 189 } catch (HgRuntimeException ex) {
156 checkResult = new Outcome(Outcome.Kind.Failure, phaseMsg, ex); 190 checkResult = new Outcome(Outcome.Kind.Failure, phaseMsg, ex);
157 return checkResult; 191 return checkResult;
158 } 192 }
159 if (toExtract != null) { 193 if (toExtract != null) {
194 Flags extractRevFlags = cachedManifest.flags(dataFile.getPath());
160 fileRevision = new HgFileRevision(repo, toExtract, extractRevFlags, dataFile.getPath()); 195 fileRevision = new HgFileRevision(repo, toExtract, extractRevFlags, dataFile.getPath());
161 checkResult = new Outcome(Outcome.Kind.Success, String.format("File %s, revision %s found at changeset %s", dataFile.getPath(), toExtract.shortNotation(), cset.shortNotation())); 196 checkResult = new Outcome(Outcome.Kind.Success, String.format("File %s, revision %s found at changeset %s", dataFile.getPath(), toExtract.shortNotation(), cset.shortNotation()));
162 return checkResult; 197 return checkResult;
163 } 198 }
164 checkResult = new Outcome(Outcome.Kind.Success, String.format("File %s nor its origins were known at repository %s revision", file, cset.shortNotation())); 199 checkResult = new Outcome(Outcome.Kind.Success, String.format("File %s nor its origins were known at revision %s", file, cset.shortNotation()));
165 return checkResult; 200 return checkResult;
166 } 201 }
167 202
168 /** 203 /**
169 * Re-get latest check status object 204 * Re-get latest check status object