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