View Javadoc
1   /*
2    * Copyright (C) 2010, 2013 Chris Aniszczyk <caniszczyk@gmail.com> and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  package org.eclipse.jgit.api;
11  
12  import static org.junit.Assert.assertEquals;
13  import static org.junit.Assert.assertNotEquals;
14  import static org.junit.Assert.assertNotNull;
15  import static org.junit.Assert.assertNull;
16  import static org.junit.Assert.assertTrue;
17  
18  import java.io.File;
19  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.List;
22  
23  import org.eclipse.jgit.junit.JGitTestUtil;
24  import org.eclipse.jgit.junit.RepositoryTestCase;
25  import org.eclipse.jgit.lib.Constants;
26  import org.eclipse.jgit.lib.ObjectId;
27  import org.eclipse.jgit.lib.Ref;
28  import org.eclipse.jgit.lib.RefUpdate;
29  import org.eclipse.jgit.lib.Repository;
30  import org.eclipse.jgit.lib.StoredConfig;
31  import org.eclipse.jgit.revwalk.RevCommit;
32  import org.eclipse.jgit.transport.FetchResult;
33  import org.eclipse.jgit.transport.RefSpec;
34  import org.eclipse.jgit.transport.RemoteConfig;
35  import org.eclipse.jgit.transport.TagOpt;
36  import org.eclipse.jgit.transport.TrackingRefUpdate;
37  import org.eclipse.jgit.transport.URIish;
38  import org.junit.Before;
39  import org.junit.Test;
40  
41  public class FetchCommandTest extends RepositoryTestCase {
42  
43  	private Git git;
44  	private Git remoteGit;
45  
46  	@Before
47  	public void setupRemoteRepository() throws Exception {
48  		git = new Git(db);
49  
50  		// create other repository
51  		Repository remoteRepository = createWorkRepository();
52  		remoteGit = new Git(remoteRepository);
53  
54  		// setup the first repository to fetch from the second repository
55  		final StoredConfig config = db.getConfig();
56  		RemoteConfig remoteConfig = new RemoteConfig(config, "test");
57  		URIish uri = new URIish(remoteRepository.getDirectory().toURI().toURL());
58  		remoteConfig.addURI(uri);
59  		remoteConfig.update(config);
60  		config.save();
61  	}
62  
63  	@Test
64  	public void testFetch() throws Exception {
65  
66  		// create some refs via commits and tag
67  		RevCommit commit = remoteGit.commit().setMessage("initial commit").call();
68  		Ref tagRef = remoteGit.tag().setName("tag").call();
69  
70  		git.fetch().setRemote("test")
71  				.setRefSpecs("refs/heads/master:refs/heads/x").call();
72  
73  		assertEquals(commit.getId(),
74  				db.resolve(commit.getId().getName() + "^{commit}"));
75  		assertEquals(tagRef.getObjectId(),
76  				db.resolve(tagRef.getObjectId().getName()));
77  	}
78  
79  	@Test
80  	public void testFetchHasRefLogForRemoteRef() throws Exception {
81  		// create an initial commit SHA1 for the default branch
82  		ObjectId defaultBranchSha1 = remoteGit.commit()
83  				.setMessage("initial commit").call().getId();
84  
85  		git.fetch().setRemote("test")
86  				.setRefSpecs("refs/heads/*:refs/remotes/origin/*").call();
87  
88  		List<Ref> allFetchedRefs = git.getRepository().getRefDatabase()
89  				.getRefs();
90  		assertEquals(allFetchedRefs.size(), 1);
91  		Ref remoteRef = allFetchedRefs.get(0);
92  
93  		assertTrue(remoteRef.getName().startsWith(Constants.R_REMOTES));
94  		assertEquals(defaultBranchSha1, remoteRef.getObjectId());
95  		assertNotNull(git.getRepository().getReflogReader(remoteRef.getName())
96  				.getLastEntry());
97  	}
98  
99  	@Test
100 	public void testForcedFetch() throws Exception {
101 		remoteGit.commit().setMessage("commit").call();
102 		remoteGit.commit().setMessage("commit2").call();
103 		git.fetch().setRemote("test")
104 				.setRefSpecs("refs/heads/master:refs/heads/master").call();
105 
106 		remoteGit.commit().setAmend(true).setMessage("amended").call();
107 		FetchResult res = git.fetch().setRemote("test")
108 				.setRefSpecs("refs/heads/master:refs/heads/master").call();
109 		assertEquals(RefUpdate.Result.REJECTED,
110 				res.getTrackingRefUpdate("refs/heads/master").getResult());
111 		res = git.fetch().setRemote("test")
112 				.setRefSpecs("refs/heads/master:refs/heads/master")
113 				.setForceUpdate(true).call();
114 		assertEquals(RefUpdate.Result.FORCED,
115 				res.getTrackingRefUpdate("refs/heads/master").getResult());
116 	}
117 
118 	@Test
119 	public void testFetchSimpleNegativeRefSpec() throws Exception {
120 		remoteGit.commit().setMessage("commit").call();
121 
122 		FetchResult res = git.fetch().setRemote("test")
123 				.setRefSpecs("refs/heads/master:refs/heads/test",
124 						"^:refs/heads/test")
125 				.call();
126 		assertNull(res.getTrackingRefUpdate("refs/heads/test"));
127 
128 		res = git.fetch().setRemote("test")
129 				.setRefSpecs("refs/heads/master:refs/heads/test",
130 						"^refs/heads/master")
131 				.call();
132 		assertNull(res.getTrackingRefUpdate("refs/heads/test"));
133 	}
134 
135 	@Test
136 	public void negativeRefSpecFilterBySource() throws Exception {
137 		remoteGit.commit().setMessage("commit").call();
138 		remoteGit.branchCreate().setName("test").call();
139 		remoteGit.commit().setMessage("commit1").call();
140 		remoteGit.branchCreate().setName("dev").call();
141 
142 		FetchResult res = git.fetch().setRemote("test")
143 				.setRefSpecs("refs/*:refs/origins/*", "^refs/*/test")
144 				.call();
145 		assertNotNull(res.getTrackingRefUpdate("refs/origins/heads/master"));
146 		assertNull(res.getTrackingRefUpdate("refs/origins/heads/test"));
147 		assertNotNull(res.getTrackingRefUpdate("refs/origins/heads/dev"));
148 	}
149 
150 	@Test
151 	public void negativeRefSpecFilterByDestination() throws Exception {
152 		remoteGit.commit().setMessage("commit").call();
153 		remoteGit.branchCreate().setName("meta").call();
154 		remoteGit.commit().setMessage("commit1").call();
155 		remoteGit.branchCreate().setName("data").call();
156 
157 		FetchResult res = git.fetch().setRemote("test")
158 				.setRefSpecs("refs/*:refs/secret/*", "^:refs/secret/*/meta")
159 				.call();
160 		assertNotNull(res.getTrackingRefUpdate("refs/secret/heads/master"));
161 		assertNull(res.getTrackingRefUpdate("refs/secret/heads/meta"));
162 		assertNotNull(res.getTrackingRefUpdate("refs/secret/heads/data"));
163 	}
164 
165 	@Test
166 	public void fetchAddsBranches() throws Exception {
167 		final String branch1 = "b1";
168 		final String branch2 = "b2";
169 		final String remoteBranch1 = "test/" + branch1;
170 		final String remoteBranch2 = "test/" + branch2;
171 		remoteGit.commit().setMessage("commit").call();
172 		Ref branchRef1 = remoteGit.branchCreate().setName(branch1).call();
173 		remoteGit.commit().setMessage("commit").call();
174 		Ref branchRef2 = remoteGit.branchCreate().setName(branch2).call();
175 
176 		String spec = "refs/heads/*:refs/remotes/test/*";
177 		git.fetch().setRemote("test").setRefSpecs(spec).call();
178 		assertEquals(branchRef1.getObjectId(), db.resolve(remoteBranch1));
179 		assertEquals(branchRef2.getObjectId(), db.resolve(remoteBranch2));
180 	}
181 
182 	@Test
183 	public void fetchDoesntDeleteBranches() throws Exception {
184 		final String branch1 = "b1";
185 		final String branch2 = "b2";
186 		final String remoteBranch1 = "test/" + branch1;
187 		final String remoteBranch2 = "test/" + branch2;
188 		remoteGit.commit().setMessage("commit").call();
189 		Ref branchRef1 = remoteGit.branchCreate().setName(branch1).call();
190 		remoteGit.commit().setMessage("commit").call();
191 		Ref branchRef2 = remoteGit.branchCreate().setName(branch2).call();
192 
193 		String spec = "refs/heads/*:refs/remotes/test/*";
194 		git.fetch().setRemote("test").setRefSpecs(spec).call();
195 		assertEquals(branchRef1.getObjectId(), db.resolve(remoteBranch1));
196 		assertEquals(branchRef2.getObjectId(), db.resolve(remoteBranch2));
197 
198 		remoteGit.branchDelete().setBranchNames(branch1).call();
199 		git.fetch().setRemote("test").setRefSpecs(spec).call();
200 		assertEquals(branchRef1.getObjectId(), db.resolve(remoteBranch1));
201 		assertEquals(branchRef2.getObjectId(), db.resolve(remoteBranch2));
202 	}
203 
204 	@Test
205 	public void fetchUpdatesBranches() throws Exception {
206 		final String branch1 = "b1";
207 		final String branch2 = "b2";
208 		final String remoteBranch1 = "test/" + branch1;
209 		final String remoteBranch2 = "test/" + branch2;
210 		remoteGit.commit().setMessage("commit").call();
211 		Ref branchRef1 = remoteGit.branchCreate().setName(branch1).call();
212 		remoteGit.commit().setMessage("commit").call();
213 		Ref branchRef2 = remoteGit.branchCreate().setName(branch2).call();
214 
215 		String spec = "refs/heads/*:refs/remotes/test/*";
216 		git.fetch().setRemote("test").setRefSpecs(spec).call();
217 		assertEquals(branchRef1.getObjectId(), db.resolve(remoteBranch1));
218 		assertEquals(branchRef2.getObjectId(), db.resolve(remoteBranch2));
219 
220 		remoteGit.commit().setMessage("commit").call();
221 		branchRef2 = remoteGit.branchCreate().setName(branch2).setForce(true).call();
222 		git.fetch().setRemote("test").setRefSpecs(spec).call();
223 		assertEquals(branchRef1.getObjectId(), db.resolve(remoteBranch1));
224 		assertEquals(branchRef2.getObjectId(), db.resolve(remoteBranch2));
225 	}
226 
227 	@Test
228 	public void fetchPrunesBranches() throws Exception {
229 		final String branch1 = "b1";
230 		final String branch2 = "b2";
231 		final String remoteBranch1 = "test/" + branch1;
232 		final String remoteBranch2 = "test/" + branch2;
233 		remoteGit.commit().setMessage("commit").call();
234 		Ref branchRef1 = remoteGit.branchCreate().setName(branch1).call();
235 		remoteGit.commit().setMessage("commit").call();
236 		Ref branchRef2 = remoteGit.branchCreate().setName(branch2).call();
237 
238 		String spec = "refs/heads/*:refs/remotes/test/*";
239 		git.fetch().setRemote("test").setRefSpecs(spec).call();
240 		assertEquals(branchRef1.getObjectId(), db.resolve(remoteBranch1));
241 		assertEquals(branchRef2.getObjectId(), db.resolve(remoteBranch2));
242 
243 		remoteGit.branchDelete().setBranchNames(branch1).call();
244 		git.fetch().setRemote("test").setRefSpecs(spec)
245 				.setRemoveDeletedRefs(true).call();
246 		assertNull(db.resolve(remoteBranch1));
247 		assertEquals(branchRef2.getObjectId(), db.resolve(remoteBranch2));
248 	}
249 
250 	@Test
251 	public void fetchShouldAutoFollowTag() throws Exception {
252 		remoteGit.commit().setMessage("commit").call();
253 		Ref tagRef = remoteGit.tag().setName("foo").call();
254 
255 		git.fetch().setRemote("test")
256 				.setRefSpecs("refs/heads/*:refs/remotes/origin/*")
257 				.setTagOpt(TagOpt.AUTO_FOLLOW).call();
258 
259 		assertEquals(tagRef.getObjectId(), db.resolve("foo"));
260 	}
261 
262 	@Test
263 	public void fetchShouldAutoFollowTagForFetchedObjects() throws Exception {
264 		remoteGit.commit().setMessage("commit").call();
265 		Ref tagRef = remoteGit.tag().setName("foo").call();
266 		remoteGit.commit().setMessage("commit2").call();
267 		git.fetch().setRemote("test")
268 				.setRefSpecs("refs/heads/*:refs/remotes/origin/*")
269 				.setTagOpt(TagOpt.AUTO_FOLLOW).call();
270 		assertEquals(tagRef.getObjectId(), db.resolve("foo"));
271 	}
272 
273 	@Test
274 	public void fetchShouldNotFetchTagsFromOtherBranches() throws Exception {
275 		remoteGit.commit().setMessage("commit").call();
276 		remoteGit.checkout().setName("other").setCreateBranch(true).call();
277 		remoteGit.commit().setMessage("commit2").call();
278 		remoteGit.tag().setName("foo").call();
279 		git.fetch().setRemote("test")
280 				.setRefSpecs("refs/heads/master:refs/remotes/origin/master")
281 				.setTagOpt(TagOpt.AUTO_FOLLOW).call();
282 		assertNull(db.resolve("foo"));
283 	}
284 
285 	@Test
286 	public void fetchWithUpdatedTagShouldNotTryToUpdateLocal() throws Exception {
287 		final String tagName = "foo";
288 		remoteGit.commit().setMessage("commit").call();
289 		Ref tagRef = remoteGit.tag().setName(tagName).call();
290 		ObjectId originalId = tagRef.getObjectId();
291 
292 		String spec = "refs/heads/*:refs/remotes/origin/*";
293 		git.fetch().setRemote("test").setRefSpecs(spec)
294 				.setTagOpt(TagOpt.AUTO_FOLLOW).call();
295 		assertEquals(originalId, db.resolve(tagName));
296 
297 		remoteGit.commit().setMessage("commit 2").call();
298 		remoteGit.tag().setName(tagName).setForceUpdate(true).call();
299 
300 		FetchResult result = git.fetch().setRemote("test").setRefSpecs(spec)
301 				.setTagOpt(TagOpt.AUTO_FOLLOW).call();
302 
303 		Collection<TrackingRefUpdate> refUpdates = result
304 				.getTrackingRefUpdates();
305 		assertEquals(1, refUpdates.size());
306 		TrackingRefUpdate update = refUpdates.iterator().next();
307 		assertEquals("refs/heads/master", update.getRemoteName());
308 
309 		assertEquals(originalId, db.resolve(tagName));
310 	}
311 
312 	@Test
313 	public void fetchWithExplicitTagsShouldUpdateLocal() throws Exception {
314 		final String tagName = "foo";
315 		remoteGit.commit().setMessage("commit").call();
316 		Ref tagRef1 = remoteGit.tag().setName(tagName).call();
317 
318 		String spec = "refs/heads/*:refs/remotes/origin/*";
319 		git.fetch().setRemote("test").setRefSpecs(spec)
320 				.setTagOpt(TagOpt.AUTO_FOLLOW).call();
321 		assertEquals(tagRef1.getObjectId(), db.resolve(tagName));
322 
323 		remoteGit.commit().setMessage("commit 2").call();
324 		Ref tagRef2 = remoteGit.tag().setName(tagName).setForceUpdate(true)
325 				.call();
326 
327 		FetchResult result = git.fetch().setRemote("test").setRefSpecs(spec)
328 				.setTagOpt(TagOpt.FETCH_TAGS).call();
329 		TrackingRefUpdate update = result.getTrackingRefUpdate(Constants.R_TAGS
330 				+ tagName);
331 		assertEquals(RefUpdate.Result.FORCED, update.getResult());
332 		assertEquals(tagRef2.getObjectId(), db.resolve(tagName));
333 	}
334 
335 	@Test
336 	public void testFetchWithPruneShouldKeepOriginHead() throws Exception {
337 		// Create a commit in the test repo.
338 		commitFile("foo", "foo", "master");
339 		// Produce a real clone of the git repo
340 		Git cloned = Git.cloneRepository()
341 				.setDirectory(createTempDirectory("testCloneRepository"))
342 				.setURI("file://"
343 						+ git.getRepository().getWorkTree().getAbsolutePath())
344 				.call();
345 		assertNotNull(cloned);
346 		Repository clonedRepo = cloned.getRepository();
347 		addRepoToClose(clonedRepo);
348 		ObjectId originMasterId = clonedRepo
349 				.resolve("refs/remotes/origin/master");
350 		assertNotNull("Should have origin/master", originMasterId);
351 		assertNotEquals("origin/master should not be zero ID",
352 				ObjectId.zeroId(), originMasterId);
353 		// Canonical git creates origin/HEAD; JGit (for now) doesn't. Let's
354 		// pretend we did the clone via command-line git.
355 		ObjectId originHeadId = clonedRepo.resolve("refs/remotes/origin/HEAD");
356 		if (originHeadId == null) {
357 			JGitTestUtil.write(
358 					new File(clonedRepo.getDirectory(),
359 							"refs/remotes/origin/HEAD"),
360 					"ref: refs/remotes/origin/master\n");
361 			originHeadId = clonedRepo.resolve("refs/remotes/origin/HEAD");
362 		}
363 		assertEquals("Should have origin/HEAD", originMasterId, originHeadId);
364 		FetchResult result = cloned.fetch().setRemote("origin")
365 				.setRemoveDeletedRefs(true).call();
366 		assertTrue("Fetch after clone should be up-to-date",
367 				result.getTrackingRefUpdates().isEmpty());
368 		assertEquals("origin/master should still exist", originMasterId,
369 				clonedRepo.resolve("refs/remotes/origin/master"));
370 		assertEquals("origin/HEAD should be unchanged", originHeadId,
371 				clonedRepo.resolve("refs/remotes/origin/HEAD"));
372 	}
373 
374 	@Test
375 	public void fetchAddRefsWithDuplicateRefspec() throws Exception {
376 		final String branchName = "branch";
377 		final String remoteBranchName = "test/" + branchName;
378 		remoteGit.commit().setMessage("commit").call();
379 		Ref branchRef = remoteGit.branchCreate().setName(branchName).call();
380 
381 		final String spec1 = "+refs/heads/*:refs/remotes/test/*";
382 		final String spec2 = "refs/heads/*:refs/remotes/test/*";
383 		final StoredConfig config = db.getConfig();
384 		RemoteConfig remoteConfig = new RemoteConfig(config, "test");
385 		remoteConfig.addFetchRefSpec(new RefSpec(spec1));
386 		remoteConfig.addFetchRefSpec(new RefSpec(spec2));
387 		remoteConfig.update(config);
388 
389 		git.fetch().setRemote("test").setRefSpecs(spec1).call();
390 		assertEquals(branchRef.getObjectId(), db.resolve(remoteBranchName));
391 	}
392 
393 	@Test
394 	public void fetchPruneRefsWithDuplicateRefspec()
395 			throws Exception {
396 		final String branchName = "branch";
397 		final String remoteBranchName = "test/" + branchName;
398 		remoteGit.commit().setMessage("commit").call();
399 		Ref branchRef = remoteGit.branchCreate().setName(branchName).call();
400 
401 		final String spec1 = "+refs/heads/*:refs/remotes/test/*";
402 		final String spec2 = "refs/heads/*:refs/remotes/test/*";
403 		final StoredConfig config = db.getConfig();
404 		RemoteConfig remoteConfig = new RemoteConfig(config, "test");
405 		remoteConfig.addFetchRefSpec(new RefSpec(spec1));
406 		remoteConfig.addFetchRefSpec(new RefSpec(spec2));
407 		remoteConfig.update(config);
408 
409 		git.fetch().setRemote("test").setRefSpecs(spec1).call();
410 		assertEquals(branchRef.getObjectId(), db.resolve(remoteBranchName));
411 
412 		remoteGit.branchDelete().setBranchNames(branchName).call();
413 		git.fetch().setRemote("test").setRefSpecs(spec1)
414 				.setRemoveDeletedRefs(true).call();
415 		assertNull(db.resolve(remoteBranchName));
416 	}
417 
418 	@Test
419 	public void fetchUpdateRefsWithDuplicateRefspec() throws Exception {
420 		final String tagName = "foo";
421 		remoteGit.commit().setMessage("commit").call();
422 		Ref tagRef1 = remoteGit.tag().setName(tagName).call();
423 		List<RefSpec> refSpecs = new ArrayList<>();
424 		refSpecs.add(new RefSpec("+refs/heads/*:refs/remotes/origin/*"));
425 		refSpecs.add(new RefSpec("+refs/tags/*:refs/tags/*"));
426 		// Updating tags via the RefSpecs and setting TagOpt.FETCH_TAGS (or
427 		// AUTO_FOLLOW) will result internally in *two* updates for the same
428 		// ref.
429 		git.fetch().setRemote("test").setRefSpecs(refSpecs)
430 				.setTagOpt(TagOpt.AUTO_FOLLOW).call();
431 		assertEquals(tagRef1.getObjectId(), db.resolve(tagName));
432 
433 		remoteGit.commit().setMessage("commit 2").call();
434 		Ref tagRef2 = remoteGit.tag().setName(tagName).setForceUpdate(true)
435 				.call();
436 		FetchResult result = git.fetch().setRemote("test").setRefSpecs(refSpecs)
437 				.setTagOpt(TagOpt.FETCH_TAGS).call();
438 		assertEquals(2, result.getTrackingRefUpdates().size());
439 		TrackingRefUpdate update = result
440 				.getTrackingRefUpdate(Constants.R_TAGS + tagName);
441 		assertEquals(RefUpdate.Result.FORCED, update.getResult());
442 		assertEquals(tagRef2.getObjectId(), db.resolve(tagName));
443 	}
444 }