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