comparison src/org/tmatesoft/hg/internal/FileHistory.java @ 691:72fc7774b87e

Fix file.isCopy() for blame/annotate. Refactor status and blame to use newly introduced FileHistory helper that builds file rename history
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Fri, 02 Aug 2013 23:07:23 +0200
parents 6526d8adbc0f
children
comparison
equal deleted inserted replaced
690:b286222158be 691:72fc7774b87e
15 * contact TMate Software at support@hg4j.com 15 * contact TMate Software at support@hg4j.com
16 */ 16 */
17 package org.tmatesoft.hg.internal; 17 package org.tmatesoft.hg.internal;
18 18
19 import static org.tmatesoft.hg.core.HgIterateDirection.NewToOld; 19 import static org.tmatesoft.hg.core.HgIterateDirection.NewToOld;
20 import static org.tmatesoft.hg.core.HgIterateDirection.OldToNew;
20 21
21 import java.util.Collections; 22 import java.util.Collections;
22 import java.util.LinkedList; 23 import java.util.LinkedList;
23 24
24 import org.tmatesoft.hg.core.HgIterateDirection; 25 import org.tmatesoft.hg.core.HgIterateDirection;
25 import org.tmatesoft.hg.core.Nodeid; 26 import org.tmatesoft.hg.core.Nodeid;
27 import org.tmatesoft.hg.internal.FileRenameHistory.Chunk;
26 import org.tmatesoft.hg.repo.HgDataFile; 28 import org.tmatesoft.hg.repo.HgDataFile;
27 import org.tmatesoft.hg.repo.HgRepository;
28 import org.tmatesoft.hg.repo.HgRuntimeException; 29 import org.tmatesoft.hg.repo.HgRuntimeException;
29 30
30 /** 31 /**
31 * History of a file, with copy/renames, and corresponding revision information. 32 * History of a file, with copy/renames, and corresponding revision information.
32 * Facility for file history iteration. 33 * Facility for file history iteration.
56 public int getEndChangeset() { 57 public int getEndChangeset() {
57 return csetTo; 58 return csetTo;
58 } 59 }
59 60
60 public void build() throws HgRuntimeException { 61 public void build() throws HgRuntimeException {
61 assert fileCompleteHistory.isEmpty();
62 HgDataFile currentFile = df;
63 final int changelogRevIndexEnd = csetTo;
64 final int changelogRevIndexStart = csetFrom;
65 int fileLastClogRevIndex = changelogRevIndexEnd;
66 FileRevisionHistoryChunk nextChunk = null;
67 fileCompleteHistory.clear(); // just in case, #build() is not expected to be called more than once 62 fileCompleteHistory.clear(); // just in case, #build() is not expected to be called more than once
68 do { 63 Nodeid fileRev = df.getRepo().getManifest().getFileRevision(csetTo, df.getPath());
69 FileRevisionHistoryChunk fileHistory = new FileRevisionHistoryChunk(currentFile); 64 int fileRevIndex = df.getRevisionIndex(fileRev);
70 fileHistory.init(fileLastClogRevIndex); 65 FileRenameHistory frh = new FileRenameHistory(csetFrom, csetTo);
71 fileHistory.linkTo(nextChunk); 66 if (frh.isOutOfRange(df, fileRevIndex)) {
72 fileCompleteHistory.addFirst(fileHistory); // to get the list in old-to-new order 67 return;
73 nextChunk = fileHistory; 68 }
74 if (fileHistory.changeset(0) > changelogRevIndexStart && currentFile.isCopy()) { 69 frh.build(df, fileRevIndex);
75 // fileHistory.changeset(0) is the earliest revision we know about so far, 70 FileRevisionHistoryChunk prevChunk = null;
76 // once we get to revisions earlier than the requested start, stop digging. 71 for (Chunk c : frh.iterate(OldToNew)) {
77 // The reason there's NO == (i.e. not >=) because: 72 FileRevisionHistoryChunk fileHistory = new FileRevisionHistoryChunk(c.file(), c.firstCset(), c.lastCset(), c.firstFileRev(), c.lastFileRev());
78 // (easy): once it's equal, we've reached our intended start 73 fileHistory.init();
79 // (hard): if changelogRevIndexStart happens to be exact start of one of renames in the 74 if (fileHistory.revisionCount() == 0) {
80 // chain of renames (test-annotate2 repository, file1->file1a->file1b, i.e. points 75 // no revisions on our cset range of interest
81 // to the very start of file1a or file1 history), presence of == would get us to the next 76 continue;
82 // chunk and hence changed parents of present chunk's first element. Our annotate alg
83 // relies on parents only (i.e. knows nothing about 'last iteration element') to find out
84 // what to compare, and hence won't report all lines of 'last iteration element' (which is the
85 // first revision of the renamed file) as "added in this revision", leaving gaps in annotate
86 HgRepository repo = currentFile.getRepo();
87 Nodeid originLastRev = currentFile.getCopySourceRevision();
88 currentFile = repo.getFileNode(currentFile.getCopySourceName());
89 fileLastClogRevIndex = currentFile.getChangesetRevisionIndex(currentFile.getRevisionIndex(originLastRev));
90 // XXX perhaps, shall fail with meaningful exception if new file doesn't exist (.i/.d not found for whatever reason)
91 // or source revision is missing?
92 } else {
93 fileHistory.chopAtChangeset(changelogRevIndexStart);
94 currentFile = null; // stop iterating
95 } 77 }
96 } while (currentFile != null && fileLastClogRevIndex > changelogRevIndexStart); 78 if (prevChunk != null) {
79 prevChunk.linkTo(fileHistory);
80 }
81 fileCompleteHistory.addLast(fileHistory); // to get the list in old-to-new order
82 prevChunk = fileHistory;
83 }
97 // fileCompleteHistory is in (origin, intermediate target, ultimate target) order 84 // fileCompleteHistory is in (origin, intermediate target, ultimate target) order
98 } 85 }
99 86
100 public Iterable<FileRevisionHistoryChunk> iterate(HgIterateDirection order) { 87 public Iterable<FileRevisionHistoryChunk> iterate(HgIterateDirection order) {
101 if (order == NewToOld) { 88 if (order == NewToOld) {