1
2
3
4
5
6
7
8
9
10 package org.eclipse.jgit.api;
11
12 import static org.junit.Assert.assertEquals;
13 import static org.junit.Assert.assertFalse;
14 import static org.junit.Assert.assertTrue;
15
16 import java.io.File;
17 import java.io.IOException;
18 import java.nio.file.Path;
19
20 import org.eclipse.jgit.api.CheckoutCommand.Stage;
21 import org.eclipse.jgit.api.errors.JGitInternalException;
22 import org.eclipse.jgit.dircache.DirCache;
23 import org.eclipse.jgit.dircache.DirCacheEntry;
24 import org.eclipse.jgit.errors.NoWorkTreeException;
25 import org.eclipse.jgit.junit.RepositoryTestCase;
26 import org.eclipse.jgit.lib.ConfigConstants;
27 import org.eclipse.jgit.lib.Constants;
28 import org.eclipse.jgit.lib.ObjectReader;
29 import org.eclipse.jgit.lib.RepositoryState;
30 import org.eclipse.jgit.lib.StoredConfig;
31 import org.eclipse.jgit.revwalk.RevCommit;
32 import org.eclipse.jgit.util.FS;
33 import org.eclipse.jgit.util.FileUtils;
34 import org.junit.Assume;
35 import org.junit.Before;
36 import org.junit.Test;
37
38
39
40
41 public class PathCheckoutCommandTest extends RepositoryTestCase {
42
43 private static final String FILE1 = "f/Test.txt";
44
45 private static final String FILE2 = "Test2.txt";
46
47 private static final String FILE3 = "Test3.txt";
48
49 private static final String LINK = "link";
50
51 Git git;
52
53 RevCommit initialCommit;
54
55 RevCommit secondCommit;
56
57 @Override
58 @Before
59 public void setUp() throws Exception {
60 super.setUp();
61 git = new Git(db);
62 writeTrashFile(FILE1, "1");
63 writeTrashFile(FILE2, "a");
64 git.add().addFilepattern(FILE1).addFilepattern(FILE2).call();
65 initialCommit = git.commit().setMessage("Initial commit").call();
66 writeTrashFile(FILE1, "2");
67 writeTrashFile(FILE2, "b");
68 git.add().addFilepattern(FILE1).addFilepattern(FILE2).call();
69 secondCommit = git.commit().setMessage("Second commit").call();
70 writeTrashFile(FILE1, "3");
71 writeTrashFile(FILE2, "c");
72 git.add().addFilepattern(FILE1).addFilepattern(FILE2).call();
73 git.commit().setMessage("Third commit").call();
74 }
75
76 @Test
77 public void testUpdateSymLink() throws Exception {
78 Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
79
80 Path path = writeLink(LINK, FILE1);
81 git.add().addFilepattern(LINK).call();
82 git.commit().setMessage("Added link").call();
83 assertEquals("3", read(path.toFile()));
84
85 writeLink(LINK, FILE2);
86 assertEquals("c", read(path.toFile()));
87
88 CheckoutCommand co = git.checkout();
89 co.addPath(LINK).call();
90
91 assertEquals("3", read(path.toFile()));
92 }
93
94 @Test
95 public void testUpdateBrokenSymLinkToDirectory() throws Exception {
96 Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
97
98 Path path = writeLink(LINK, "f");
99 git.add().addFilepattern(LINK).call();
100 git.commit().setMessage("Added link").call();
101 assertEquals("f", FileUtils.readSymLink(path.toFile()));
102 assertTrue(path.toFile().exists());
103
104 writeLink(LINK, "link_to_nowhere");
105 assertFalse(path.toFile().exists());
106 assertEquals("link_to_nowhere", FileUtils.readSymLink(path.toFile()));
107
108 CheckoutCommand co = git.checkout();
109 co.addPath(LINK).call();
110
111 assertEquals("f", FileUtils.readSymLink(path.toFile()));
112 }
113
114 @Test
115 public void testUpdateBrokenSymLink() throws Exception {
116 Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
117
118 Path path = writeLink(LINK, FILE1);
119 git.add().addFilepattern(LINK).call();
120 git.commit().setMessage("Added link").call();
121 assertEquals("3", read(path.toFile()));
122 assertEquals(FILE1, FileUtils.readSymLink(path.toFile()));
123
124 writeLink(LINK, "link_to_nowhere");
125 assertFalse(path.toFile().exists());
126 assertEquals("link_to_nowhere", FileUtils.readSymLink(path.toFile()));
127
128 CheckoutCommand co = git.checkout();
129 co.addPath(LINK).call();
130
131 assertEquals("3", read(path.toFile()));
132 }
133
134 @Test
135 public void testUpdateWorkingDirectory() throws Exception {
136 CheckoutCommand co = git.checkout();
137 File written = writeTrashFile(FILE1, "");
138 assertEquals("", read(written));
139 co.addPath(FILE1).call();
140 assertEquals("3", read(written));
141 assertEquals("c", read(new File(db.getWorkTree(), FILE2)));
142 }
143
144 @Test
145 public void testCheckoutFirst() throws Exception {
146 CheckoutCommand co = git.checkout();
147 File written = writeTrashFile(FILE1, "");
148 co.setStartPoint(initialCommit).addPath(FILE1).call();
149 assertEquals("1", read(written));
150 assertEquals("c", read(new File(db.getWorkTree(), FILE2)));
151 }
152
153 @Test
154 public void testCheckoutSecond() throws Exception {
155 CheckoutCommand co = git.checkout();
156 File written = writeTrashFile(FILE1, "");
157 co.setStartPoint("HEAD~1").addPath(FILE1).call();
158 assertEquals("2", read(written));
159 assertEquals("c", read(new File(db.getWorkTree(), FILE2)));
160 }
161
162 @Test
163 public void testCheckoutMultiple() throws Exception {
164 CheckoutCommand co = git.checkout();
165 File test = writeTrashFile(FILE1, "");
166 File test2 = writeTrashFile(FILE2, "");
167 co.setStartPoint("HEAD~2").addPath(FILE1).addPath(FILE2).call();
168 assertEquals("1", read(test));
169 assertEquals("a", read(test2));
170 }
171
172 @Test
173 public void testUpdateWorkingDirectoryFromIndex() throws Exception {
174 CheckoutCommand co = git.checkout();
175 File written = writeTrashFile(FILE1, "3a");
176 git.add().addFilepattern(FILE1).call();
177 written = writeTrashFile(FILE1, "");
178 assertEquals("", read(written));
179 co.addPath(FILE1).call();
180 assertEquals("3a", read(written));
181 assertEquals("c", read(new File(db.getWorkTree(), FILE2)));
182 }
183
184 @Test
185 public void testUpdateWorkingDirectoryFromHeadWithIndexChange()
186 throws Exception {
187 CheckoutCommand co = git.checkout();
188 File written = writeTrashFile(FILE1, "3a");
189 git.add().addFilepattern(FILE1).call();
190 written = writeTrashFile(FILE1, "");
191 assertEquals("", read(written));
192 co.addPath(FILE1).setStartPoint("HEAD").call();
193 assertEquals("3", read(written));
194 assertEquals("c", read(new File(db.getWorkTree(), FILE2)));
195 }
196
197 @Test
198 public void testUpdateWorkingDirectoryFromIndex2() throws Exception {
199 CheckoutCommand co = git.checkout();
200 fsTick(git.getRepository().getIndexFile());
201
202 File written1 = writeTrashFile(FILE1, "3(modified)");
203 File written2 = writeTrashFile(FILE2, "a(modified)");
204 fsTick(written2);
205
206
207 writeTrashFile(FILE3, "foo");
208 git.add().addFilepattern(FILE3).call();
209 fsTick(git.getRepository().getIndexFile());
210
211 git.add().addFilepattern(FILE1).addFilepattern(FILE2).call();
212 fsTick(git.getRepository().getIndexFile());
213
214 writeTrashFile(FILE1, "3(modified again)");
215 writeTrashFile(FILE2, "a(modified again)");
216 fsTick(written2);
217
218 co.addPath(FILE1).setStartPoint(secondCommit).call();
219
220 assertEquals("2", read(written1));
221 assertEquals("a(modified again)", read(written2));
222
223 validateIndex(git);
224 }
225
226 public static void validateIndex(Git git) throws NoWorkTreeException,
227 IOException {
228 DirCache dc = git.getRepository().lockDirCache();
229 try (ObjectReader r = git.getRepository().getObjectDatabase()
230 .newReader()) {
231 for (int i = 0; i < dc.getEntryCount(); ++i) {
232 DirCacheEntry entry = dc.getEntry(i);
233 if (entry.getLength() > 0)
234 assertEquals(entry.getLength(), r.getObjectSize(
235 entry.getObjectId(), ObjectReader.OBJ_ANY));
236 }
237 } finally {
238 dc.unlock();
239 }
240 }
241
242 @Test
243 public void testCheckoutMixedNewlines() throws Exception {
244
245 StoredConfig config = git.getRepository().getConfig();
246 config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
247 ConfigConstants.CONFIG_KEY_AUTOCRLF, true);
248 config.save();
249
250 File written = writeTrashFile(FILE1, "4\r\n4");
251 assertEquals("4\r\n4", read(written));
252
253 git.add().addFilepattern(FILE1).call();
254
255 git.commit().setMessage("CRLF").call();
256
257 written = writeTrashFile(FILE1, "4\n4");
258 assertEquals("4\n4", read(written));
259
260 git.add().addFilepattern(FILE1).call();
261
262 git.checkout().addPath(FILE1).call();
263
264 Status status = git.status().call();
265 assertEquals(0, status.getAdded().size());
266 assertEquals(0, status.getChanged().size());
267 assertEquals(0, status.getConflicting().size());
268 assertEquals(0, status.getMissing().size());
269 assertEquals(0, status.getModified().size());
270 assertEquals(0, status.getRemoved().size());
271 assertEquals(0, status.getUntracked().size());
272 }
273
274 @Test
275 public void testCheckoutRepository() throws Exception {
276 CheckoutCommand co = git.checkout();
277 File test = writeTrashFile(FILE1, "");
278 File test2 = writeTrashFile(FILE2, "");
279 co.setStartPoint("HEAD~2").setAllPaths(true).call();
280 assertEquals("1", read(test));
281 assertEquals("a", read(test2));
282 }
283
284
285 @Test(expected = JGitInternalException.class)
286 public void testCheckoutOfConflictingFileShouldThrow()
287 throws Exception {
288 setupConflictingState();
289
290 git.checkout().addPath(FILE1).call();
291 }
292
293 @Test
294 public void testCheckoutOurs() throws Exception {
295 setupConflictingState();
296
297 git.checkout().setStage(Stage.OURS).addPath(FILE1).call();
298
299 assertEquals("3", read(FILE1));
300 assertStageOneToThree(FILE1);
301 }
302
303 @Test
304 public void testCheckoutTheirs() throws Exception {
305 setupConflictingState();
306
307 git.checkout().setStage(Stage.THEIRS).addPath(FILE1).call();
308
309 assertEquals("Conflicting", read(FILE1));
310 assertStageOneToThree(FILE1);
311 }
312
313 @Test
314 public void testCheckoutFileWithConflict() throws Exception {
315 setupConflictingState();
316 assertEquals('[' + FILE1 + ']',
317 git.status().call().getConflicting().toString());
318 git.checkout().setStartPoint(Constants.HEAD).addPath(FILE1).call();
319 assertEquals("3", read(FILE1));
320 assertTrue(git.status().call().isClean());
321 }
322
323 @Test
324 public void testCheckoutOursWhenNoBase() throws Exception {
325 String file = "added.txt";
326
327 git.checkout().setCreateBranch(true).setName("side")
328 .setStartPoint(initialCommit).call();
329 writeTrashFile(file, "Added on side");
330 git.add().addFilepattern(file).call();
331 RevCommit side = git.commit().setMessage("Commit on side").call();
332
333 git.checkout().setName("master").call();
334 writeTrashFile(file, "Added on master");
335 git.add().addFilepattern(file).call();
336 git.commit().setMessage("Commit on master").call();
337
338 git.merge().include(side).call();
339 assertEquals(RepositoryState.MERGING, db.getRepositoryState());
340
341 DirCache cache = DirCache.read(db.getIndexFile(), db.getFS());
342 assertEquals("Expected add/add file to not have base stage",
343 DirCacheEntry.STAGE_2, cache.getEntry(file).getStage());
344
345 assertTrue(read(file).startsWith("<<<<<<< HEAD"));
346
347 git.checkout().setStage(Stage.OURS).addPath(file).call();
348
349 assertEquals("Added on master", read(file));
350
351 cache = DirCache.read(db.getIndexFile(), db.getFS());
352 assertEquals("Expected conflict stages to still exist after checkout",
353 DirCacheEntry.STAGE_2, cache.getEntry(file).getStage());
354 }
355
356 @Test(expected = IllegalStateException.class)
357 public void testStageNotPossibleWithBranch() throws Exception {
358 git.checkout().setStage(Stage.OURS).setStartPoint("master").call();
359 }
360
361 private void setupConflictingState() throws Exception {
362 git.checkout().setCreateBranch(true).setName("conflict")
363 .setStartPoint(initialCommit).call();
364 writeTrashFile(FILE1, "Conflicting");
365 RevCommit conflict = git.commit().setAll(true)
366 .setMessage("Conflicting change").call();
367
368 git.checkout().setName("master").call();
369
370 git.merge().include(conflict).call();
371 assertEquals(RepositoryState.MERGING, db.getRepositoryState());
372 assertStageOneToThree(FILE1);
373 }
374
375 private void assertStageOneToThree(String name) throws Exception {
376 DirCache cache = DirCache.read(db.getIndexFile(), db.getFS());
377 int i = cache.findEntry(name);
378 DirCacheEntry stage1 = cache.getEntry(i);
379 DirCacheEntry stage2 = cache.getEntry(i + 1);
380 DirCacheEntry stage3 = cache.getEntry(i + 2);
381
382 assertEquals(DirCacheEntry.STAGE_1, stage1.getStage());
383 assertEquals(DirCacheEntry.STAGE_2, stage2.getStage());
384 assertEquals(DirCacheEntry.STAGE_3, stage3.getStage());
385 }
386 }