comparison src/org/tmatesoft/hg/repo/HgDataFile.java @ 401:a57a21aa4190

Support WORKING_COPY constant for dataFile.length() operation
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Mon, 27 Feb 2012 19:16:01 +0100
parents 0ae53c32ecef
children ee8264d80747
comparison
equal deleted inserted replaced
400:deb64baaa412 401:a57a21aa4190
111 * @return size of the file content at the revision identified by local revision number. 111 * @return size of the file content at the revision identified by local revision number.
112 * @throws HgInvalidRevisionException if supplied argument doesn't represent revision index in this revlog (<em>runtime exception</em>) 112 * @throws HgInvalidRevisionException if supplied argument doesn't represent revision index in this revlog (<em>runtime exception</em>)
113 * @throws HgInvalidControlFileException if access to revlog index/data entry failed 113 * @throws HgInvalidControlFileException if access to revlog index/data entry failed
114 */ 114 */
115 public int length(int fileRevisionIndex) throws HgInvalidControlFileException, HgInvalidRevisionException { 115 public int length(int fileRevisionIndex) throws HgInvalidControlFileException, HgInvalidRevisionException {
116 // FIXME support WORKING_COPY constant 116 if (wrongRevisionIndex(fileRevisionIndex) || fileRevisionIndex == BAD_REVISION) {
117 throw new HgInvalidRevisionException(fileRevisionIndex);
118 }
119 if (fileRevisionIndex == TIP) {
120 fileRevisionIndex = getLastRevision();
121 } else if (fileRevisionIndex == WORKING_COPY) {
122 File f = getRepo().getFile(this);
123 if (f.exists()) {
124 return (int) /*FIXME long!*/ f.length();
125 }
126 Nodeid fileRev = getWorkingCopyRevision();
127 if (fileRev == null) {
128 throw new HgInvalidRevisionException(String.format("File %s is not part of working copy", getPath()), null, fileRevisionIndex);
129 }
130 fileRevisionIndex = getRevisionIndex(fileRev);
131 }
117 if (metadata == null || !metadata.checked(fileRevisionIndex)) { 132 if (metadata == null || !metadata.checked(fileRevisionIndex)) {
118 checkAndRecordMetadata(fileRevisionIndex); 133 checkAndRecordMetadata(fileRevisionIndex);
119 } 134 }
120 final int dataLen = content.dataLength(fileRevisionIndex); 135 final int dataLen = content.dataLength(fileRevisionIndex);
121 if (metadata.known(fileRevisionIndex)) { 136 if (metadata.known(fileRevisionIndex)) {
125 } 140 }
126 141
127 /** 142 /**
128 * Reads content of the file from working directory. If file present in the working directory, its actual content without 143 * Reads content of the file from working directory. If file present in the working directory, its actual content without
129 * any filters is supplied through the sink. If file does not exist in the working dir, this method provides content of a file 144 * any filters is supplied through the sink. If file does not exist in the working dir, this method provides content of a file
130 * as if it would be refreshed in the working copy, i.e. its corresponding revision 145 * as if it would be refreshed in the working copy, i.e. its corresponding revision (according to dirstate) is read from the
131 * (XXX according to dirstate? file tip?) is read from the repository, and filters repo -> working copy get applied. 146 * repository, and filters repo -> working copy get applied.
147 *
148 * NOTE, if file is missing from the working directory and is not part of the dirstate (but otherwise legal repository file,
149 * e.g. from another branch), no content would be supplied.
132 * 150 *
133 * @param sink content consumer 151 * @param sink content consumer
134 * @throws HgInvalidControlFileException if access to revlog index/data entry failed 152 * @throws HgInvalidControlFileException if access to revlog index/data entry failed
135 * @throws HgInvalidFileException if access to file in working directory failed 153 * @throws HgInvalidFileException if access to file in working directory failed
136 * @throws CancelledException if execution of the operation was cancelled 154 * @throws CancelledException if execution of the operation was cancelled
165 getRepo().getContext().getLog().info(getClass(), ex, null); 183 getRepo().getContext().getLog().info(getClass(), ex, null);
166 } 184 }
167 } 185 }
168 } 186 }
169 } else { 187 } else {
170 final Pair<Nodeid, Nodeid> wcParents = getRepo().getWorkingCopyParents(); 188 Nodeid fileRev = getWorkingCopyRevision();
171 Nodeid p = wcParents.first().isNull() ? wcParents.second() : wcParents.first(); 189 if (fileRev == null) {
172 if (p.isNull()) { 190 // no content for this data file in the working copy - it is not part of the actual working state.
173 // no dirstate parents - no content 191 // XXX perhaps, shall report this to caller somehow, not silently pass no data?
174 // XXX what if it's repository with no dirstate? Shall I use TIP then?
175 return; 192 return;
176 } 193 }
177 final HgChangelog clog = getRepo().getChangelog(); 194 final int fileRevIndex = getRevisionIndex(fileRev);
195 contentWithFilters(fileRevIndex, sink);
196 }
197 }
198
199 /**
200 * @return file revision as recorded in repository manifest for dirstate parent, or <code>null</code> if no file revision can be found
201 */
202 private Nodeid getWorkingCopyRevision() throws HgInvalidControlFileException {
203 final Pair<Nodeid, Nodeid> wcParents = getRepo().getWorkingCopyParents();
204 Nodeid p = wcParents.first().isNull() ? wcParents.second() : wcParents.first();
205 final HgChangelog clog = getRepo().getChangelog();
206 final int csetRevIndex;
207 if (p.isNull()) {
208 // no dirstate parents
209 getRepo().getContext().getLog().info(getClass(), "No dirstate parents, resort to TIP", getPath());
210 // if it's a repository with no dirstate, use TIP then
211 csetRevIndex = clog.getLastRevision();
212 if (csetRevIndex == -1) {
213 // shall not happen provided there's .i for this data file (hence at least one cset)
214 // and perhaps exception is better here. However, null as "can't find" indication seems reasonable.
215 return null;
216 }
217 } else {
178 // common case to avoid searching complete changelog for nodeid match 218 // common case to avoid searching complete changelog for nodeid match
179 final Nodeid tipRev = clog.getRevision(TIP); 219 final Nodeid tipRev = clog.getRevision(TIP);
180 final int csetRevIndex;
181 if (tipRev.equals(p)) { 220 if (tipRev.equals(p)) {
182 csetRevIndex = clog.getLastRevision(); 221 csetRevIndex = clog.getLastRevision();
183 } else { 222 } else {
184 // bad luck, need to search honestly 223 // bad luck, need to search honestly
185 csetRevIndex = getRepo().getChangelog().getRevisionIndex(p); 224 csetRevIndex = getRepo().getChangelog().getRevisionIndex(p);
186 } 225 }
187 Nodeid fileRev = getRepo().getManifest().getFileRevision(csetRevIndex, getPath()); 226 }
188 final int fileRevIndex = getRevisionIndex(fileRev); 227 Nodeid fileRev = getRepo().getManifest().getFileRevision(csetRevIndex, getPath());
189 contentWithFilters(fileRevIndex, sink); 228 // it's possible for a file to be in working dir and have store/.i but to belong e.g. to a different
190 } 229 // branch than the one from dirstate. Thus it's possible to get null fileRev
230 // which would serve as an indication this data file is not part of working copy
231 return fileRev;
191 } 232 }
192 233
193 /** 234 /**
194 * Access content of a file revision 235 * Access content of a file revision
195 * XXX not sure distinct method contentWithFilters() is the best way to do, perhaps, callers shall add filters themselves? 236 * XXX not sure distinct method contentWithFilters() is the best way to do, perhaps, callers shall add filters themselves?
503 return sb.toString(); 544 return sb.toString();
504 } 545 }
505 546
506 private void checkAndRecordMetadata(int localRev) throws HgInvalidControlFileException { 547 private void checkAndRecordMetadata(int localRev) throws HgInvalidControlFileException {
507 // content() always initializes metadata. 548 // content() always initializes metadata.
508 // FIXME this is expensive way to find out metadata, distinct RevlogStream.Iterator would be better. 549 // TODO [post-1.0] this is expensive way to find out metadata, distinct RevlogStream.Iterator would be better.
509 // Alternatively, may parameterize MetadataContentPipe to do prepare only. 550 // Alternatively, may parameterize MetadataContentPipe to do prepare only.
510 // For reference, when throwing CancelledException, hg status -A --rev 3:80 takes 70 ms 551 // For reference, when throwing CancelledException, hg status -A --rev 3:80 takes 70 ms
511 // however, if we just consume buffer instead (buffer.position(buffer.limit()), same command takes ~320ms 552 // however, if we just consume buffer instead (buffer.position(buffer.limit()), same command takes ~320ms
512 // (compared to command-line counterpart of 190ms) 553 // (compared to command-line counterpart of 190ms)
513 try { 554 try {