aboutsummaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategySimpleTwoWayInCore.java
blob: c3180abd3531abd50324cee67ab9ba29636234dd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/*
 * Copyright (C) 2008-2009, Google Inc. and others
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Distribution License v. 1.0 which is available at
 * https://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

package org.eclipse.jgit.merge;

import java.io.IOException;

import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.errors.UnmergedPathException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.NameConflictTreeWalk;

/**
 * Merges two commits together in-memory, ignoring any working directory.
 * <p>
 * The strategy chooses a path from one of the two input trees if the path is
 * unchanged in the other relative to their common merge base tree. This is a
 * trivial 3-way merge (at the file path level only).
 * <p>
 * Modifications of the same file path (content and/or file mode) by both input
 * trees will cause a merge conflict, as this strategy does not attempt to merge
 * file contents.
 */
public class StrategySimpleTwoWayInCore extends ThreeWayMergeStrategy {
	/**
	 * Create a new instance of the strategy.
	 */
	protected StrategySimpleTwoWayInCore() {
		//
	}

	@Override
	public String getName() {
		return "simple-two-way-in-core"; //$NON-NLS-1$
	}

	@Override
	public ThreeWayMerger newMerger(Repository db) {
		return new InCoreMerger(db);
	}

	@Override
	public ThreeWayMerger newMerger(Repository db, boolean inCore) {
		// This class is always inCore, so ignore the parameter
		return newMerger(db);
	}

	@Override
	public ThreeWayMerger newMerger(ObjectInserter inserter, Config config) {
		return new InCoreMerger(inserter);
	}

	private static class InCoreMerger extends ThreeWayMerger {
		private static final int T_BASE = 0;

		private static final int T_OURS = 1;

		private static final int T_THEIRS = 2;

		private final NameConflictTreeWalk tw;

		private final DirCache cache;

		private DirCacheBuilder builder;

		private ObjectId resultTree;

		InCoreMerger(Repository local) {
			super(local);
			tw = new NameConflictTreeWalk(local, reader);
			cache = DirCache.newInCore();
		}

		InCoreMerger(ObjectInserter inserter) {
			super(inserter);
			tw = new NameConflictTreeWalk(null, reader);
			cache = DirCache.newInCore();
		}

		@Override
		protected boolean mergeImpl() throws IOException {
			tw.addTree(mergeBase());
			tw.addTree(sourceTrees[0]);
			tw.addTree(sourceTrees[1]);

			boolean hasConflict = false;
			builder = cache.builder();
			while (tw.next()) {
				final int modeO = tw.getRawMode(T_OURS);
				final int modeT = tw.getRawMode(T_THEIRS);
				if (modeO == modeT && tw.idEqual(T_OURS, T_THEIRS)) {
					add(T_OURS, DirCacheEntry.STAGE_0);
					continue;
				}

				final int modeB = tw.getRawMode(T_BASE);
				if (modeB == modeO && tw.idEqual(T_BASE, T_OURS))
					add(T_THEIRS, DirCacheEntry.STAGE_0);
				else if (modeB == modeT && tw.idEqual(T_BASE, T_THEIRS))
					add(T_OURS, DirCacheEntry.STAGE_0);
				else {
					if (nonTree(modeB)) {
						add(T_BASE, DirCacheEntry.STAGE_1);
						hasConflict = true;
					}
					if (nonTree(modeO)) {
						add(T_OURS, DirCacheEntry.STAGE_2);
						hasConflict = true;
					}
					if (nonTree(modeT)) {
						add(T_THEIRS, DirCacheEntry.STAGE_3);
						hasConflict = true;
					}
					if (tw.isSubtree())
						tw.enterSubtree();
				}
			}
			builder.finish();
			builder = null;

			if (hasConflict)
				return false;
			try {
				ObjectInserter odi = getObjectInserter();
				resultTree = cache.writeTree(odi);
				odi.flush();
				return true;
			} catch (UnmergedPathException upe) {
				resultTree = null;
				return false;
			}
		}

		private static boolean nonTree(int mode) {
			return mode != 0 && !FileMode.TREE.equals(mode);
		}

		private void add(int tree, int stage) throws IOException {
			final AbstractTreeIterator i = getTree(tree);
			if (i != null) {
				if (FileMode.TREE.equals(tw.getRawMode(tree))) {
					builder.addTree(tw.getRawPath(), stage, reader, tw
							.getObjectId(tree));
				} else {
					final DirCacheEntry e;

					e = new DirCacheEntry(tw.getRawPath(), stage);
					e.setObjectIdFromRaw(i.idBuffer(), i.idOffset());
					e.setFileMode(tw.getFileMode(tree));
					builder.add(e);
				}
			}
		}

		private AbstractTreeIterator getTree(int tree) {
			return tw.getTree(tree, AbstractTreeIterator.class);
		}

		@Override
		public ObjectId getResultTreeId() {
			return resultTree;
		}
	}

}