View Javadoc
1   /*
2    * Copyright (C) 2011, GitHub Inc.
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at 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
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42   */
43  package org.eclipse.jgit.submodule;
44  
45  import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PATH;
46  import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_URL;
47  import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_SUBMODULE_SECTION;
48  import static org.eclipse.jgit.lib.Constants.DOT_GIT_MODULES;
49  import static org.junit.Assert.assertEquals;
50  import static org.junit.Assert.assertFalse;
51  import static org.junit.Assert.assertNotNull;
52  import static org.junit.Assert.assertNull;
53  import static org.junit.Assert.assertTrue;
54  
55  import java.io.File;
56  import java.io.FileWriter;
57  import java.io.IOException;
58  
59  import org.eclipse.jgit.api.Git;
60  import org.eclipse.jgit.api.Status;
61  import org.eclipse.jgit.api.errors.GitAPIException;
62  import org.eclipse.jgit.dircache.DirCache;
63  import org.eclipse.jgit.dircache.DirCacheEditor;
64  import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
65  import org.eclipse.jgit.dircache.DirCacheEntry;
66  import org.eclipse.jgit.errors.ConfigInvalidException;
67  import org.eclipse.jgit.errors.NoWorkTreeException;
68  import org.eclipse.jgit.junit.RepositoryTestCase;
69  import org.eclipse.jgit.junit.TestRepository;
70  import org.eclipse.jgit.lib.Config;
71  import org.eclipse.jgit.lib.Constants;
72  import org.eclipse.jgit.lib.FileMode;
73  import org.eclipse.jgit.lib.ObjectId;
74  import org.eclipse.jgit.lib.Repository;
75  import org.eclipse.jgit.revwalk.RevBlob;
76  import org.eclipse.jgit.revwalk.RevCommit;
77  import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
78  import org.eclipse.jgit.treewalk.CanonicalTreeParser;
79  import org.eclipse.jgit.treewalk.filter.PathFilter;
80  import org.junit.Before;
81  import org.junit.Test;
82  
83  /**
84   * Unit tests of {@link SubmoduleWalk}
85   */
86  public class SubmoduleWalkTest extends RepositoryTestCase {
87  	private TestRepository<Repository> testDb;
88  
89  	@Before
90  	public void setUp() throws Exception {
91  		super.setUp();
92  		testDb = new TestRepository<Repository>(db);
93  	}
94  
95  	@Test
96  	public void repositoryWithNoSubmodules() throws IOException {
97  		SubmoduleWalk gen = SubmoduleWalk.forIndex(db);
98  		assertFalse(gen.next());
99  		assertNull(gen.getPath());
100 		assertEquals(ObjectId.zeroId(), gen.getObjectId());
101 	}
102 
103 	@Test
104 	public void repositoryWithRootLevelSubmodule() throws IOException,
105 			ConfigInvalidException, NoWorkTreeException, GitAPIException {
106 		final ObjectId id = ObjectId
107 				.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
108 		final String path = "sub";
109 		DirCache cache = db.lockDirCache();
110 		DirCacheEditor editor = cache.editor();
111 		editor.add(new PathEdit(path) {
112 
113 			public void apply(DirCacheEntry ent) {
114 				ent.setFileMode(FileMode.GITLINK);
115 				ent.setObjectId(id);
116 			}
117 		});
118 		editor.commit();
119 
120 		SubmoduleWalk gen = SubmoduleWalk.forIndex(db);
121 		assertTrue(gen.next());
122 		assertEquals(path, gen.getPath());
123 		assertEquals(id, gen.getObjectId());
124 		assertEquals(new File(db.getWorkTree(), path), gen.getDirectory());
125 		assertNull(gen.getConfigUpdate());
126 		assertNull(gen.getConfigUrl());
127 		assertNull(gen.getModulesPath());
128 		assertNull(gen.getModulesUpdate());
129 		assertNull(gen.getModulesUrl());
130 		assertNull(gen.getRepository());
131 		Status status = Git.wrap(db).status().call();
132 		assertTrue(!status.isClean());
133 		assertFalse(gen.next());
134 	}
135 
136 	@SuppressWarnings("resource" /* java 7 */)
137 	@Test
138 	public void repositoryWithRootLevelSubmoduleAbsoluteRef()
139 			throws IOException, ConfigInvalidException {
140 		final ObjectId id = ObjectId
141 				.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
142 		final String path = "sub";
143 		File dotGit = new File(db.getWorkTree(), path + File.separatorChar
144 				+ Constants.DOT_GIT);
145 		if (!dotGit.getParentFile().exists())
146 			dotGit.getParentFile().mkdirs();
147 
148 		File modulesGitDir = new File(db.getDirectory(), "modules"
149 				+ File.separatorChar + path);
150 		new FileWriter(dotGit).append(
151 				"gitdir: " + modulesGitDir.getAbsolutePath()).close();
152 		FileRepositoryBuilder builder = new FileRepositoryBuilder();
153 		builder.setWorkTree(new File(db.getWorkTree(), path));
154 		builder.build().create();
155 
156 		DirCache cache = db.lockDirCache();
157 		DirCacheEditor editor = cache.editor();
158 		editor.add(new PathEdit(path) {
159 
160 			public void apply(DirCacheEntry ent) {
161 				ent.setFileMode(FileMode.GITLINK);
162 				ent.setObjectId(id);
163 			}
164 		});
165 		editor.commit();
166 
167 		SubmoduleWalk gen = SubmoduleWalk.forIndex(db);
168 		assertTrue(gen.next());
169 		assertEquals(path, gen.getPath());
170 		assertEquals(id, gen.getObjectId());
171 		assertEquals(new File(db.getWorkTree(), path), gen.getDirectory());
172 		assertNull(gen.getConfigUpdate());
173 		assertNull(gen.getConfigUrl());
174 		assertNull(gen.getModulesPath());
175 		assertNull(gen.getModulesUpdate());
176 		assertNull(gen.getModulesUrl());
177 		Repository subRepo = gen.getRepository();
178 		assertNotNull(subRepo);
179 		assertEquals(modulesGitDir.getAbsolutePath(),
180 				subRepo.getDirectory().getAbsolutePath());
181 		assertEquals(new File(db.getWorkTree(), path).getAbsolutePath(),
182 				subRepo.getWorkTree().getAbsolutePath());
183 		subRepo.close();
184 		assertFalse(gen.next());
185 	}
186 
187 	@SuppressWarnings("resource" /* java 7 */)
188 	@Test
189 	public void repositoryWithRootLevelSubmoduleRelativeRef()
190 			throws IOException, ConfigInvalidException {
191 		final ObjectId id = ObjectId
192 				.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
193 		final String path = "sub";
194 		File dotGit = new File(db.getWorkTree(), path + File.separatorChar
195 				+ Constants.DOT_GIT);
196 		if (!dotGit.getParentFile().exists())
197 			dotGit.getParentFile().mkdirs();
198 
199 		File modulesGitDir = new File(db.getDirectory(), "modules"
200 				+ File.separatorChar + path);
201 		new FileWriter(dotGit).append(
202 				"gitdir: " + "../" + Constants.DOT_GIT + "/modules/" + path)
203 				.close();
204 		FileRepositoryBuilder builder = new FileRepositoryBuilder();
205 		builder.setWorkTree(new File(db.getWorkTree(), path));
206 		builder.build().create();
207 
208 		DirCache cache = db.lockDirCache();
209 		DirCacheEditor editor = cache.editor();
210 		editor.add(new PathEdit(path) {
211 
212 			public void apply(DirCacheEntry ent) {
213 				ent.setFileMode(FileMode.GITLINK);
214 				ent.setObjectId(id);
215 			}
216 		});
217 		editor.commit();
218 
219 		SubmoduleWalk gen = SubmoduleWalk.forIndex(db);
220 		assertTrue(gen.next());
221 		assertEquals(path, gen.getPath());
222 		assertEquals(id, gen.getObjectId());
223 		assertEquals(new File(db.getWorkTree(), path), gen.getDirectory());
224 		assertNull(gen.getConfigUpdate());
225 		assertNull(gen.getConfigUrl());
226 		assertNull(gen.getModulesPath());
227 		assertNull(gen.getModulesUpdate());
228 		assertNull(gen.getModulesUrl());
229 		Repository subRepo = gen.getRepository();
230 		assertNotNull(subRepo);
231 		assertEqualsFile(modulesGitDir, subRepo.getDirectory());
232 		assertEqualsFile(new File(db.getWorkTree(), path),
233 				subRepo.getWorkTree());
234 		subRepo.close();
235 		assertFalse(gen.next());
236 	}
237 
238 	@Test
239 	public void repositoryWithNestedSubmodule() throws IOException,
240 			ConfigInvalidException {
241 		final ObjectId id = ObjectId
242 				.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
243 		final String path = "sub/dir/final";
244 		DirCache cache = db.lockDirCache();
245 		DirCacheEditor editor = cache.editor();
246 		editor.add(new PathEdit(path) {
247 
248 			public void apply(DirCacheEntry ent) {
249 				ent.setFileMode(FileMode.GITLINK);
250 				ent.setObjectId(id);
251 			}
252 		});
253 		editor.commit();
254 
255 		SubmoduleWalk gen = SubmoduleWalk.forIndex(db);
256 		assertTrue(gen.next());
257 		assertEquals(path, gen.getPath());
258 		assertEquals(id, gen.getObjectId());
259 		assertEquals(new File(db.getWorkTree(), path), gen.getDirectory());
260 		assertNull(gen.getConfigUpdate());
261 		assertNull(gen.getConfigUrl());
262 		assertNull(gen.getModulesPath());
263 		assertNull(gen.getModulesUpdate());
264 		assertNull(gen.getModulesUrl());
265 		assertNull(gen.getRepository());
266 		assertFalse(gen.next());
267 	}
268 
269 	@Test
270 	public void generatorFilteredToOneOfTwoSubmodules() throws IOException {
271 		final ObjectId id1 = ObjectId
272 				.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
273 		final String path1 = "sub1";
274 		final ObjectId id2 = ObjectId
275 				.fromString("abcd1234abcd1234abcd1234abcd1234abcd1235");
276 		final String path2 = "sub2";
277 		DirCache cache = db.lockDirCache();
278 		DirCacheEditor editor = cache.editor();
279 		editor.add(new PathEdit(path1) {
280 
281 			public void apply(DirCacheEntry ent) {
282 				ent.setFileMode(FileMode.GITLINK);
283 				ent.setObjectId(id1);
284 			}
285 		});
286 		editor.add(new PathEdit(path2) {
287 
288 			public void apply(DirCacheEntry ent) {
289 				ent.setFileMode(FileMode.GITLINK);
290 				ent.setObjectId(id2);
291 			}
292 		});
293 		editor.commit();
294 
295 		SubmoduleWalk gen = SubmoduleWalk.forIndex(db);
296 		gen.setFilter(PathFilter.create(path1));
297 		assertTrue(gen.next());
298 		assertEquals(path1, gen.getPath());
299 		assertEquals(id1, gen.getObjectId());
300 		assertFalse(gen.next());
301 	}
302 
303 	@Test
304 	public void indexWithGitmodules() throws Exception {
305 		final ObjectId subId = ObjectId
306 				.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
307 		final String path = "sub";
308 
309 		final Config gitmodules = new Config();
310 		gitmodules.setString(CONFIG_SUBMODULE_SECTION, path, CONFIG_KEY_PATH,
311 				"sub");
312 		// Different config in the index should be overridden by the working tree.
313 		gitmodules.setString(CONFIG_SUBMODULE_SECTION, path, CONFIG_KEY_URL,
314 				"git://example.com/bad");
315 		final RevBlob gitmodulesBlob = testDb.blob(gitmodules.toText());
316 
317 		gitmodules.setString(CONFIG_SUBMODULE_SECTION, path, CONFIG_KEY_URL,
318 				"git://example.com/sub");
319 		writeTrashFile(DOT_GIT_MODULES, gitmodules.toText());
320 
321 		DirCache cache = db.lockDirCache();
322 		DirCacheEditor editor = cache.editor();
323 		editor.add(new PathEdit(path) {
324 
325 			public void apply(DirCacheEntry ent) {
326 				ent.setFileMode(FileMode.GITLINK);
327 				ent.setObjectId(subId);
328 			}
329 		});
330 		editor.add(new PathEdit(DOT_GIT_MODULES) {
331 
332 			public void apply(DirCacheEntry ent) {
333 				ent.setFileMode(FileMode.REGULAR_FILE);
334 				ent.setObjectId(gitmodulesBlob);
335 			}
336 		});
337 		editor.commit();
338 
339 		SubmoduleWalk gen = SubmoduleWalk.forIndex(db);
340 		assertTrue(gen.next());
341 		assertEquals(path, gen.getPath());
342 		assertEquals(subId, gen.getObjectId());
343 		assertEquals(new File(db.getWorkTree(), path), gen.getDirectory());
344 		assertNull(gen.getConfigUpdate());
345 		assertNull(gen.getConfigUrl());
346 		assertEquals("sub", gen.getModulesPath());
347 		assertNull(gen.getModulesUpdate());
348 		assertEquals("git://example.com/sub", gen.getModulesUrl());
349 		assertNull(gen.getRepository());
350 		assertFalse(gen.next());
351 	}
352 
353 	@Test
354 	public void treeIdWithGitmodules() throws Exception {
355 		final ObjectId subId = ObjectId
356 				.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
357 		final String path = "sub";
358 
359 		final Config gitmodules = new Config();
360 		gitmodules.setString(CONFIG_SUBMODULE_SECTION, path, CONFIG_KEY_PATH,
361 				"sub");
362 		gitmodules.setString(CONFIG_SUBMODULE_SECTION, path, CONFIG_KEY_URL,
363 				"git://example.com/sub");
364 
365 		RevCommit commit = testDb.getRevWalk().parseCommit(testDb.commit()
366 				.noParents()
367 				.add(DOT_GIT_MODULES, gitmodules.toText())
368 				.edit(new PathEdit(path) {
369 
370 							public void apply(DirCacheEntry ent) {
371 								ent.setFileMode(FileMode.GITLINK);
372 								ent.setObjectId(subId);
373 							}
374 						})
375 				.create());
376 
377 		SubmoduleWalk gen = SubmoduleWalk.forPath(db, commit.getTree(), "sub");
378 		assertEquals(path, gen.getPath());
379 		assertEquals(subId, gen.getObjectId());
380 		assertEquals(new File(db.getWorkTree(), path), gen.getDirectory());
381 		assertNull(gen.getConfigUpdate());
382 		assertNull(gen.getConfigUrl());
383 		assertEquals("sub", gen.getModulesPath());
384 		assertNull(gen.getModulesUpdate());
385 		assertEquals("git://example.com/sub", gen.getModulesUrl());
386 		assertNull(gen.getRepository());
387 		assertFalse(gen.next());
388 	}
389 
390 	@Test
391 	public void testTreeIteratorWithGitmodules() throws Exception {
392 		final ObjectId subId = ObjectId
393 				.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
394 		final String path = "sub";
395 
396 		final Config gitmodules = new Config();
397 		gitmodules.setString(CONFIG_SUBMODULE_SECTION, path, CONFIG_KEY_PATH,
398 				"sub");
399 		gitmodules.setString(CONFIG_SUBMODULE_SECTION, path, CONFIG_KEY_URL,
400 				"git://example.com/sub");
401 
402 		RevCommit commit = testDb.getRevWalk().parseCommit(testDb.commit()
403 				.noParents()
404 				.add(DOT_GIT_MODULES, gitmodules.toText())
405 				.edit(new PathEdit(path) {
406 
407 							public void apply(DirCacheEntry ent) {
408 								ent.setFileMode(FileMode.GITLINK);
409 								ent.setObjectId(subId);
410 							}
411 						})
412 				.create());
413 
414 		final CanonicalTreeParser p = new CanonicalTreeParser();
415 		p.reset(testDb.getRevWalk().getObjectReader(), commit.getTree());
416 		SubmoduleWalk gen = SubmoduleWalk.forPath(db, p, "sub");
417 		assertEquals(path, gen.getPath());
418 		assertEquals(subId, gen.getObjectId());
419 		assertEquals(new File(db.getWorkTree(), path), gen.getDirectory());
420 		assertNull(gen.getConfigUpdate());
421 		assertNull(gen.getConfigUrl());
422 		assertEquals("sub", gen.getModulesPath());
423 		assertNull(gen.getModulesUpdate());
424 		assertEquals("git://example.com/sub", gen.getModulesUrl());
425 		assertNull(gen.getRepository());
426 		assertFalse(gen.next());
427 	}
428 }