Mercurial > jhg
comparison src/org/tmatesoft/hg/core/HgMergeCommand.java @ 705:b4242b7e7dfe
Merge command: implement conflict resolution alternatives
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
|---|---|
| date | Thu, 15 Aug 2013 18:43:50 +0200 |
| parents | 7743a9c10bfa |
| children | cd5c87d96315 |
comparison
equal
deleted
inserted
replaced
| 704:7743a9c10bfa | 705:b4242b7e7dfe |
|---|---|
| 16 */ | 16 */ |
| 17 package org.tmatesoft.hg.core; | 17 package org.tmatesoft.hg.core; |
| 18 | 18 |
| 19 import static org.tmatesoft.hg.repo.HgRepository.BAD_REVISION; | 19 import static org.tmatesoft.hg.repo.HgRepository.BAD_REVISION; |
| 20 | 20 |
| 21 import java.io.File; | |
| 22 import java.io.FileInputStream; | |
| 23 import java.io.IOException; | |
| 21 import java.io.InputStream; | 24 import java.io.InputStream; |
| 22 | 25 |
| 23 import org.tmatesoft.hg.internal.Callback; | 26 import org.tmatesoft.hg.internal.Callback; |
| 24 import org.tmatesoft.hg.internal.CsetParamKeeper; | 27 import org.tmatesoft.hg.internal.CsetParamKeeper; |
| 28 import org.tmatesoft.hg.internal.DirstateBuilder; | |
| 29 import org.tmatesoft.hg.internal.DirstateReader; | |
| 25 import org.tmatesoft.hg.internal.Experimental; | 30 import org.tmatesoft.hg.internal.Experimental; |
| 31 import org.tmatesoft.hg.internal.FileUtils; | |
| 32 import org.tmatesoft.hg.internal.Internals; | |
| 26 import org.tmatesoft.hg.internal.ManifestRevision; | 33 import org.tmatesoft.hg.internal.ManifestRevision; |
| 34 import org.tmatesoft.hg.internal.MergeStateBuilder; | |
| 27 import org.tmatesoft.hg.internal.Pool; | 35 import org.tmatesoft.hg.internal.Pool; |
| 36 import org.tmatesoft.hg.internal.Transaction; | |
| 37 import org.tmatesoft.hg.internal.WorkingDirFileWriter; | |
| 28 import org.tmatesoft.hg.repo.HgChangelog; | 38 import org.tmatesoft.hg.repo.HgChangelog; |
| 29 import org.tmatesoft.hg.repo.HgParentChildMap; | 39 import org.tmatesoft.hg.repo.HgParentChildMap; |
| 30 import org.tmatesoft.hg.repo.HgRepository; | 40 import org.tmatesoft.hg.repo.HgRepository; |
| 31 import org.tmatesoft.hg.repo.HgRepositoryLock; | 41 import org.tmatesoft.hg.repo.HgRepositoryLock; |
| 32 import org.tmatesoft.hg.repo.HgRevisionMap; | 42 import org.tmatesoft.hg.repo.HgRevisionMap; |
| 60 public HgMergeCommand changeset(int revisionIndex) throws HgBadArgumentException { | 70 public HgMergeCommand changeset(int revisionIndex) throws HgBadArgumentException { |
| 61 initHeadsAndAncestor(new CsetParamKeeper(repo).set(revisionIndex).get()); | 71 initHeadsAndAncestor(new CsetParamKeeper(repo).set(revisionIndex).get()); |
| 62 return this; | 72 return this; |
| 63 } | 73 } |
| 64 | 74 |
| 65 public void execute(Mediator mediator) throws HgCallbackTargetException, HgRepositoryLockException, HgLibraryFailureException, CancelledException { | 75 public void execute(Mediator mediator) throws HgCallbackTargetException, HgRepositoryLockException, HgIOException, HgLibraryFailureException, CancelledException { |
| 66 if (firstCset == BAD_REVISION || secondCset == BAD_REVISION || ancestorCset == BAD_REVISION) { | 76 if (firstCset == BAD_REVISION || secondCset == BAD_REVISION || ancestorCset == BAD_REVISION) { |
| 67 throw new IllegalArgumentException("Merge heads and their ancestors are not initialized"); | 77 throw new IllegalArgumentException("Merge heads and their ancestors are not initialized"); |
| 68 } | 78 } |
| 69 final HgRepositoryLock wdLock = repo.getWorkingDirLock(); | 79 final HgRepositoryLock wdLock = repo.getWorkingDirLock(); |
| 70 wdLock.acquire(); | 80 wdLock.acquire(); |
| 71 try { | 81 try { |
| 72 Pool<Nodeid> cacheRevs = new Pool<Nodeid>(); | 82 Pool<Nodeid> cacheRevs = new Pool<Nodeid>(); |
| 73 Pool<Path> cacheFiles = new Pool<Path>(); | 83 Pool<Path> cacheFiles = new Pool<Path>(); |
| 84 | |
| 85 Internals implRepo = Internals.getInstance(repo); | |
| 86 final DirstateBuilder dirstateBuilder = new DirstateBuilder(implRepo); | |
| 87 dirstateBuilder.fillFrom(new DirstateReader(implRepo, new Path.SimpleSource(repo.getSessionContext().getPathFactory(), cacheFiles))); | |
| 88 final HgChangelog clog = repo.getChangelog(); | |
| 89 dirstateBuilder.parents(clog.getRevision(firstCset), clog.getRevision(secondCset)); | |
| 90 // | |
| 91 MergeStateBuilder mergeStateBuilder = new MergeStateBuilder(implRepo); | |
| 92 | |
| 74 ManifestRevision m1, m2, ma; | 93 ManifestRevision m1, m2, ma; |
| 75 m1 = new ManifestRevision(cacheRevs, cacheFiles).init(repo, firstCset); | 94 m1 = new ManifestRevision(cacheRevs, cacheFiles).init(repo, firstCset); |
| 76 m2 = new ManifestRevision(cacheRevs, cacheFiles).init(repo, secondCset); | 95 m2 = new ManifestRevision(cacheRevs, cacheFiles).init(repo, secondCset); |
| 77 ma = new ManifestRevision(cacheRevs, cacheFiles).init(repo, ancestorCset); | 96 ma = new ManifestRevision(cacheRevs, cacheFiles).init(repo, ancestorCset); |
| 78 ResolverImpl resolver = new ResolverImpl(); | 97 Transaction transaction = implRepo.getTransactionFactory().create(repo); |
| 79 for (Path f : m1.files()) { | 98 ResolverImpl resolver = new ResolverImpl(implRepo, dirstateBuilder, mergeStateBuilder); |
| 80 Nodeid fileRevBase, fileRevA, fileRevB; | 99 try { |
| 81 if (m2.contains(f)) { | 100 for (Path f : m1.files()) { |
| 82 fileRevA = m1.nodeid(f); | 101 Nodeid fileRevBase, fileRevA, fileRevB; |
| 83 fileRevB = m2.nodeid(f); | 102 if (m2.contains(f)) { |
| 84 fileRevBase = ma.contains(f) ? ma.nodeid(f) : null; | 103 fileRevA = m1.nodeid(f); |
| 85 if (fileRevA.equals(fileRevB)) { | 104 fileRevB = m2.nodeid(f); |
| 86 HgFileRevision fr = new HgFileRevision(repo, fileRevA, m1.flags(f), f); | 105 fileRevBase = ma.contains(f) ? ma.nodeid(f) : null; |
| 87 mediator.same(fr, fr, resolver); | 106 if (fileRevA.equals(fileRevB)) { |
| 88 } else if (fileRevBase == fileRevA) { | 107 HgFileRevision fr = new HgFileRevision(repo, fileRevA, m1.flags(f), f); |
| 89 assert fileRevBase != null; | 108 resolver.presentState(f, fr, fr); |
| 90 HgFileRevision frBase = new HgFileRevision(repo, fileRevBase, ma.flags(f), f); | 109 mediator.same(fr, resolver); |
| 91 HgFileRevision frSecond= new HgFileRevision(repo, fileRevB, m2.flags(f), f); | 110 } else if (fileRevBase == fileRevA) { |
| 92 mediator.fastForwardB(frBase, frSecond, resolver); | 111 assert fileRevBase != null; |
| 93 } else if (fileRevBase == fileRevB) { | 112 HgFileRevision frBase = new HgFileRevision(repo, fileRevBase, ma.flags(f), f); |
| 94 assert fileRevBase != null; | 113 HgFileRevision frSecond= new HgFileRevision(repo, fileRevB, m2.flags(f), f); |
| 95 HgFileRevision frBase = new HgFileRevision(repo, fileRevBase, ma.flags(f), f); | 114 resolver.presentState(f, frBase, frSecond); |
| 96 HgFileRevision frFirst = new HgFileRevision(repo, fileRevA, m1.flags(f), f); | 115 mediator.fastForwardB(frBase, frSecond, resolver); |
| 97 mediator.fastForwardA(frBase, frFirst, resolver); | 116 } else if (fileRevBase == fileRevB) { |
| 117 assert fileRevBase != null; | |
| 118 HgFileRevision frBase = new HgFileRevision(repo, fileRevBase, ma.flags(f), f); | |
| 119 HgFileRevision frFirst = new HgFileRevision(repo, fileRevA, m1.flags(f), f); | |
| 120 resolver.presentState(f, frFirst, frBase); | |
| 121 mediator.fastForwardA(frBase, frFirst, resolver); | |
| 122 } else { | |
| 123 HgFileRevision frBase = fileRevBase == null ? null : new HgFileRevision(repo, fileRevBase, ma.flags(f), f); | |
| 124 HgFileRevision frFirst = new HgFileRevision(repo, fileRevA, m1.flags(f), f); | |
| 125 HgFileRevision frSecond= new HgFileRevision(repo, fileRevB, m2.flags(f), f); | |
| 126 resolver.presentState(f, frFirst, frSecond); | |
| 127 mediator.resolve(frBase, frFirst, frSecond, resolver); | |
| 128 } | |
| 98 } else { | 129 } else { |
| 99 HgFileRevision frBase = fileRevBase == null ? null : new HgFileRevision(repo, fileRevBase, ma.flags(f), f); | 130 // m2 doesn't contain the file, either new in m1, or deleted in m2 |
| 100 HgFileRevision frFirst = new HgFileRevision(repo, fileRevA, m1.flags(f), f); | 131 HgFileRevision frFirst = new HgFileRevision(repo, m1.nodeid(f), m1.flags(f), f); |
| 101 HgFileRevision frSecond= new HgFileRevision(repo, fileRevB, m2.flags(f), f); | 132 resolver.presentState(f, frFirst, null); |
| 102 mediator.resolve(frBase, frFirst, frSecond, resolver); | 133 if (ma.contains(f)) { |
| 134 // deleted in m2 | |
| 135 HgFileRevision frBase = new HgFileRevision(repo, ma.nodeid(f), ma.flags(f), f); | |
| 136 mediator.onlyA(frBase, frFirst, resolver); | |
| 137 } else { | |
| 138 // new in m1 | |
| 139 mediator.newInA(frFirst, resolver); | |
| 140 } | |
| 103 } | 141 } |
| 104 } else { | 142 resolver.apply(); |
| 105 // m2 doesn't contain the file, either new in m1, or deleted in m2 | 143 } // for m1 files |
| 106 HgFileRevision frFirst = new HgFileRevision(repo, m1.nodeid(f), m1.flags(f), f); | 144 for (Path f : m2.files()) { |
| 145 if (m1.contains(f)) { | |
| 146 continue; | |
| 147 } | |
| 148 HgFileRevision frSecond= new HgFileRevision(repo, m2.nodeid(f), m2.flags(f), f); | |
| 149 // file in m2 is either new or deleted in m1 | |
| 150 resolver.presentState(f, null, frSecond); | |
| 107 if (ma.contains(f)) { | 151 if (ma.contains(f)) { |
| 108 // deleted in m2 | 152 // deleted in m1 |
| 109 HgFileRevision frBase = new HgFileRevision(repo, ma.nodeid(f), ma.flags(f), f); | 153 HgFileRevision frBase = new HgFileRevision(repo, ma.nodeid(f), ma.flags(f), f); |
| 110 mediator.onlyA(frBase, frFirst, resolver); | 154 mediator.onlyB(frBase, frSecond, resolver); |
| 111 } else { | 155 } else { |
| 112 // new in m1 | 156 // new in m2 |
| 113 mediator.newInA(frFirst, resolver); | 157 mediator.newInB(frSecond, resolver); |
| 114 } | 158 } |
| 159 resolver.apply(); | |
| 115 } | 160 } |
| 116 resolver.apply(); | 161 resolver.serializeChanged(transaction); |
| 117 } // for m1 files | 162 transaction.commit(); |
| 118 for (Path f : m2.files()) { | 163 } catch (HgRuntimeException ex) { |
| 119 if (m1.contains(f)) { | 164 transaction.rollback(); |
| 120 continue; | 165 throw ex; |
| 121 } | 166 } catch (HgIOException ex) { |
| 122 HgFileRevision frSecond= new HgFileRevision(repo, m2.nodeid(f), m2.flags(f), f); | 167 transaction.rollback(); |
| 123 // file in m2 is either new or deleted in m1 | 168 throw ex; |
| 124 if (ma.contains(f)) { | |
| 125 // deleted in m1 | |
| 126 HgFileRevision frBase = new HgFileRevision(repo, ma.nodeid(f), ma.flags(f), f); | |
| 127 mediator.onlyB(frBase, frSecond, resolver); | |
| 128 } else { | |
| 129 // new in m2 | |
| 130 mediator.newInB(frSecond, resolver); | |
| 131 } | |
| 132 resolver.apply(); | |
| 133 } | 169 } |
| 134 } catch (HgRuntimeException ex) { | 170 } catch (HgRuntimeException ex) { |
| 135 throw new HgLibraryFailureException(ex); | 171 throw new HgLibraryFailureException(ex); |
| 136 } finally { | 172 } finally { |
| 137 wdLock.release(); | 173 wdLock.release(); |
| 158 secondCset = csetIndexB; | 194 secondCset = csetIndexB; |
| 159 ancestorCset = rmap.revisionIndex(ancestor); | 195 ancestorCset = rmap.revisionIndex(ancestor); |
| 160 } | 196 } |
| 161 | 197 |
| 162 /** | 198 /** |
| 163 * This is the way client code takes part in the merge process | 199 * This is the way client code takes part in the merge process. |
| 200 * It's advised to subclass {@link MediatorBase} unless special treatment for regular cases is desired | |
| 164 */ | 201 */ |
| 165 @Experimental(reason="Provisional API. Work in progress") | 202 @Experimental(reason="Provisional API. Work in progress") |
| 166 @Callback | 203 @Callback |
| 167 public interface Mediator { | 204 public interface Mediator { |
| 168 public void same(HgFileRevision first, HgFileRevision second, Resolver resolver) throws HgCallbackTargetException; | 205 /** |
| 206 * file revisions are identical in both heads | |
| 207 */ | |
| 208 public void same(HgFileRevision rev, Resolver resolver) throws HgCallbackTargetException; | |
| 209 /** | |
| 210 * file left in first/left/A trunk only, deleted in second/right/B trunk | |
| 211 */ | |
| 169 public void onlyA(HgFileRevision base, HgFileRevision rev, Resolver resolver) throws HgCallbackTargetException; | 212 public void onlyA(HgFileRevision base, HgFileRevision rev, Resolver resolver) throws HgCallbackTargetException; |
| 213 /** | |
| 214 * file left in second/right/B trunk only, deleted in first/left/A trunk | |
| 215 */ | |
| 170 public void onlyB(HgFileRevision base, HgFileRevision rev, Resolver resolver) throws HgCallbackTargetException; | 216 public void onlyB(HgFileRevision base, HgFileRevision rev, Resolver resolver) throws HgCallbackTargetException; |
| 217 /** | |
| 218 * file is missing in ancestor revision and second/right/B trunk, introduced in first/left/A trunk | |
| 219 */ | |
| 171 public void newInA(HgFileRevision rev, Resolver resolver) throws HgCallbackTargetException; | 220 public void newInA(HgFileRevision rev, Resolver resolver) throws HgCallbackTargetException; |
| 221 /** | |
| 222 * file is missing in ancestor revision and first/left/A trunk, introduced in second/right/B trunk | |
| 223 */ | |
| 172 public void newInB(HgFileRevision rev, Resolver resolver) throws HgCallbackTargetException; | 224 public void newInB(HgFileRevision rev, Resolver resolver) throws HgCallbackTargetException; |
| 225 /** | |
| 226 * file was changed in first/left/A trunk, unchanged in second/right/B trunk | |
| 227 */ | |
| 173 public void fastForwardA(HgFileRevision base, HgFileRevision first, Resolver resolver) throws HgCallbackTargetException; | 228 public void fastForwardA(HgFileRevision base, HgFileRevision first, Resolver resolver) throws HgCallbackTargetException; |
| 229 /** | |
| 230 * file was changed in second/right/B trunk, unchanged in first/left/A trunk | |
| 231 */ | |
| 174 public void fastForwardB(HgFileRevision base, HgFileRevision second, Resolver resolver) throws HgCallbackTargetException; | 232 public void fastForwardB(HgFileRevision base, HgFileRevision second, Resolver resolver) throws HgCallbackTargetException; |
| 233 /** | |
| 234 * File changed (or added, if base is <code>null</code>) in both trunks | |
| 235 */ | |
| 175 public void resolve(HgFileRevision base, HgFileRevision first, HgFileRevision second, Resolver resolver) throws HgCallbackTargetException; | 236 public void resolve(HgFileRevision base, HgFileRevision first, HgFileRevision second, Resolver resolver) throws HgCallbackTargetException; |
| 176 } | 237 } |
| 177 | 238 |
| 178 /** | 239 /** |
| 179 * Clients shall not implement this interface. | 240 * Clients shall not implement this interface. |
| 180 * They use this API from inside {@link Mediator#resolve(HgFileRevision, HgFileRevision, HgFileRevision, Resolver)} | 241 * They use this API from inside {@link Mediator#resolve(HgFileRevision, HgFileRevision, HgFileRevision, Resolver)} |
| 181 */ | 242 */ |
| 182 @Experimental(reason="Provisional API. Work in progress") | 243 @Experimental(reason="Provisional API. Work in progress") |
| 183 public interface Resolver { | 244 public interface Resolver { |
| 184 public void use(HgFileRevision rev); | 245 public void use(HgFileRevision rev); |
| 185 public void use(InputStream content); | 246 /** |
| 247 * Replace current revision with stream content. | |
| 248 * Note, callers are not expected to {@link InputStream#close()} this stream. | |
| 249 * It will be {@link InputStream#close() closed} at <b>Hg4J</b>'s discretion | |
| 250 * not necessarily during invocation of this method. IOW, the library may decide to | |
| 251 * use this stream not right away, at some point of time later, and streams supplied | |
| 252 * shall respect this. | |
| 253 * | |
| 254 * @param content New content to replace current revision, shall not be <code>null</code> | |
| 255 * @throws IOException propagated exceptions from content | |
| 256 */ | |
| 257 public void use(InputStream content) throws IOException; | |
| 258 public void forget(HgFileRevision rev); | |
| 186 public void unresolved(); // record the file for later processing by 'hg resolve' | 259 public void unresolved(); // record the file for later processing by 'hg resolve' |
| 187 } | 260 } |
| 188 | 261 |
| 262 /** | |
| 263 * Base mediator implementation, with regular resolution | |
| 264 */ | |
| 265 @Experimental(reason="Provisional API. Work in progress") | |
| 266 public abstract class MediatorBase implements Mediator { | |
| 267 public void same(HgFileRevision rev, Resolver resolver) throws HgCallbackTargetException { | |
| 268 resolver.use(rev); | |
| 269 } | |
| 270 public void onlyA(HgFileRevision base, HgFileRevision rev, Resolver resolver) throws HgCallbackTargetException { | |
| 271 resolver.use(rev); | |
| 272 } | |
| 273 public void onlyB(HgFileRevision base, HgFileRevision rev, Resolver resolver) throws HgCallbackTargetException { | |
| 274 resolver.use(rev); | |
| 275 } | |
| 276 public void newInA(HgFileRevision rev, Resolver resolver) throws HgCallbackTargetException { | |
| 277 resolver.use(rev); | |
| 278 } | |
| 279 public void newInB(HgFileRevision rev, Resolver resolver) throws HgCallbackTargetException { | |
| 280 resolver.use(rev); | |
| 281 } | |
| 282 public void fastForwardA(HgFileRevision base, HgFileRevision first, Resolver resolver) throws HgCallbackTargetException { | |
| 283 resolver.use(first); | |
| 284 } | |
| 285 public void fastForwardB(HgFileRevision base, HgFileRevision second, Resolver resolver) throws HgCallbackTargetException { | |
| 286 resolver.use(second); | |
| 287 } | |
| 288 } | |
| 289 | |
| 189 private static class ResolverImpl implements Resolver { | 290 private static class ResolverImpl implements Resolver { |
| 190 void apply() { | 291 |
| 292 private final Internals repo; | |
| 293 private final DirstateBuilder dirstateBuilder; | |
| 294 private final MergeStateBuilder mergeStateBuilder; | |
| 295 private boolean changedDirstate; | |
| 296 private HgFileRevision revA; | |
| 297 private HgFileRevision revB; | |
| 298 private Path file; | |
| 299 // resolutions: | |
| 300 private HgFileRevision resolveUse, resolveForget; | |
| 301 private File resolveContent; | |
| 302 private boolean resolveMarkUnresolved; | |
| 303 | |
| 304 public ResolverImpl(Internals implRepo, DirstateBuilder dirstateBuilder, MergeStateBuilder mergeStateBuilder) { | |
| 305 repo = implRepo; | |
| 306 this.dirstateBuilder = dirstateBuilder; | |
| 307 this.mergeStateBuilder = mergeStateBuilder; | |
| 308 changedDirstate = false; | |
| 309 } | |
| 310 | |
| 311 void serializeChanged(Transaction tr) throws HgIOException { | |
| 312 if (changedDirstate) { | |
| 313 dirstateBuilder.serialize(tr); | |
| 314 } | |
| 315 mergeStateBuilder.serialize(tr); | |
| 316 } | |
| 317 | |
| 318 void presentState(Path p, HgFileRevision revA, HgFileRevision revB) { | |
| 319 assert revA != null || revB != null; | |
| 320 file = p; | |
| 321 this.revA = revA; | |
| 322 this.revB = revB; | |
| 323 resolveUse = resolveForget = null; | |
| 324 resolveContent = null; | |
| 325 resolveMarkUnresolved = false; | |
| 326 } | |
| 327 | |
| 328 void apply() throws HgIOException, HgRuntimeException { | |
| 329 if (resolveMarkUnresolved) { | |
| 330 mergeStateBuilder.unresolved(file); | |
| 331 } else if (resolveForget != null) { | |
| 332 if (resolveForget == revA) { | |
| 333 changedDirstate = true; | |
| 334 dirstateBuilder.recordRemoved(file); | |
| 335 } | |
| 336 } else if (resolveUse != null) { | |
| 337 if (resolveUse != revA) { | |
| 338 changedDirstate = true; | |
| 339 final WorkingDirFileWriter fw = new WorkingDirFileWriter(repo); | |
| 340 fw.processFile(resolveUse); | |
| 341 if (resolveUse == revB) { | |
| 342 dirstateBuilder.recordMergedFromP2(file); | |
| 343 } else { | |
| 344 dirstateBuilder.recordMerged(file, fw.fmode(), fw.mtime(), fw.bytesWritten()); | |
| 345 } | |
| 346 } // if resolution is to use revA, nothing to do | |
| 347 } else if (resolveContent != null) { | |
| 348 changedDirstate = true; | |
| 349 // FIXME write content to file using transaction? | |
| 350 InputStream is; | |
| 351 try { | |
| 352 is = new FileInputStream(resolveContent); | |
| 353 } catch (IOException ex) { | |
| 354 throw new HgIOException("Failed to read temporary content", ex, resolveContent); | |
| 355 } | |
| 356 final WorkingDirFileWriter fw = new WorkingDirFileWriter(repo); | |
| 357 fw.processFile(file, is, revA == null ? revB.getFileFlags() : revA.getFileFlags()); | |
| 358 // XXX if presentState(null, fileOnlyInB), and use(InputStream) - i.e. | |
| 359 // resolution is to add file with supplied content - shall I put 'Merged', MergedFromP2 or 'Added' into dirstate? | |
| 360 if (revA == null && revB != null) { | |
| 361 dirstateBuilder.recordMergedFromP2(file); | |
| 362 } else { | |
| 363 dirstateBuilder.recordMerged(file, fw.fmode(), fw.mtime(), fw.bytesWritten()); | |
| 364 } | |
| 365 } else { | |
| 366 assert false; | |
| 367 } | |
| 191 } | 368 } |
| 192 | 369 |
| 193 public void use(HgFileRevision rev) { | 370 public void use(HgFileRevision rev) { |
| 194 // TODO Auto-generated method stub | 371 if (rev == null) { |
| 195 } | 372 throw new IllegalArgumentException(); |
| 196 | 373 } |
| 197 public void use(InputStream content) { | 374 assert resolveContent == null; |
| 198 // TODO Auto-generated method stub | 375 assert resolveForget == null; |
| 376 resolveUse = rev; | |
| 377 } | |
| 378 | |
| 379 public void use(InputStream content) throws IOException { | |
| 380 if (content == null) { | |
| 381 throw new IllegalArgumentException(); | |
| 382 } | |
| 383 assert resolveUse == null; | |
| 384 assert resolveForget == null; | |
| 385 try { | |
| 386 // cache new contents just to fail fast if there are troubles with content | |
| 387 final FileUtils fileUtils = new FileUtils(repo.getLog(), this); | |
| 388 resolveContent = fileUtils.createTempFile(); | |
| 389 fileUtils.write(content, resolveContent); | |
| 390 } finally { | |
| 391 content.close(); | |
| 392 } | |
| 393 // do not care deleting file in case of failure to allow analyze of the issue | |
| 394 } | |
| 395 | |
| 396 public void forget(HgFileRevision rev) { | |
| 397 if (rev == null) { | |
| 398 throw new IllegalArgumentException(); | |
| 399 } | |
| 400 if (rev != revA || rev != revB) { | |
| 401 throw new IllegalArgumentException("Can't forget revision which doesn't represent actual state in either merged trunk"); | |
| 402 } | |
| 403 assert resolveUse == null; | |
| 404 assert resolveContent == null; | |
| 405 resolveForget = rev; | |
| 199 } | 406 } |
| 200 | 407 |
| 201 public void unresolved() { | 408 public void unresolved() { |
| 202 // TODO Auto-generated method stub | 409 resolveMarkUnresolved = true; |
| 203 } | 410 } |
| 204 } | 411 } |
| 205 } | 412 } |
