Define a basic merge API, and a two-way tree merge strategy
[egit/graphgui.git] / org.spearce.jgit / src / org / spearce / jgit / merge / Merger.java
blob100cc38eb689d4d666d79159777ab8be18fd7a2e
1 /*
2 * Copyright (C) 2008, Google Inc.
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or
7 * without modification, are permitted provided that the following
8 * conditions are met:
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
18 * - Neither the name of the Git Development Community nor the
19 * names of its contributors may be used to endorse or promote
20 * products derived from this software without specific prior
21 * written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
24 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
25 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
28 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 package org.spearce.jgit.merge;
40 import java.io.IOException;
42 import org.spearce.jgit.errors.IncorrectObjectTypeException;
43 import org.spearce.jgit.lib.AnyObjectId;
44 import org.spearce.jgit.lib.Constants;
45 import org.spearce.jgit.lib.ObjectId;
46 import org.spearce.jgit.lib.ObjectWriter;
47 import org.spearce.jgit.lib.Repository;
48 import org.spearce.jgit.lib.WindowCursor;
49 import org.spearce.jgit.revwalk.RevCommit;
50 import org.spearce.jgit.revwalk.RevObject;
51 import org.spearce.jgit.revwalk.RevTree;
52 import org.spearce.jgit.revwalk.RevWalk;
53 import org.spearce.jgit.revwalk.filter.RevFilter;
54 import org.spearce.jgit.treewalk.AbstractTreeIterator;
55 import org.spearce.jgit.treewalk.CanonicalTreeParser;
56 import org.spearce.jgit.treewalk.EmptyTreeIterator;
58 /**
59 * Instance of a specific {@link MergeStrategy} for a single {@link Repository}.
61 public abstract class Merger {
62 /** The repository this merger operates on. */
63 protected final Repository db;
65 /** A RevWalk for computing merge bases, or listing incoming commits. */
66 protected final RevWalk walk;
68 private ObjectWriter writer;
70 /** The original objects supplied in the merge; this can be any tree-ish. */
71 protected RevObject[] sourceObjects;
73 /** If {@link #sourceObjects}[i] is a commit, this is the commit. */
74 protected RevCommit[] sourceCommits;
76 /** The trees matching every entry in {@link #sourceObjects}. */
77 protected RevTree[] sourceTrees;
79 /**
80 * Create a new merge instance for a repository.
82 * @param local
83 * the repository this merger will read and write data on.
85 protected Merger(final Repository local) {
86 db = local;
87 walk = new RevWalk(db);
90 /**
91 * @return the repository this merger operates on.
93 public Repository getRepository() {
94 return db;
97 /**
98 * @return an object writer to create objects in {@link #getRepository()}.
100 public ObjectWriter getObjectWriter() {
101 if (writer == null)
102 writer = new ObjectWriter(getRepository());
103 return writer;
107 * Merge together two or more tree-ish objects.
108 * <p>
109 * Any tree-ish may be supplied as inputs. Commits and/or tags pointing at
110 * trees or commits may be passed as input objects.
112 * @param tips
113 * source trees to be combined together. The merge base is not
114 * included in this set.
115 * @return true if the merge was completed without conflicts; false if the
116 * merge strategy cannot handle this merge or there were conflicts
117 * preventing it from automatically resolving all paths.
118 * @throws IncorrectObjectTypeException
119 * one of the input objects is not a commit, but the strategy
120 * requires it to be a commit.
121 * @throws IOException
122 * one or more sources could not be read, or outputs could not
123 * be written to the Repository.
125 public final boolean merge(final AnyObjectId[] tips) throws IOException {
126 sourceObjects = new RevObject[tips.length];
127 for (int i = 0; i < tips.length; i++)
128 sourceObjects[i] = walk.parseAny(tips[i]);
130 sourceCommits = new RevCommit[sourceObjects.length];
131 for (int i = 0; i < sourceObjects.length; i++) {
132 try {
133 sourceCommits[i] = walk.parseCommit(sourceObjects[i]);
134 } catch (IncorrectObjectTypeException err) {
135 sourceCommits[i] = null;
139 sourceTrees = new RevTree[sourceObjects.length];
140 for (int i = 0; i < sourceObjects.length; i++)
141 sourceTrees[i] = walk.parseTree(sourceObjects[i]);
143 return mergeImpl();
147 * Create an iterator to walk the merge base of two commits.
149 * @param aIdx
150 * index of the first commit in {@link #sourceObjects}.
151 * @param bIdx
152 * index of the second commit in {@link #sourceObjects}.
153 * @return the new iterator
154 * @throws IncorrectObjectTypeException
155 * one of the input objects is not a commit.
156 * @throws IOException
157 * objects are missing or multiple merge bases were found.
159 protected AbstractTreeIterator mergeBase(final int aIdx, final int bIdx)
160 throws IOException {
161 if (sourceCommits[aIdx] == null)
162 throw new IncorrectObjectTypeException(sourceObjects[aIdx],
163 Constants.TYPE_COMMIT);
164 if (sourceCommits[bIdx] == null)
165 throw new IncorrectObjectTypeException(sourceObjects[bIdx],
166 Constants.TYPE_COMMIT);
168 walk.reset();
169 walk.setRevFilter(RevFilter.MERGE_BASE);
170 walk.markStart(sourceCommits[aIdx]);
171 walk.markStart(sourceCommits[bIdx]);
172 final RevCommit base = walk.next();
173 if (base == null)
174 return new EmptyTreeIterator();
175 final RevCommit base2 = walk.next();
176 if (base2 != null) {
177 throw new IOException("Multiple merge bases for:" + "\n "
178 + sourceCommits[aIdx].name() + "\n "
179 + sourceCommits[bIdx].name() + "found:" + "\n "
180 + base.name() + "\n " + base2.name());
182 final WindowCursor curs = new WindowCursor();
183 try {
184 return new CanonicalTreeParser(null, db, base.getTree(), curs);
185 } finally {
186 curs.release();
191 * Execute the merge.
192 * <p>
193 * This method is called from {@link #merge(AnyObjectId[])} after the
194 * {@link #sourceObjects}, {@link #sourceCommits} and {@link #sourceTrees}
195 * have been populated.
197 * @return true if the merge was completed without conflicts; false if the
198 * merge strategy cannot handle this merge or there were conflicts
199 * preventing it from automatically resolving all paths.
200 * @throws IncorrectObjectTypeException
201 * one of the input objects is not a commit, but the strategy
202 * requires it to be a commit.
203 * @throws IOException
204 * one or more sources could not be read, or outputs could not
205 * be written to the Repository.
207 protected abstract boolean mergeImpl() throws IOException;
210 * @return resulting tree, if {@link #merge(AnyObjectId[])} returned true.
212 public abstract ObjectId getResultTreeId();