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