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 testForcedFetch() throws Exception {
81  		remoteGit.commit().setMessage("commit").call();
82  		remoteGit.commit().setMessage("commit2").call();
83  		git.fetch().setRemote("test")
84  				.setRefSpecs("refs/heads/master:refs/heads/master").call();
85  
86  		remoteGit.commit().setAmend(true).setMessage("amended").call();
87  		FetchResult res = git.fetch().setRemote("test")
88  				.setRefSpecs("refs/heads/master:refs/heads/master").call();
89  		assertEquals(RefUpdate.Result.REJECTED,
90  				res.getTrackingRefUpdate("refs/heads/master").getResult());
91  		res = git.fetch().setRemote("test")
92  				.setRefSpecs("refs/heads/master:refs/heads/master")
93  				.setForceUpdate(true).call();
94  		assertEquals(RefUpdate.Result.FORCED,
95  				res.getTrackingRefUpdate("refs/heads/master").getResult());
96  	}
97  
98  	@Test
99  	public void testFetchSimpleNegativeRefSpec() throws Exception {
100 		remoteGit.commit().setMessage("commit").call();
101 
102 		FetchResult res = git.fetch().setRemote("test")
103 				.setRefSpecs("refs/heads/master:refs/heads/test",
104 						"^:refs/heads/test")
105 				.call();
106 		assertNull(res.getTrackingRefUpdate("refs/heads/test"));
107 
108 		res = git.fetch().setRemote("test")
109 				.setRefSpecs("refs/heads/master:refs/heads/test",
110 						"^refs/heads/master")
111 				.call();
112 		assertNull(res.getTrackingRefUpdate("refs/heads/test"));
113 	}
114 
115 	@Test
116 	public void negativeRefSpecFilterBySource() throws Exception {
117 		remoteGit.commit().setMessage("commit").call();
118 		remoteGit.branchCreate().setName("test").call();
119 		remoteGit.commit().setMessage("commit1").call();
120 		remoteGit.branchCreate().setName("dev").call();
121 
122 		FetchResult res = git.fetch().setRemote("test")
123 				.setRefSpecs("refs/*:refs/origins/*", "^refs/*/test")
124 				.call();
125 		assertNotNull(res.getTrackingRefUpdate("refs/origins/heads/master"));
126 		assertNull(res.getTrackingRefUpdate("refs/origins/heads/test"));
127 		assertNotNull(res.getTrackingRefUpdate("refs/origins/heads/dev"));
128 	}
129 
130 	@Test
131 	public void negativeRefSpecFilterByDestination() throws Exception {
132 		remoteGit.commit().setMessage("commit").call();
133 		remoteGit.branchCreate().setName("meta").call();
134 		remoteGit.commit().setMessage("commit1").call();
135 		remoteGit.branchCreate().setName("data").call();
136 
137 		FetchResult res = git.fetch().setRemote("test")
138 				.setRefSpecs("refs/*:refs/secret/*", "^:refs/secret/*/meta")
139 				.call();
140 		assertNotNull(res.getTrackingRefUpdate("refs/secret/heads/master"));
141 		assertNull(res.getTrackingRefUpdate("refs/secret/heads/meta"));
142 		assertNotNull(res.getTrackingRefUpdate("refs/secret/heads/data"));
143 	}
144 
145 	@Test
146 	public void fetchAddsBranches() throws Exception {
147 		final String branch1 = "b1";
148 		final String branch2 = "b2";
149 		final String remoteBranch1 = "test/" + branch1;
150 		final String remoteBranch2 = "test/" + branch2;
151 		remoteGit.commit().setMessage("commit").call();
152 		Ref branchRef1 = remoteGit.branchCreate().setName(branch1).call();
153 		remoteGit.commit().setMessage("commit").call();
154 		Ref branchRef2 = remoteGit.branchCreate().setName(branch2).call();
155 
156 		String spec = "refs/heads/*:refs/remotes/test/*";
157 		git.fetch().setRemote("test").setRefSpecs(spec).call();
158 		assertEquals(branchRef1.getObjectId(), db.resolve(remoteBranch1));
159 		assertEquals(branchRef2.getObjectId(), db.resolve(remoteBranch2));
160 	}
161 
162 	@Test
163 	public void fetchDoesntDeleteBranches() throws Exception {
164 		final String branch1 = "b1";
165 		final String branch2 = "b2";
166 		final String remoteBranch1 = "test/" + branch1;
167 		final String remoteBranch2 = "test/" + branch2;
168 		remoteGit.commit().setMessage("commit").call();
169 		Ref branchRef1 = remoteGit.branchCreate().setName(branch1).call();
170 		remoteGit.commit().setMessage("commit").call();
171 		Ref branchRef2 = remoteGit.branchCreate().setName(branch2).call();
172 
173 		String spec = "refs/heads/*:refs/remotes/test/*";
174 		git.fetch().setRemote("test").setRefSpecs(spec).call();
175 		assertEquals(branchRef1.getObjectId(), db.resolve(remoteBranch1));
176 		assertEquals(branchRef2.getObjectId(), db.resolve(remoteBranch2));
177 
178 		remoteGit.branchDelete().setBranchNames(branch1).call();
179 		git.fetch().setRemote("test").setRefSpecs(spec).call();
180 		assertEquals(branchRef1.getObjectId(), db.resolve(remoteBranch1));
181 		assertEquals(branchRef2.getObjectId(), db.resolve(remoteBranch2));
182 	}
183 
184 	@Test
185 	public void fetchUpdatesBranches() throws Exception {
186 		final String branch1 = "b1";
187 		final String branch2 = "b2";
188 		final String remoteBranch1 = "test/" + branch1;
189 		final String remoteBranch2 = "test/" + branch2;
190 		remoteGit.commit().setMessage("commit").call();
191 		Ref branchRef1 = remoteGit.branchCreate().setName(branch1).call();
192 		remoteGit.commit().setMessage("commit").call();
193 		Ref branchRef2 = remoteGit.branchCreate().setName(branch2).call();
194 
195 		String spec = "refs/heads/*:refs/remotes/test/*";
196 		git.fetch().setRemote("test").setRefSpecs(spec).call();
197 		assertEquals(branchRef1.getObjectId(), db.resolve(remoteBranch1));
198 		assertEquals(branchRef2.getObjectId(), db.resolve(remoteBranch2));
199 
200 		remoteGit.commit().setMessage("commit").call();
201 		branchRef2 = remoteGit.branchCreate().setName(branch2).setForce(true).call();
202 		git.fetch().setRemote("test").setRefSpecs(spec).call();
203 		assertEquals(branchRef1.getObjectId(), db.resolve(remoteBranch1));
204 		assertEquals(branchRef2.getObjectId(), db.resolve(remoteBranch2));
205 	}
206 
207 	@Test
208 	public void fetchPrunesBranches() throws Exception {
209 		final String branch1 = "b1";
210 		final String branch2 = "b2";
211 		final String remoteBranch1 = "test/" + branch1;
212 		final String remoteBranch2 = "test/" + branch2;
213 		remoteGit.commit().setMessage("commit").call();
214 		Ref branchRef1 = remoteGit.branchCreate().setName(branch1).call();
215 		remoteGit.commit().setMessage("commit").call();
216 		Ref branchRef2 = remoteGit.branchCreate().setName(branch2).call();
217 
218 		String spec = "refs/heads/*:refs/remotes/test/*";
219 		git.fetch().setRemote("test").setRefSpecs(spec).call();
220 		assertEquals(branchRef1.getObjectId(), db.resolve(remoteBranch1));
221 		assertEquals(branchRef2.getObjectId(), db.resolve(remoteBranch2));
222 
223 		remoteGit.branchDelete().setBranchNames(branch1).call();
224 		git.fetch().setRemote("test").setRefSpecs(spec)
225 				.setRemoveDeletedRefs(true).call();
226 		assertNull(db.resolve(remoteBranch1));
227 		assertEquals(branchRef2.getObjectId(), db.resolve(remoteBranch2));
228 	}
229 
230 	@Test
231 	public void fetchShouldAutoFollowTag() throws Exception {
232 		remoteGit.commit().setMessage("commit").call();
233 		Ref tagRef = remoteGit.tag().setName("foo").call();
234 
235 		git.fetch().setRemote("test")
236 				.setRefSpecs("refs/heads/*:refs/remotes/origin/*")
237 				.setTagOpt(TagOpt.AUTO_FOLLOW).call();
238 
239 		assertEquals(tagRef.getObjectId(), db.resolve("foo"));
240 	}
241 
242 	@Test
243 	public void fetchShouldAutoFollowTagForFetchedObjects() throws Exception {
244 		remoteGit.commit().setMessage("commit").call();
245 		Ref tagRef = remoteGit.tag().setName("foo").call();
246 		remoteGit.commit().setMessage("commit2").call();
247 		git.fetch().setRemote("test")
248 				.setRefSpecs("refs/heads/*:refs/remotes/origin/*")
249 				.setTagOpt(TagOpt.AUTO_FOLLOW).call();
250 		assertEquals(tagRef.getObjectId(), db.resolve("foo"));
251 	}
252 
253 	@Test
254 	public void fetchShouldNotFetchTagsFromOtherBranches() throws Exception {
255 		remoteGit.commit().setMessage("commit").call();
256 		remoteGit.checkout().setName("other").setCreateBranch(true).call();
257 		remoteGit.commit().setMessage("commit2").call();
258 		remoteGit.tag().setName("foo").call();
259 		git.fetch().setRemote("test")
260 				.setRefSpecs("refs/heads/master:refs/remotes/origin/master")
261 				.setTagOpt(TagOpt.AUTO_FOLLOW).call();
262 		assertNull(db.resolve("foo"));
263 	}
264 
265 	@Test
266 	public void fetchWithUpdatedTagShouldNotTryToUpdateLocal() throws Exception {
267 		final String tagName = "foo";
268 		remoteGit.commit().setMessage("commit").call();
269 		Ref tagRef = remoteGit.tag().setName(tagName).call();
270 		ObjectId originalId = tagRef.getObjectId();
271 
272 		String spec = "refs/heads/*:refs/remotes/origin/*";
273 		git.fetch().setRemote("test").setRefSpecs(spec)
274 				.setTagOpt(TagOpt.AUTO_FOLLOW).call();
275 		assertEquals(originalId, db.resolve(tagName));
276 
277 		remoteGit.commit().setMessage("commit 2").call();
278 		remoteGit.tag().setName(tagName).setForceUpdate(true).call();
279 
280 		FetchResult result = git.fetch().setRemote("test").setRefSpecs(spec)
281 				.setTagOpt(TagOpt.AUTO_FOLLOW).call();
282 
283 		Collection<TrackingRefUpdate> refUpdates = result
284 				.getTrackingRefUpdates();
285 		assertEquals(1, refUpdates.size());
286 		TrackingRefUpdate update = refUpdates.iterator().next();
287 		assertEquals("refs/heads/master", update.getRemoteName());
288 
289 		assertEquals(originalId, db.resolve(tagName));
290 	}
291 
292 	@Test
293 	public void fetchWithExplicitTagsShouldUpdateLocal() throws Exception {
294 		final String tagName = "foo";
295 		remoteGit.commit().setMessage("commit").call();
296 		Ref tagRef1 = remoteGit.tag().setName(tagName).call();
297 
298 		String spec = "refs/heads/*:refs/remotes/origin/*";
299 		git.fetch().setRemote("test").setRefSpecs(spec)
300 				.setTagOpt(TagOpt.AUTO_FOLLOW).call();
301 		assertEquals(tagRef1.getObjectId(), db.resolve(tagName));
302 
303 		remoteGit.commit().setMessage("commit 2").call();
304 		Ref tagRef2 = remoteGit.tag().setName(tagName).setForceUpdate(true)
305 				.call();
306 
307 		FetchResult result = git.fetch().setRemote("test").setRefSpecs(spec)
308 				.setTagOpt(TagOpt.FETCH_TAGS).call();
309 		TrackingRefUpdate update = result.getTrackingRefUpdate(Constants.R_TAGS
310 				+ tagName);
311 		assertEquals(RefUpdate.Result.FORCED, update.getResult());
312 		assertEquals(tagRef2.getObjectId(), db.resolve(tagName));
313 	}
314 
315 	@Test
316 	public void testFetchWithPruneShouldKeepOriginHead() throws Exception {
317 		// Create a commit in the test repo.
318 		commitFile("foo", "foo", "master");
319 		// Produce a real clone of the git repo
320 		Git cloned = Git.cloneRepository()
321 				.setDirectory(createTempDirectory("testCloneRepository"))
322 				.setURI("file://"
323 						+ git.getRepository().getWorkTree().getAbsolutePath())
324 				.call();
325 		assertNotNull(cloned);
326 		Repository clonedRepo = cloned.getRepository();
327 		addRepoToClose(clonedRepo);
328 		ObjectId originMasterId = clonedRepo
329 				.resolve("refs/remotes/origin/master");
330 		assertNotNull("Should have origin/master", originMasterId);
331 		assertNotEquals("origin/master should not be zero ID",
332 				ObjectId.zeroId(), originMasterId);
333 		// Canonical git creates origin/HEAD; JGit (for now) doesn't. Let's
334 		// pretend we did the clone via command-line git.
335 		ObjectId originHeadId = clonedRepo.resolve("refs/remotes/origin/HEAD");
336 		if (originHeadId == null) {
337 			JGitTestUtil.write(
338 					new File(clonedRepo.getDirectory(),
339 							"refs/remotes/origin/HEAD"),
340 					"ref: refs/remotes/origin/master\n");
341 			originHeadId = clonedRepo.resolve("refs/remotes/origin/HEAD");
342 		}
343 		assertEquals("Should have origin/HEAD", originMasterId, originHeadId);
344 		FetchResult result = cloned.fetch().setRemote("origin")
345 				.setRemoveDeletedRefs(true).call();
346 		assertTrue("Fetch after clone should be up-to-date",
347 				result.getTrackingRefUpdates().isEmpty());
348 		assertEquals("origin/master should still exist", originMasterId,
349 				clonedRepo.resolve("refs/remotes/origin/master"));
350 		assertEquals("origin/HEAD should be unchanged", originHeadId,
351 				clonedRepo.resolve("refs/remotes/origin/HEAD"));
352 	}
353 
354 	@Test
355 	public void fetchAddRefsWithDuplicateRefspec() throws Exception {
356 		final String branchName = "branch";
357 		final String remoteBranchName = "test/" + branchName;
358 		remoteGit.commit().setMessage("commit").call();
359 		Ref branchRef = remoteGit.branchCreate().setName(branchName).call();
360 
361 		final String spec1 = "+refs/heads/*:refs/remotes/test/*";
362 		final String spec2 = "refs/heads/*:refs/remotes/test/*";
363 		final StoredConfig config = db.getConfig();
364 		RemoteConfig remoteConfig = new RemoteConfig(config, "test");
365 		remoteConfig.addFetchRefSpec(new RefSpec(spec1));
366 		remoteConfig.addFetchRefSpec(new RefSpec(spec2));
367 		remoteConfig.update(config);
368 
369 		git.fetch().setRemote("test").setRefSpecs(spec1).call();
370 		assertEquals(branchRef.getObjectId(), db.resolve(remoteBranchName));
371 	}
372 
373 	@Test
374 	public void fetchPruneRefsWithDuplicateRefspec()
375 			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 		remoteGit.branchDelete().setBranchNames(branchName).call();
393 		git.fetch().setRemote("test").setRefSpecs(spec1)
394 				.setRemoveDeletedRefs(true).call();
395 		assertNull(db.resolve(remoteBranchName));
396 	}
397 
398 	@Test
399 	public void fetchUpdateRefsWithDuplicateRefspec() throws Exception {
400 		final String tagName = "foo";
401 		remoteGit.commit().setMessage("commit").call();
402 		Ref tagRef1 = remoteGit.tag().setName(tagName).call();
403 		List<RefSpec> refSpecs = new ArrayList<>();
404 		refSpecs.add(new RefSpec("+refs/heads/*:refs/remotes/origin/*"));
405 		refSpecs.add(new RefSpec("+refs/tags/*:refs/tags/*"));
406 		// Updating tags via the RefSpecs and setting TagOpt.FETCH_TAGS (or
407 		// AUTO_FOLLOW) will result internally in *two* updates for the same
408 		// ref.
409 		git.fetch().setRemote("test").setRefSpecs(refSpecs)
410 				.setTagOpt(TagOpt.AUTO_FOLLOW).call();
411 		assertEquals(tagRef1.getObjectId(), db.resolve(tagName));
412 
413 		remoteGit.commit().setMessage("commit 2").call();
414 		Ref tagRef2 = remoteGit.tag().setName(tagName).setForceUpdate(true)
415 				.call();
416 		FetchResult result = git.fetch().setRemote("test").setRefSpecs(refSpecs)
417 				.setTagOpt(TagOpt.FETCH_TAGS).call();
418 		assertEquals(2, result.getTrackingRefUpdates().size());
419 		TrackingRefUpdate update = result
420 				.getTrackingRefUpdate(Constants.R_TAGS + tagName);
421 		assertEquals(RefUpdate.Result.FORCED, update.getResult());
422 		assertEquals(tagRef2.getObjectId(), db.resolve(tagName));
423 	}
424 }