View Javadoc
1   /*
2    * Copyright (C) 2013, Axel Richard <axel.richard@obeo.fr>
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available under the
6    * terms of the Eclipse Distribution License v1.0 which accompanies this
7    * distribution, is reproduced below, and is available at
8    * http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or without
13   * modification, are permitted provided that the following conditions are met:
14   *
15   * - Redistributions of source code must retain the above copyright notice, this
16   * list of conditions and the following disclaimer.
17   *
18   * - Redistributions in binary form must reproduce the above copyright notice,
19   * this list of conditions and the following disclaimer in the documentation
20   * and/or other materials provided with the distribution.
21   *
22   * - Neither the name of the Eclipse Foundation, Inc. nor the names of its
23   * contributors may be used to endorse or promote products derived from this
24   * software without specific prior written permission.
25   *
26   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
30   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36   * POSSIBILITY OF SUCH DAMAGE.
37   */
38  package org.eclipse.jgit.symlinks;
39  
40  import static org.junit.Assert.assertEquals;
41  
42  import java.io.File;
43  import java.util.List;
44  
45  import org.eclipse.jgit.api.Git;
46  import org.eclipse.jgit.diff.DiffEntry;
47  import org.eclipse.jgit.junit.RepositoryTestCase;
48  import org.eclipse.jgit.lib.FileMode;
49  import org.eclipse.jgit.lib.Ref;
50  import org.eclipse.jgit.revwalk.RevCommit;
51  import org.eclipse.jgit.treewalk.FileTreeIterator;
52  import org.eclipse.jgit.treewalk.FileTreeIterator.FileEntry;
53  import org.eclipse.jgit.treewalk.TreeWalk;
54  import org.eclipse.jgit.util.FS;
55  import org.eclipse.jgit.util.FileUtils;
56  import org.junit.Before;
57  import org.junit.Test;
58  
59  public class SymlinksTest extends RepositoryTestCase {
60  	@Before
61  	public void beforeMethod() {
62  		// If this assumption fails the tests are skipped. When running on a
63  		// filesystem not supporting symlinks I don't want this tests
64  		org.junit.Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
65  	}
66  
67  	/**
68  	 * Steps: 1.Add file 'a' 2.Commit 3.Create branch '1' 4.Replace file 'a' by
69  	 * symlink 'a' 5.Commit 6.Checkout branch '1'
70  	 *
71  	 * The working tree should contain 'a' with FileMode.REGULAR_FILE after the
72  	 * checkout.
73  	 *
74  	 * @throws Exception
75  	 */
76  	@Test
77  	public void fileModeTestFileThenSymlink() throws Exception {
78  		try (Git git = new Git(db)) {
79  			writeTrashFile("a", "Hello world a");
80  			writeTrashFile("b", "Hello world b");
81  			git.add().addFilepattern(".").call();
82  			git.commit().setMessage("add files a & b").call();
83  			Ref branch_1 = git.branchCreate().setName("branch_1").call();
84  			git.rm().addFilepattern("a").call();
85  			FileUtils.createSymLink(new File(db.getWorkTree(), "a"), "b");
86  			git.add().addFilepattern("a").call();
87  			git.commit().setMessage("add symlink a").call();
88  
89  			FileEntry entry = new FileTreeIterator.FileEntry(new File(
90  					db.getWorkTree(), "a"), db.getFS());
91  			assertEquals(FileMode.SYMLINK, entry.getMode());
92  
93  			git.checkout().setName(branch_1.getName()).call();
94  
95  			entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
96  					db.getFS());
97  			assertEquals(FileMode.REGULAR_FILE, entry.getMode());
98  		}
99  	}
100 
101 	/**
102 	 * Steps: 1.Add symlink 'a' 2.Commit 3.Create branch '1' 4.Replace symlink
103 	 * 'a' by file 'a' 5.Commit 6.Checkout branch '1'
104 	 *
105 	 * The working tree should contain 'a' with FileMode.SYMLINK after the
106 	 * checkout.
107 	 *
108 	 * @throws Exception
109 	 */
110 	@Test
111 	public void fileModeTestSymlinkThenFile() throws Exception {
112 		try (Git git = new Git(db)) {
113 			writeTrashFile("b", "Hello world b");
114 			FileUtils.createSymLink(new File(db.getWorkTree(), "a"), "b");
115 			git.add().addFilepattern(".").call();
116 			git.commit().setMessage("add file b & symlink a").call();
117 			Ref branch_1 = git.branchCreate().setName("branch_1").call();
118 			git.rm().addFilepattern("a").call();
119 			writeTrashFile("a", "Hello world a");
120 			git.add().addFilepattern("a").call();
121 			git.commit().setMessage("add file a").call();
122 
123 			FileEntry entry = new FileTreeIterator.FileEntry(new File(
124 					db.getWorkTree(), "a"), db.getFS());
125 			assertEquals(FileMode.REGULAR_FILE, entry.getMode());
126 
127 			git.checkout().setName(branch_1.getName()).call();
128 
129 			entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
130 					db.getFS());
131 			assertEquals(FileMode.SYMLINK, entry.getMode());
132 		}
133 	}
134 
135 	/**
136 	 * Steps: 1.Add folder 'a' 2.Commit 3.Create branch '1' 4.Replace folder 'a'
137 	 * by symlink 'a' 5.Commit 6.Checkout branch '1'
138 	 *
139 	 * The working tree should contain 'a' with FileMode.TREE after the
140 	 * checkout.
141 	 *
142 	 * @throws Exception
143 	 */
144 	@Test
145 	public void fileModeTestFolderThenSymlink() throws Exception {
146 		try (Git git = new Git(db)) {
147 			FileUtils.mkdirs(new File(db.getWorkTree(), "a"));
148 			writeTrashFile("a/b", "Hello world b");
149 			writeTrashFile("c", "Hello world c");
150 			git.add().addFilepattern(".").call();
151 			git.commit().setMessage("add folder a").call();
152 			Ref branch_1 = git.branchCreate().setName("branch_1").call();
153 			git.rm().addFilepattern("a").call();
154 			FileUtils.createSymLink(new File(db.getWorkTree(), "a"), "c");
155 			git.add().addFilepattern("a").call();
156 			git.commit().setMessage("add symlink a").call();
157 
158 			FileEntry entry = new FileTreeIterator.FileEntry(new File(
159 					db.getWorkTree(), "a"), db.getFS());
160 			assertEquals(FileMode.SYMLINK, entry.getMode());
161 
162 			git.checkout().setName(branch_1.getName()).call();
163 
164 			entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
165 					db.getFS());
166 			assertEquals(FileMode.TREE, entry.getMode());
167 		}
168 	}
169 
170 	/**
171 	 * Steps: 1.Add symlink 'a' 2.Commit 3.Create branch '1' 4.Replace symlink
172 	 * 'a' by folder 'a' 5.Commit 6.Checkout branch '1'
173 	 *
174 	 * The working tree should contain 'a' with FileMode.SYMLINK after the
175 	 * checkout.
176 	 *
177 	 * @throws Exception
178 	 */
179 	@Test
180 	public void fileModeTestSymlinkThenFolder() throws Exception {
181 		try (Git git = new Git(db)) {
182 			writeTrashFile("c", "Hello world c");
183 			FileUtils.createSymLink(new File(db.getWorkTree(), "a"), "c");
184 			git.add().addFilepattern(".").call();
185 			git.commit().setMessage("add symlink a").call();
186 			Ref branch_1 = git.branchCreate().setName("branch_1").call();
187 			git.rm().addFilepattern("a").call();
188 			FileUtils.mkdirs(new File(db.getWorkTree(), "a"));
189 			writeTrashFile("a/b", "Hello world b");
190 			git.add().addFilepattern("a").call();
191 			git.commit().setMessage("add folder a").call();
192 
193 			FileEntry entry = new FileTreeIterator.FileEntry(new File(
194 					db.getWorkTree(), "a"), db.getFS());
195 			assertEquals(FileMode.TREE, entry.getMode());
196 
197 			git.checkout().setName(branch_1.getName()).call();
198 
199 			entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
200 					db.getFS());
201 			assertEquals(FileMode.SYMLINK, entry.getMode());
202 		}
203 	}
204 
205 	/**
206 	 * Steps: 1.Add file 'b' 2.Commit 3.Create branch '1' 4.Add symlink 'a'
207 	 * 5.Commit 6.Checkout branch '1'
208 	 *
209 	 * The working tree should not contain 'a' -&gt; FileMode.MISSING after the
210 	 * checkout.
211 	 *
212 	 * @throws Exception
213 	 */
214 	@Test
215 	public void fileModeTestMissingThenSymlink() throws Exception {
216 		try (Git git = new Git(db);
217 				TreeWalk tw = new TreeWalk(db);) {
218 			writeTrashFile("b", "Hello world b");
219 			git.add().addFilepattern(".").call();
220 			RevCommit commit1 = git.commit().setMessage("add file b").call();
221 			Ref branch_1 = git.branchCreate().setName("branch_1").call();
222 			FileUtils.createSymLink(new File(db.getWorkTree(), "a"), "b");
223 			git.add().addFilepattern("a").call();
224 			RevCommit commit2 = git.commit().setMessage("add symlink a").call();
225 
226 			git.checkout().setName(branch_1.getName()).call();
227 
228 			tw.addTree(commit1.getTree());
229 			tw.addTree(commit2.getTree());
230 			List<DiffEntry> scan = DiffEntry.scan(tw);
231 			assertEquals(1, scan.size());
232 			assertEquals(FileMode.SYMLINK, scan.get(0).getNewMode());
233 			assertEquals(FileMode.MISSING, scan.get(0).getOldMode());
234 		}
235 	}
236 
237 	/**
238 	 * Steps: 1.Add symlink 'a' 2.Commit 3.Create branch '1' 4.Delete symlink
239 	 * 'a' 5.Commit 6.Checkout branch '1'
240 	 *
241 	 * The working tree should contain 'a' with FileMode.SYMLINK after the
242 	 * checkout.
243 	 *
244 	 * @throws Exception
245 	 */
246 	@Test
247 	public void fileModeTestSymlinkThenMissing() throws Exception {
248 		try (Git git = new Git(db);
249 				TreeWalk tw = new TreeWalk(db);) {
250 			writeTrashFile("b", "Hello world b");
251 			FileUtils.createSymLink(new File(db.getWorkTree(), "a"), "b");
252 			git.add().addFilepattern(".").call();
253 			RevCommit commit1 = git.commit().setMessage("add file b & symlink a")
254 					.call();
255 			Ref branch_1 = git.branchCreate().setName("branch_1").call();
256 			git.rm().addFilepattern("a").call();
257 			RevCommit commit2 = git.commit().setMessage("delete symlink a").call();
258 
259 			git.checkout().setName(branch_1.getName()).call();
260 
261 			tw.addTree(commit1.getTree());
262 			tw.addTree(commit2.getTree());
263 			List<DiffEntry> scan = DiffEntry.scan(tw);
264 			assertEquals(1, scan.size());
265 			assertEquals(FileMode.MISSING, scan.get(0).getNewMode());
266 			assertEquals(FileMode.SYMLINK, scan.get(0).getOldMode());
267 		}
268 	}
269 
270 	@Test
271 	public void createSymlinkAfterTarget() throws Exception {
272 		try (Git git = new Git(db)) {
273 			writeTrashFile("a", "start");
274 			git.add().addFilepattern("a").call();
275 			RevCommit base = git.commit().setMessage("init").call();
276 			writeTrashFile("target", "someData");
277 			FileUtils.createSymLink(new File(db.getWorkTree(), "link"), "target");
278 			git.add().addFilepattern("target").addFilepattern("link").call();
279 			git.commit().setMessage("add target").call();
280 			assertEquals(4, db.getWorkTree().list().length); // self-check
281 			git.checkout().setName(base.name()).call();
282 			assertEquals(2, db.getWorkTree().list().length); // self-check
283 			git.checkout().setName("master").call();
284 			assertEquals(4, db.getWorkTree().list().length);
285 			String data = read(new File(db.getWorkTree(), "target"));
286 			assertEquals(8, new File(db.getWorkTree(), "target").length());
287 			assertEquals("someData", data);
288 			data = read(new File(db.getWorkTree(), "link"));
289 			assertEquals("target",
290 					FileUtils.readSymLink(new File(db.getWorkTree(), "link")));
291 			assertEquals("someData", data);
292 		}
293 	}
294 
295 	@Test
296 	public void createFileSymlinkBeforeTarget() throws Exception {
297 		try (Git git = new Git(db)) {
298 			writeTrashFile("a", "start");
299 			git.add().addFilepattern("a").call();
300 			RevCommit base = git.commit().setMessage("init").call();
301 			writeTrashFile("target", "someData");
302 			FileUtils.createSymLink(new File(db.getWorkTree(), "tlink"), "target");
303 			git.add().addFilepattern("target").addFilepattern("tlink").call();
304 			git.commit().setMessage("add target").call();
305 			assertEquals(4, db.getWorkTree().list().length); // self-check
306 			git.checkout().setName(base.name()).call();
307 			assertEquals(2, db.getWorkTree().list().length); // self-check
308 			git.checkout().setName("master").call();
309 			assertEquals(4, db.getWorkTree().list().length);
310 			String data = read(new File(db.getWorkTree(), "target"));
311 			assertEquals(8, new File(db.getWorkTree(), "target").length());
312 			assertEquals("someData", data);
313 			data = read(new File(db.getWorkTree(), "tlink"));
314 			assertEquals("target",
315 					FileUtils.readSymLink(new File(db.getWorkTree(), "tlink")));
316 			assertEquals("someData", data);
317 		}
318 	}
319 
320 	@Test
321 	public void createDirSymlinkBeforeTarget() throws Exception {
322 		try (Git git = new Git(db)) {
323 			writeTrashFile("a", "start");
324 			git.add().addFilepattern("a").call();
325 			RevCommit base = git.commit().setMessage("init").call();
326 			FileUtils.createSymLink(new File(db.getWorkTree(), "link"), "target");
327 			FileUtils.mkdir(new File(db.getWorkTree(), "target"));
328 			writeTrashFile("target/file", "someData");
329 			git.add().addFilepattern("target").addFilepattern("link").call();
330 			git.commit().setMessage("add target").call();
331 			assertEquals(4, db.getWorkTree().list().length); // self-check
332 			git.checkout().setName(base.name()).call();
333 			assertEquals(2, db.getWorkTree().list().length); // self-check
334 			git.checkout().setName("master").call();
335 			assertEquals(4, db.getWorkTree().list().length);
336 			String data = read(new File(db.getWorkTree(), "target/file"));
337 			assertEquals(8, new File(db.getWorkTree(), "target/file").length());
338 			assertEquals("someData", data);
339 			data = read(new File(db.getWorkTree(), "link/file"));
340 			assertEquals("target",
341 					FileUtils.readSymLink(new File(db.getWorkTree(), "link")));
342 			assertEquals("someData", data);
343 		}
344 	}
345 }