comparison src/org/tmatesoft/hg/internal/FileRenameHistory.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
children 7efabe0cddcf
comparison
equal deleted inserted replaced
690:b286222158be 691:72fc7774b87e
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.internal;
18
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.LinkedList;
22 import java.util.List;
23
24 import org.tmatesoft.hg.core.HgFileRevision;
25 import org.tmatesoft.hg.core.HgIterateDirection;
26 import org.tmatesoft.hg.repo.HgDataFile;
27
28 /**
29 * Traces file renames. Quite similar to HgChangesetFileSneaker, although the latter tries different paths
30 * to find origin names, while this class traces first renames found only.
31 *
32 * @author Artem Tikhomirov
33 * @author TMate Software Ltd.
34 */
35 public final class FileRenameHistory {
36
37 private final int csetFrom;
38 private final int csetTo;
39 private final List<Chunk> history;
40
41 public FileRenameHistory(int csetStartIndex, int csetEndIndex) {
42 csetFrom = csetStartIndex;
43 csetTo = csetEndIndex;
44 history = new ArrayList<Chunk>(3);
45 }
46
47 public int startChangeset() {
48 return csetFrom;
49 }
50
51 public int endChangeset() {
52 return csetTo;
53 }
54
55 public boolean isOutOfRange(HgDataFile df, int fileRev) {
56 return df.getChangesetRevisionIndex(fileRev) < csetFrom || df.getChangesetRevisionIndex(0) > csetTo;
57 }
58
59 public void build(HgDataFile df, int fileRev) {
60 assert !isOutOfRange(df, fileRev);
61 LinkedList<Chunk> chunks = new LinkedList<Chunk>();
62 int chunkStart = 0, chunkEnd = fileRev;
63 int csetChunkEnd = -1, csetChunkStart = -1;
64 while (fileRev >= 0) {
65 int cset = df.getChangesetRevisionIndex(fileRev);
66 if (csetChunkEnd == -1) {
67 csetChunkEnd = cset;
68 }
69 if (cset <= csetFrom) {
70 chunkStart = fileRev;
71 csetChunkStart = csetFrom;
72 break;
73 }
74 if (cset > csetTo) {
75 chunkEnd = --fileRev;
76 csetChunkEnd = -1;
77 continue;
78 }
79 csetChunkStart = cset;
80 if (df.isCopy(fileRev)) {
81 chunks.addFirst(new Chunk(df, fileRev, chunkEnd, csetChunkStart, csetChunkEnd));
82 HgFileRevision origin = df.getCopySource(fileRev);
83 df = df.getRepo().getFileNode(origin.getPath());
84 fileRev = chunkEnd = df.getRevisionIndex(origin.getRevision());
85 chunkStart = 0;
86 csetChunkEnd = cset - 1; // if df is copy, cset can't be 0
87 csetChunkStart = -1;
88 } else {
89 fileRev--;
90 }
91 }
92 assert chunkStart >= 0;
93 assert chunkEnd >= 0; // can be negative only if df.cset(0) > csetTo
94 assert csetChunkEnd >= 0;
95 assert csetChunkStart >= 0;
96 chunks.addFirst(new Chunk(df, chunkStart, chunkEnd, csetChunkStart, csetChunkEnd));
97
98 history.clear();
99 history.addAll(chunks);
100 }
101
102 public Iterable<Chunk> iterate(HgIterateDirection order) {
103 if (order == HgIterateDirection.NewToOld) {
104 return ReverseIterator.reversed(history);
105 }
106 assert order == HgIterateDirection.OldToNew;
107 return Collections.unmodifiableList(history);
108 }
109
110 public int chunks() {
111 return history.size();
112 }
113
114 public Chunk chunkAt(int cset) {
115 if (cset < csetFrom || cset > csetTo) {
116 return null;
117 }
118 for (Chunk c : history) {
119 if (c.firstCset() > cset) {
120 break;
121 }
122 if (cset <= c.lastCset()) {
123 return c;
124 }
125 }
126 return null;
127 }
128
129
130 /**
131 * file has changes [firstFileRev..lastFileRev] that have occurred somewhere in [firstCset..lastCset]
132 */
133 public static class Chunk {
134 private final HgDataFile df;
135 private final int fileRevFrom;
136 private final int fileRevTo;
137 private final int csetFrom;
138 private final int csetTo;
139 Chunk(HgDataFile file, int fileRevStart, int fileRevEnd, int csetStart, int csetEnd) {
140 df = file;
141 fileRevFrom = fileRevStart;
142 fileRevTo = fileRevEnd;
143 csetFrom = csetStart;
144 csetTo = csetEnd;
145 }
146 public HgDataFile file() {
147 return df;
148 }
149 public int firstFileRev() {
150 return fileRevFrom;
151 }
152 public int lastFileRev() {
153 return fileRevTo;
154 }
155 public int firstCset() {
156 return csetFrom;
157 }
158 public int lastCset() {
159 return csetTo;
160 }
161 }
162 }