comparison src/org/tmatesoft/hg/internal/CommitFacility.java @ 708:4ffc17c0b534

Merge: tests for resolver and complex scenario. Enable commit for merged revisions. Reuse file revisions if nothing changed
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Tue, 20 Aug 2013 18:41:34 +0200
parents b4242b7e7dfe
children
comparison
equal deleted inserted replaced
707:42b88709e41d 708:4ffc17c0b534
17 package org.tmatesoft.hg.internal; 17 package org.tmatesoft.hg.internal;
18 18
19 import static org.tmatesoft.hg.repo.HgRepository.DEFAULT_BRANCH_NAME; 19 import static org.tmatesoft.hg.repo.HgRepository.DEFAULT_BRANCH_NAME;
20 import static org.tmatesoft.hg.repo.HgRepository.NO_REVISION; 20 import static org.tmatesoft.hg.repo.HgRepository.NO_REVISION;
21 import static org.tmatesoft.hg.repo.HgRepositoryFiles.*; 21 import static org.tmatesoft.hg.repo.HgRepositoryFiles.*;
22 import static org.tmatesoft.hg.repo.HgRepositoryFiles.Branch;
23 import static org.tmatesoft.hg.repo.HgRepositoryFiles.UndoBranch;
24 import static org.tmatesoft.hg.util.LogFacility.Severity.Error; 22 import static org.tmatesoft.hg.util.LogFacility.Severity.Error;
25 23
26 import java.io.File; 24 import java.io.File;
27 import java.io.FileOutputStream; 25 import java.io.FileOutputStream;
28 import java.io.FileWriter; 26 import java.io.FileWriter;
29 import java.io.IOException; 27 import java.io.IOException;
28 import java.nio.ByteBuffer;
30 import java.util.ArrayList; 29 import java.util.ArrayList;
31 import java.util.HashMap; 30 import java.util.HashMap;
32 import java.util.LinkedHashMap; 31 import java.util.LinkedHashMap;
33 import java.util.Map; 32 import java.util.Map;
34 import java.util.Set; 33 import java.util.Set;
37 36
38 import org.tmatesoft.hg.core.HgCommitCommand; 37 import org.tmatesoft.hg.core.HgCommitCommand;
39 import org.tmatesoft.hg.core.HgIOException; 38 import org.tmatesoft.hg.core.HgIOException;
40 import org.tmatesoft.hg.core.HgRepositoryLockException; 39 import org.tmatesoft.hg.core.HgRepositoryLockException;
41 import org.tmatesoft.hg.core.Nodeid; 40 import org.tmatesoft.hg.core.Nodeid;
41 import org.tmatesoft.hg.internal.DataSerializer.ByteArraySerializer;
42 import org.tmatesoft.hg.internal.DataSerializer.DataSource; 42 import org.tmatesoft.hg.internal.DataSerializer.DataSource;
43 import org.tmatesoft.hg.repo.HgChangelog; 43 import org.tmatesoft.hg.repo.HgChangelog;
44 import org.tmatesoft.hg.repo.HgDataFile; 44 import org.tmatesoft.hg.repo.HgDataFile;
45 import org.tmatesoft.hg.repo.HgPhase; 45 import org.tmatesoft.hg.repo.HgPhase;
46 import org.tmatesoft.hg.repo.HgRuntimeException; 46 import org.tmatesoft.hg.repo.HgRuntimeException;
47 import org.tmatesoft.hg.util.ByteChannel;
48 import org.tmatesoft.hg.util.CancelledException;
47 import org.tmatesoft.hg.util.Pair; 49 import org.tmatesoft.hg.util.Pair;
48 import org.tmatesoft.hg.util.Path; 50 import org.tmatesoft.hg.util.Path;
49 51
50 /** 52 /**
51 * Name: CommitObject, FutureCommit or PendingCommit 53 * Name: CommitObject, FutureCommit or PendingCommit
147 for (Pair<HgDataFile, DataSource> e : files.values()) { 149 for (Pair<HgDataFile, DataSource> e : files.values()) {
148 HgDataFile df = e.first(); 150 HgDataFile df = e.first();
149 DataSource bds = e.second(); 151 DataSource bds = e.second();
150 Pair<Integer, Integer> fp = fileParents.get(df.getPath()); 152 Pair<Integer, Integer> fp = fileParents.get(df.getPath());
151 if (fp == null) { 153 if (fp == null) {
152 // NEW FILE 154 // NEW FILE, either just added or resurrected from p2
153 fp = new Pair<Integer, Integer>(NO_REVISION, NO_REVISION); 155 Nodeid fileRevInP2;
154 } 156 if ((fileRevInP2 = c2Manifest.nodeid(df.getPath())) != null) {
155 RevlogStream contentStream = repo.getImplAccess().getStream(df); 157 fp = new Pair<Integer, Integer>(df.getRevisionIndex(fileRevInP2), NO_REVISION);
158 } else {
159 // brand new
160 fp = new Pair<Integer, Integer>(NO_REVISION, NO_REVISION);
161 }
162 }
163 // TODO if fp.first() != NO_REVISION and fp.second() != NO_REVISION check if one
164 // revision is ancestor of another and use the latest as p1, then
165 Nodeid fileRev = null;
156 final boolean isNewFile = !df.exists(); 166 final boolean isNewFile = !df.exists();
157 RevlogStreamWriter fileWriter = new RevlogStreamWriter(repo, contentStream, transaction); 167 if (fp.first() != NO_REVISION && fp.second() == NO_REVISION && !isNewFile) {
158 Nodeid fileRev = fileWriter.addRevision(bds, clogRevisionIndex, fp.first(), fp.second()).second(); 168 // compare file contents to see if anything has changed, and reuse old revision, if unchanged.
169 // XXX ineffective, need better access to revision conten
170 ByteArraySerializer bas = new ByteArraySerializer();
171 bds.serialize(bas);
172 final byte[] newContent = bas.toByteArray();
173 // unless there's a way to reset DataSource, replace it with the content just read
174 bds = new DataSerializer.ByteArrayDataSource(newContent);
175 if (new ComparatorChannel(newContent).same(df, fp.first())) {
176 fileRev = df.getRevision(fp.first());
177 }
178 }
179 if (fileRev == null) {
180 RevlogStream contentStream = repo.getImplAccess().getStream(df);
181 RevlogStreamWriter fileWriter = new RevlogStreamWriter(repo, contentStream, transaction);
182 fileRev = fileWriter.addRevision(bds, clogRevisionIndex, fp.first(), fp.second()).second();
183 if (isNewFile) {
184 // registerNew shall go after fileWriter.addRevision as it needs to know if data is inlined or not
185 fncache.registerNew(df.getPath(), contentStream);
186 }
187 }
159 newManifestRevision.put(df.getPath(), fileRev); 188 newManifestRevision.put(df.getPath(), fileRev);
160 touchInDirstate.add(df.getPath()); 189 touchInDirstate.add(df.getPath());
161 if (isNewFile) {
162 // registerNew shall go after fileWriter.addRevision as it needs to know if data is inlined or not
163 fncache.registerNew(df.getPath(), contentStream);
164 }
165 } 190 }
166 // 191 //
167 final EncodingHelper encHelper = repo.buildFileNameEncodingHelper(); 192 final EncodingHelper encHelper = repo.buildFileNameEncodingHelper();
168 // 193 //
169 // Manifest 194 // Manifest
249 throw new HgIOException("Failed to save last commit message", ex, lastMessage); 274 throw new HgIOException("Failed to save last commit message", ex, lastMessage);
250 } finally { 275 } finally {
251 new FileUtils(repo.getLog(), this).closeQuietly(w, lastMessage); 276 new FileUtils(repo.getLog(), this).closeQuietly(w, lastMessage);
252 } 277 }
253 } 278 }
279
280 private static class ComparatorChannel implements ByteChannel {
281 private int index;
282 private final byte[] content;
283
284 public ComparatorChannel(byte[] contentToCompare) {
285 content = contentToCompare;
286 }
287
288 public int write(ByteBuffer buffer) throws IOException, CancelledException {
289 int consumed = 0;
290 while (buffer.hasRemaining()) {
291 byte b = buffer.get();
292 consumed++;
293 if (content[index++] != b) {
294 throw new CancelledException();
295 }
296 }
297 return consumed;
298 }
299
300 public boolean same(HgDataFile df, int fileRevIndex) {
301 index = 0;
302 try {
303 df.contentWithFilters(fileRevIndex, this);
304 return index == content.length;
305 } catch (CancelledException ex) {
306 // comparison failed, content differs, ok to go on
307 }
308 return false;
309 }
310 }
311
254 /* 312 /*
255 private Pair<Integer, Integer> getManifestParents() { 313 private Pair<Integer, Integer> getManifestParents() {
256 return new Pair<Integer, Integer>(extractManifestRevisionIndex(p1Commit), extractManifestRevisionIndex(p2Commit)); 314 return new Pair<Integer, Integer>(extractManifestRevisionIndex(p1Commit), extractManifestRevisionIndex(p2Commit));
257 } 315 }
258 316