View Javadoc
1   /*
2    * Copyright (C) 2010, 2014 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.assertNotNull;
47  import static org.junit.Assert.assertTrue;
48  import static org.junit.Assert.fail;
49  
50  import java.io.IOException;
51  import java.net.URISyntaxException;
52  import java.util.Properties;
53  
54  import org.eclipse.jgit.api.errors.GitAPIException;
55  import org.eclipse.jgit.api.errors.JGitInternalException;
56  import org.eclipse.jgit.api.errors.TransportException;
57  import org.eclipse.jgit.errors.MissingObjectException;
58  import org.eclipse.jgit.junit.RepositoryTestCase;
59  import org.eclipse.jgit.lib.Ref;
60  import org.eclipse.jgit.lib.RefUpdate;
61  import org.eclipse.jgit.lib.Repository;
62  import org.eclipse.jgit.lib.StoredConfig;
63  import org.eclipse.jgit.revwalk.RevCommit;
64  import org.eclipse.jgit.transport.PushResult;
65  import org.eclipse.jgit.transport.RefSpec;
66  import org.eclipse.jgit.transport.RemoteConfig;
67  import org.eclipse.jgit.transport.TrackingRefUpdate;
68  import org.eclipse.jgit.transport.URIish;
69  import org.junit.Test;
70  
71  public class PushCommandTest extends RepositoryTestCase {
72  
73  	@Test
74  	public void testPush() throws JGitInternalException, IOException,
75  			GitAPIException, URISyntaxException {
76  
77  		// create other repository
78  		Repository db2 = createWorkRepository();
79  
80  		// setup the first repository
81  		final StoredConfig config = db.getConfig();
82  		RemoteConfig remoteConfig = new RemoteConfig(config, "test");
83  		URIish uri = new URIish(db2.getDirectory().toURI().toURL());
84  		remoteConfig.addURI(uri);
85  		remoteConfig.update(config);
86  		config.save();
87  
88  		Git git1 = new Git(db);
89  		// create some refs via commits and tag
90  		RevCommit commit = git1.commit().setMessage("initial commit").call();
91  		Ref tagRef = git1.tag().setName("tag").call();
92  
93  		try {
94  			db2.resolve(commit.getId().getName() + "^{commit}");
95  			fail("id shouldn't exist yet");
96  		} catch (MissingObjectException e) {
97  			// we should get here
98  		}
99  
100 		RefSpec spec = new RefSpec("refs/heads/master:refs/heads/x");
101 		git1.push().setRemote("test").setRefSpecs(spec)
102 				.call();
103 
104 		assertEquals(commit.getId(),
105 				db2.resolve(commit.getId().getName() + "^{commit}"));
106 		assertEquals(tagRef.getObjectId(),
107 				db2.resolve(tagRef.getObjectId().getName()));
108 	}
109 
110 	@Test
111 	public void testTrackingUpdate() throws Exception {
112 		Repository db2 = createBareRepository();
113 
114 		String remote = "origin";
115 		String branch = "refs/heads/master";
116 		String trackingBranch = "refs/remotes/" + remote + "/master";
117 
118 		Git git = new Git(db);
119 
120 		RevCommit commit1 = git.commit().setMessage("Initial commit")
121 				.call();
122 
123 		RefUpdate branchRefUpdate = db.updateRef(branch);
124 		branchRefUpdate.setNewObjectId(commit1.getId());
125 		branchRefUpdate.update();
126 
127 		RefUpdate trackingBranchRefUpdate = db.updateRef(trackingBranch);
128 		trackingBranchRefUpdate.setNewObjectId(commit1.getId());
129 		trackingBranchRefUpdate.update();
130 
131 		final StoredConfig config = db.getConfig();
132 		RemoteConfig remoteConfig = new RemoteConfig(config, remote);
133 		URIish uri = new URIish(db2.getDirectory().toURI().toURL());
134 		remoteConfig.addURI(uri);
135 		remoteConfig.addFetchRefSpec(new RefSpec("+refs/heads/*:refs/remotes/"
136 				+ remote + "/*"));
137 		remoteConfig.update(config);
138 		config.save();
139 
140 
141 		RevCommit commit2 = git.commit().setMessage("Commit to push").call();
142 
143 		RefSpec spec = new RefSpec(branch + ":" + branch);
144 		Iterable<PushResult> resultIterable = git.push().setRemote(remote)
145 				.setRefSpecs(spec).call();
146 
147 		PushResult result = resultIterable.iterator().next();
148 		TrackingRefUpdate trackingRefUpdate = result
149 				.getTrackingRefUpdate(trackingBranch);
150 
151 		assertNotNull(trackingRefUpdate);
152 		assertEquals(trackingBranch, trackingRefUpdate.getLocalName());
153 		assertEquals(branch, trackingRefUpdate.getRemoteName());
154 		assertEquals(commit2.getId(), trackingRefUpdate.getNewObjectId());
155 		assertEquals(commit2.getId(), db.resolve(trackingBranch));
156 		assertEquals(commit2.getId(), db2.resolve(branch));
157 	}
158 
159 	/**
160 	 * Check that pushes over file protocol lead to appropriate ref-updates.
161 	 *
162 	 * @throws Exception
163 	 */
164 	@Test
165 	public void testPushRefUpdate() throws Exception {
166 		Git git = new Git(db);
167 		Git git2 = new Git(createBareRepository());
168 
169 		final StoredConfig config = git.getRepository().getConfig();
170 		RemoteConfig remoteConfig = new RemoteConfig(config, "test");
171 		URIish uri = new URIish(git2.getRepository().getDirectory().toURI()
172 				.toURL());
173 		remoteConfig.addURI(uri);
174 		remoteConfig.addPushRefSpec(new RefSpec("+refs/heads/*:refs/heads/*"));
175 		remoteConfig.update(config);
176 		config.save();
177 
178 		writeTrashFile("f", "content of f");
179 		git.add().addFilepattern("f").call();
180 		RevCommit commit = git.commit().setMessage("adding f").call();
181 
182 		assertEquals(null, git2.getRepository().resolve("refs/heads/master"));
183 		git.push().setRemote("test").call();
184 		assertEquals(commit.getId(),
185 				git2.getRepository().resolve("refs/heads/master"));
186 
187 		git.branchCreate().setName("refs/heads/test").call();
188 		git.checkout().setName("refs/heads/test").call();
189 
190 
191 		for (int i = 0; i < 6; i++) {
192 			writeTrashFile("f" + i, "content of f" + i);
193 			git.add().addFilepattern("f" + i).call();
194 			commit = git.commit().setMessage("adding f" + i).call();
195 			git.push().setRemote("test").call();
196 			git2.getRepository().getAllRefs();
197 			assertEquals("failed to update on attempt " + i, commit.getId(),
198 					git2.getRepository().resolve("refs/heads/test"));
199 
200 		}
201 	}
202 
203 	/**
204 	 * Check that the push refspec is read from config.
205 	 *
206 	 * @throws Exception
207 	 */
208 	@Test
209 	public void testPushWithRefSpecFromConfig() throws Exception {
210 		Git git = new Git(db);
211 		Git git2 = new Git(createBareRepository());
212 
213 		final StoredConfig config = git.getRepository().getConfig();
214 		RemoteConfig remoteConfig = new RemoteConfig(config, "test");
215 		URIish uri = new URIish(git2.getRepository().getDirectory().toURI()
216 				.toURL());
217 		remoteConfig.addURI(uri);
218 		remoteConfig.addPushRefSpec(new RefSpec("HEAD:refs/heads/newbranch"));
219 		remoteConfig.update(config);
220 		config.save();
221 
222 		writeTrashFile("f", "content of f");
223 		git.add().addFilepattern("f").call();
224 		RevCommit commit = git.commit().setMessage("adding f").call();
225 
226 		assertEquals(null, git2.getRepository().resolve("refs/heads/master"));
227 		git.push().setRemote("test").call();
228 		assertEquals(commit.getId(),
229 				git2.getRepository().resolve("refs/heads/newbranch"));
230 
231 
232 	}
233 
234 	/**
235 	 * Check that only HEAD is pushed if no refspec is given.
236 	 *
237 	 * @throws Exception
238 	 */
239 	@Test
240 	public void testPushWithoutPushRefSpec() throws Exception {
241 		Git git = new Git(db);
242 		Git git2 = new Git(createBareRepository());
243 
244 		final StoredConfig config = git.getRepository().getConfig();
245 		RemoteConfig remoteConfig = new RemoteConfig(config, "test");
246 		URIish uri = new URIish(git2.getRepository().getDirectory().toURI()
247 				.toURL());
248 		remoteConfig.addURI(uri);
249 		remoteConfig.addFetchRefSpec(new RefSpec(
250 				"+refs/heads/*:refs/remotes/origin/*"));
251 		remoteConfig.update(config);
252 		config.save();
253 
254 		writeTrashFile("f", "content of f");
255 		git.add().addFilepattern("f").call();
256 		RevCommit commit = git.commit().setMessage("adding f").call();
257 
258 		git.checkout().setName("not-pushed").setCreateBranch(true).call();
259 		git.checkout().setName("branchtopush").setCreateBranch(true).call();
260 
261 		assertEquals(null,
262 				git2.getRepository().resolve("refs/heads/branchtopush"));
263 		assertEquals(null, git2.getRepository()
264 				.resolve("refs/heads/not-pushed"));
265 		assertEquals(null, git2.getRepository().resolve("refs/heads/master"));
266 		git.push().setRemote("test").call();
267 		assertEquals(commit.getId(),
268 				git2.getRepository().resolve("refs/heads/branchtopush"));
269 		assertEquals(null, git2.getRepository()
270 				.resolve("refs/heads/not-pushed"));
271 		assertEquals(null, git2.getRepository().resolve("refs/heads/master"));
272 
273 	}
274 
275 	/**
276 	 * Check that missing refs don't cause errors during push
277 	 *
278 	 * @throws Exception
279 	 */
280 	@Test
281 	public void testPushAfterGC() throws Exception {
282 		// create other repository
283 		Repository db2 = createWorkRepository();
284 
285 		// setup the first repository
286 		final StoredConfig config = db.getConfig();
287 		RemoteConfig remoteConfig = new RemoteConfig(config, "test");
288 		URIish uri = new URIish(db2.getDirectory().toURI().toURL());
289 		remoteConfig.addURI(uri);
290 		remoteConfig.update(config);
291 		config.save();
292 
293 		Git git1 = new Git(db);
294 		Git git2 = new Git(db2);
295 
296 		// push master (with a new commit) to the remote
297 		git1.commit().setMessage("initial commit").call();
298 
299 		RefSpec spec = new RefSpec("refs/heads/*:refs/heads/*");
300 		git1.push().setRemote("test").setRefSpecs(spec).call();
301 
302 		// create an unrelated ref and a commit on our remote
303 		git2.branchCreate().setName("refs/heads/other").call();
304 		git2.checkout().setName("refs/heads/other").call();
305 
306 		writeTrashFile("a", "content of a");
307 		git2.add().addFilepattern("a").call();
308 		RevCommit commit2 = git2.commit().setMessage("adding a").call();
309 
310 		// run a gc to ensure we have a bitmap index
311 		Properties res = git1.gc().setExpire(null).call();
312 		assertEquals(7, res.size());
313 
314 		// create another commit so we have something else to push
315 		writeTrashFile("b", "content of b");
316 		git1.add().addFilepattern("b").call();
317 		RevCommit commit3 = git1.commit().setMessage("adding b").call();
318 
319 		try {
320 			// Re-run the push.  Failure may happen here.
321 			git1.push().setRemote("test").setRefSpecs(spec).call();
322 		} catch (TransportException e) {
323 			assertTrue("should be caused by a MissingObjectException", e
324 					.getCause().getCause() instanceof MissingObjectException);
325 			fail("caught MissingObjectException for a change we don't have");
326 		}
327 
328 		// Remote will have both a and b.  Master will have only b
329 		try {
330 			db.resolve(commit2.getId().getName() + "^{commit}");
331 			fail("id shouldn't exist locally");
332 		} catch (MissingObjectException e) {
333 			// we should get here
334 		}
335 		assertEquals(commit2.getId(),
336 				db2.resolve(commit2.getId().getName() + "^{commit}"));
337 		assertEquals(commit3.getId(),
338 				db2.resolve(commit3.getId().getName() + "^{commit}"));
339 	}
340 }