1
2
3
4
5
6
7
8
9
10 package org.eclipse.jgit.merge;
11
12 import static org.junit.Assert.assertEquals;
13 import static org.junit.Assert.assertFalse;
14 import static org.junit.Assert.assertTrue;
15 import static org.junit.Assume.assumeTrue;
16
17 import java.io.File;
18 import java.io.IOException;
19 import java.nio.file.Files;
20 import java.nio.file.LinkOption;
21
22 import org.eclipse.jgit.api.Git;
23 import org.eclipse.jgit.api.MergeResult;
24 import org.eclipse.jgit.api.MergeResult.MergeStatus;
25 import org.eclipse.jgit.api.ResetCommand.ResetType;
26 import org.eclipse.jgit.junit.RepositoryTestCase;
27 import org.eclipse.jgit.junit.TestRepository;
28 import org.eclipse.jgit.lib.ConfigConstants;
29 import org.eclipse.jgit.lib.Repository;
30 import org.eclipse.jgit.lib.StoredConfig;
31 import org.eclipse.jgit.revwalk.RevCommit;
32 import org.junit.Test;
33 import org.junit.runner.RunWith;
34 import org.junit.runners.Parameterized;
35 import org.junit.runners.Parameterized.Parameter;
36 import org.junit.runners.Parameterized.Parameters;
37
38
39
40
41 @RunWith(Parameterized.class)
42 public class SymlinkMergeTest extends RepositoryTestCase {
43
44 @Parameters(name = "target={0}, core.symlinks={1}")
45 public static Object[][] parameters() {
46 return new Object[][] {
47 { Target.NONE, Boolean.TRUE },
48 { Target.FILE, Boolean.TRUE },
49 { Target.DIRECTORY, Boolean.TRUE },
50 { Target.NONE, Boolean.FALSE },
51 { Target.FILE, Boolean.FALSE },
52 { Target.DIRECTORY, Boolean.FALSE },
53 };
54 }
55
56 public enum Target {
57 NONE, FILE, DIRECTORY
58 }
59
60 @Parameter(0)
61 public Target target;
62
63 @Parameter(1)
64 public boolean useSymLinks;
65
66 private void setTargets() throws IOException {
67 switch (target) {
68 case DIRECTORY:
69 assertTrue(new File(trash, "target").mkdir());
70 assertTrue(new File(trash, "target1").mkdir());
71 assertTrue(new File(trash, "target2").mkdir());
72 break;
73 case FILE:
74 writeTrashFile("target", "t");
75 writeTrashFile("target1", "t1");
76 writeTrashFile("target2", "t2");
77 break;
78 default:
79 break;
80 }
81 }
82
83 private void checkTargets() throws IOException {
84 File t = new File(trash, "target");
85 File t1 = new File(trash, "target1");
86 File t2 = new File(trash, "target2");
87 switch (target) {
88 case DIRECTORY:
89 assertTrue(t.isDirectory());
90 assertTrue(t1.isDirectory());
91 assertTrue(t2.isDirectory());
92 break;
93 case FILE:
94 checkFile(t, "t");
95 checkFile(t1, "t1");
96 checkFile(t2, "t2");
97 break;
98 default:
99 assertFalse(Files.exists(t.toPath(), LinkOption.NOFOLLOW_LINKS));
100 assertFalse(Files.exists(t1.toPath(), LinkOption.NOFOLLOW_LINKS));
101 assertFalse(Files.exists(t2.toPath(), LinkOption.NOFOLLOW_LINKS));
102 break;
103 }
104 }
105
106 private void assertSymLink(File link, String content) throws Exception {
107 if (useSymLinks) {
108 assertTrue(Files.isSymbolicLink(link.toPath()));
109 assertEquals(content, db.getFS().readSymLink(link));
110 } else {
111 assertFalse(Files.isSymbolicLink(link.toPath()));
112 assertTrue(link.isFile());
113 checkFile(link, content);
114 }
115 }
116
117
118
119
120 @Test
121 public void mergeWithSymlinkConflict() throws Exception {
122 assumeTrue(db.getFS().supportsSymlinks() || !useSymLinks);
123 StoredConfig config = db.getConfig();
124 config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
125 ConfigConstants.CONFIG_KEY_SYMLINKS, useSymLinks);
126 config.save();
127 try (TestRepository<Repository> repo = new TestRepository<>(db)) {
128 db.incrementOpen();
129
130
131
132 RevCommit base = repo
133 .commit(repo.tree(repo.link("link", repo.blob("target"))));
134 RevCommit side = repo.commit(
135 repo.tree(repo.link("link", repo.blob("target1"))), base);
136 RevCommit head = repo.commit(
137 repo.tree(repo.link("link", repo.blob("target2"))), base);
138 try (Git git = new Git(db)) {
139 setTargets();
140 git.reset().setMode(ResetType.HARD).setRef(head.name()).call();
141 File link = new File(trash, "link");
142 assertSymLink(link, "target2");
143 MergeResult result = git.merge().include(side)
144 .setMessage("merged").call();
145 assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
146
147 assertSymLink(link, "target2");
148 checkTargets();
149 assertEquals("[link, mode:120000, stage:1, content:target]"
150 + "[link, mode:120000, stage:2, content:target2]"
151 + "[link, mode:120000, stage:3, content:target1]",
152 indexState(CONTENT));
153 }
154 }
155 }
156
157
158
159
160 @Test
161 public void mergeWithFileSymlinkConflict() throws Exception {
162 assumeTrue(db.getFS().supportsSymlinks() || !useSymLinks);
163 StoredConfig config = db.getConfig();
164 config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
165 ConfigConstants.CONFIG_KEY_SYMLINKS, useSymLinks);
166 config.save();
167 try (TestRepository<Repository> repo = new TestRepository<>(db)) {
168 db.incrementOpen();
169 RevCommit base = repo.commit(repo.tree());
170 RevCommit side = repo.commit(
171 repo.tree(repo.link("link", repo.blob("target1"))), base);
172 RevCommit head = repo.commit(
173 repo.tree(repo.file("link", repo.blob("not a link"))),
174 base);
175 try (Git git = new Git(db)) {
176 setTargets();
177 git.reset().setMode(ResetType.HARD).setRef(head.name()).call();
178 File link = new File(trash, "link");
179 assertFalse(Files.isSymbolicLink(link.toPath()));
180 checkFile(link, "not a link");
181 MergeResult result = git.merge().include(side)
182 .setMessage("merged").call();
183 assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
184
185 assertFalse(Files.isSymbolicLink(link.toPath()));
186 checkFile(link, "not a link");
187 checkTargets();
188 assertEquals("[link, mode:100644, stage:2, content:not a link]"
189 + "[link, mode:120000, stage:3, content:target1]",
190 indexState(CONTENT));
191 }
192 }
193 }
194
195 @Test
196 public void mergeWithSymlinkFileConflict() throws Exception {
197 assumeTrue(db.getFS().supportsSymlinks() || !useSymLinks);
198 StoredConfig config = db.getConfig();
199 config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
200 ConfigConstants.CONFIG_KEY_SYMLINKS, useSymLinks);
201 config.save();
202 try (TestRepository<Repository> repo = new TestRepository<>(db)) {
203 db.incrementOpen();
204 RevCommit base = repo.commit(repo.tree());
205 RevCommit side = repo.commit(
206 repo.tree(repo.file("link", repo.blob("not a link"))),
207 base);
208 RevCommit head = repo.commit(
209 repo.tree(repo.link("link", repo.blob("target2"))), base);
210 try (Git git = new Git(db)) {
211 setTargets();
212 git.reset().setMode(ResetType.HARD).setRef(head.name()).call();
213 File link = new File(trash, "link");
214 assertSymLink(link, "target2");
215 MergeResult result = git.merge().include(side)
216 .setMessage("merged").call();
217 assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
218
219 assertFalse(Files.isSymbolicLink(link.toPath()));
220 checkFile(link, "not a link");
221 checkTargets();
222 assertEquals("[link, mode:120000, stage:2, content:target2]"
223 + "[link, mode:100644, stage:3, content:not a link]",
224 indexState(CONTENT));
225 }
226 }
227 }
228
229
230
231
232 @Test
233 public void mergeWithSymlinkDeleteModify() throws Exception {
234 assumeTrue(db.getFS().supportsSymlinks() || !useSymLinks);
235 StoredConfig config = db.getConfig();
236 config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
237 ConfigConstants.CONFIG_KEY_SYMLINKS, useSymLinks);
238 config.save();
239 try (TestRepository<Repository> repo = new TestRepository<>(db)) {
240 db.incrementOpen();
241 RevCommit base = repo
242 .commit(repo.tree(repo.link("link", repo.blob("target"))));
243 RevCommit side = repo.commit(
244 repo.tree(repo.link("link", repo.blob("target1"))), base);
245 RevCommit head = repo.commit(repo.tree(), base);
246 try (Git git = new Git(db)) {
247 setTargets();
248 git.reset().setMode(ResetType.HARD).setRef(head.name()).call();
249 File link = new File(trash, "link");
250 assertFalse(
251 Files.exists(link.toPath(), LinkOption.NOFOLLOW_LINKS));
252 MergeResult result = git.merge().include(side)
253 .setMessage("merged").call();
254 assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
255
256 assertSymLink(link, "target1");
257 checkTargets();
258 assertEquals("[link, mode:120000, stage:1, content:target]"
259 + "[link, mode:120000, stage:3, content:target1]",
260 indexState(CONTENT));
261 }
262 }
263 }
264
265 @Test
266 public void mergeWithSymlinkModifyDelete() throws Exception {
267 assumeTrue(db.getFS().supportsSymlinks() || !useSymLinks);
268 StoredConfig config = db.getConfig();
269 config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
270 ConfigConstants.CONFIG_KEY_SYMLINKS, useSymLinks);
271 config.save();
272 try (TestRepository<Repository> repo = new TestRepository<>(db)) {
273 db.incrementOpen();
274 RevCommit base = repo
275 .commit(repo.tree(repo.link("link", repo.blob("target"))));
276 RevCommit side = repo.commit(repo.tree(), base);
277 RevCommit head = repo.commit(
278 repo.tree(repo.link("link", repo.blob("target2"))), base);
279 try (Git git = new Git(db)) {
280 setTargets();
281 git.reset().setMode(ResetType.HARD).setRef(head.name()).call();
282 File link = new File(trash, "link");
283 assertSymLink(link, "target2");
284 MergeResult result = git.merge().include(side)
285 .setMessage("merged").call();
286 assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
287
288 assertSymLink(link, "target2");
289 checkTargets();
290 assertEquals("[link, mode:120000, stage:1, content:target]"
291 + "[link, mode:120000, stage:2, content:target2]",
292 indexState(CONTENT));
293 }
294 }
295 }
296 }