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