View Javadoc
1   /*
2    * Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.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.assertFalse;
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.ByteArrayOutputStream;
52  import java.io.File;
53  import java.io.FileInputStream;
54  import java.io.FileOutputStream;
55  import java.io.IOException;
56  import java.util.concurrent.Callable;
57  
58  import org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode;
59  import org.eclipse.jgit.api.MergeResult.MergeStatus;
60  import org.eclipse.jgit.api.errors.NoHeadException;
61  import org.eclipse.jgit.junit.JGitTestUtil;
62  import org.eclipse.jgit.junit.RepositoryTestCase;
63  import org.eclipse.jgit.lib.Constants;
64  import org.eclipse.jgit.lib.ObjectId;
65  import org.eclipse.jgit.lib.RefUpdate;
66  import org.eclipse.jgit.lib.Repository;
67  import org.eclipse.jgit.lib.RepositoryState;
68  import org.eclipse.jgit.lib.StoredConfig;
69  import org.eclipse.jgit.revwalk.RevCommit;
70  import org.eclipse.jgit.revwalk.RevSort;
71  import org.eclipse.jgit.revwalk.RevWalk;
72  import org.eclipse.jgit.transport.RefSpec;
73  import org.eclipse.jgit.transport.RemoteConfig;
74  import org.eclipse.jgit.transport.URIish;
75  import org.junit.Before;
76  import org.junit.Test;
77  
78  public class PullCommandTest extends RepositoryTestCase {
79  	/** Second Test repository */
80  	protected Repository dbTarget;
81  
82  	private Git source;
83  
84  	private Git target;
85  
86  	private File sourceFile;
87  
88  	private File targetFile;
89  
90  	@Test
91  	public void testPullFastForward() throws Exception {
92  		PullResult res = target.pull().call();
93  		// nothing to update since we don't have different data yet
94  		assertTrue(res.getFetchResult().getTrackingRefUpdates().isEmpty());
95  		assertTrue(res.getMergeResult().getMergeStatus().equals(
96  				MergeStatus.ALREADY_UP_TO_DATE));
97  
98  		assertFileContentsEqual(targetFile, "Hello world");
99  
100 		// change the source file
101 		writeToFile(sourceFile, "Another change");
102 		source.add().addFilepattern("SomeFile.txt").call();
103 		source.commit().setMessage("Some change in remote").call();
104 
105 		res = target.pull().call();
106 
107 		assertFalse(res.getFetchResult().getTrackingRefUpdates().isEmpty());
108 		assertEquals(res.getMergeResult().getMergeStatus(),
109 				MergeStatus.FAST_FORWARD);
110 		assertFileContentsEqual(targetFile, "Another change");
111 		assertEquals(RepositoryState.SAFE, target.getRepository()
112 				.getRepositoryState());
113 
114 		res = target.pull().call();
115 		assertEquals(res.getMergeResult().getMergeStatus(),
116 				MergeStatus.ALREADY_UP_TO_DATE);
117 	}
118 
119 	@Test
120 	public void testPullMerge() throws Exception {
121 		PullResult res = target.pull().call();
122 		// nothing to update since we don't have different data yet
123 		assertTrue(res.getFetchResult().getTrackingRefUpdates().isEmpty());
124 		assertTrue(res.getMergeResult().getMergeStatus()
125 				.equals(MergeStatus.ALREADY_UP_TO_DATE));
126 
127 		writeToFile(sourceFile, "Source change");
128 		source.add().addFilepattern("SomeFile.txt");
129 		RevCommit sourceCommit = source.commit()
130 				.setMessage("Source change in remote").call();
131 
132 		File targetFile2 = new File(dbTarget.getWorkTree(), "OtherFile.txt");
133 		writeToFile(targetFile2, "Unconflicting change");
134 		target.add().addFilepattern("OtherFile.txt").call();
135 		RevCommit targetCommit = target.commit()
136 				.setMessage("Unconflicting change in local").call();
137 
138 		res = target.pull().call();
139 
140 		MergeResult mergeResult = res.getMergeResult();
141 		ObjectId[] mergedCommits = mergeResult.getMergedCommits();
142 		assertEquals(targetCommit.getId(), mergedCommits[0]);
143 		assertEquals(sourceCommit.getId(), mergedCommits[1]);
144 		try (RevWalk rw = new RevWalk(dbTarget)) {
145 			RevCommit mergeCommit = rw.parseCommit(mergeResult.getNewHead());
146 			String message = "Merge branch 'master' of "
147 					+ db.getWorkTree().getAbsolutePath();
148 			assertEquals(message, mergeCommit.getShortMessage());
149 		}
150 	}
151 
152 	@Test
153 	public void testPullConflict() throws Exception {
154 		PullResult res = target.pull().call();
155 		// nothing to update since we don't have different data yet
156 		assertTrue(res.getFetchResult().getTrackingRefUpdates().isEmpty());
157 		assertTrue(res.getMergeResult().getMergeStatus().equals(
158 				MergeStatus.ALREADY_UP_TO_DATE));
159 
160 		assertFileContentsEqual(targetFile, "Hello world");
161 
162 		// change the source file
163 		writeToFile(sourceFile, "Source change");
164 		source.add().addFilepattern("SomeFile.txt").call();
165 		source.commit().setMessage("Source change in remote").call();
166 
167 		// change the target file
168 		writeToFile(targetFile, "Target change");
169 		target.add().addFilepattern("SomeFile.txt").call();
170 		target.commit().setMessage("Target change in local").call();
171 
172 		res = target.pull().call();
173 
174 		String sourceChangeString = "Source change\n>>>>>>> branch 'master' of "
175 				+ target.getRepository().getConfig().getString("remote",
176 						"origin", "url");
177 
178 		assertFalse(res.getFetchResult().getTrackingRefUpdates().isEmpty());
179 		assertEquals(res.getMergeResult().getMergeStatus(),
180 				MergeStatus.CONFLICTING);
181 		String result = "<<<<<<< HEAD\nTarget change\n=======\n"
182 				+ sourceChangeString + "\n";
183 		assertFileContentsEqual(targetFile, result);
184 		assertEquals(RepositoryState.MERGING, target.getRepository()
185 				.getRepositoryState());
186 	}
187 
188 	@Test
189 	public void testPullWithUntrackedStash() throws Exception {
190 		target.pull().call();
191 
192 		// change the source file
193 		writeToFile(sourceFile, "Source change");
194 		source.add().addFilepattern("SomeFile.txt").call();
195 		source.commit().setMessage("Source change in remote").call();
196 
197 		// write untracked file
198 		writeToFile(new File(dbTarget.getWorkTree(), "untracked.txt"),
199 				"untracked");
200 		RevCommit stash = target.stashCreate().setIndexMessage("message here")
201 				.setIncludeUntracked(true).call();
202 		assertNotNull(stash);
203 		assertTrue(target.status().call().isClean());
204 
205 		// pull from source
206 		assertTrue(target.pull().call().isSuccessful());
207 		assertEquals("[SomeFile.txt, mode:100644, content:Source change]",
208 				indexState(dbTarget, CONTENT));
209 		assertFalse(JGitTestUtil.check(dbTarget, "untracked.txt"));
210 		assertEquals("Source change",
211 				JGitTestUtil.read(dbTarget, "SomeFile.txt"));
212 
213 		// apply the stash
214 		target.stashApply().setStashRef(stash.getName()).call();
215 		assertEquals("[SomeFile.txt, mode:100644, content:Source change]",
216 				indexState(dbTarget, CONTENT));
217 		assertEquals("untracked", JGitTestUtil.read(dbTarget, "untracked.txt"));
218 		assertEquals("Source change",
219 				JGitTestUtil.read(dbTarget, "SomeFile.txt"));
220 	}
221 
222 	@Test
223 	public void testPullLocalConflict() throws Exception {
224 		target.branchCreate().setName("basedOnMaster").setStartPoint(
225 				"refs/heads/master").setUpstreamMode(SetupUpstreamMode.TRACK)
226 				.call();
227 		target.getRepository().updateRef(Constants.HEAD).link(
228 				"refs/heads/basedOnMaster");
229 		PullResult res = target.pull().call();
230 		// nothing to update since we don't have different data yet
231 		assertNull(res.getFetchResult());
232 		assertTrue(res.getMergeResult().getMergeStatus().equals(
233 				MergeStatus.ALREADY_UP_TO_DATE));
234 
235 		assertFileContentsEqual(targetFile, "Hello world");
236 
237 		// change the file in master
238 		target.getRepository().updateRef(Constants.HEAD).link(
239 				"refs/heads/master");
240 		writeToFile(targetFile, "Master change");
241 		target.add().addFilepattern("SomeFile.txt").call();
242 		target.commit().setMessage("Source change in master").call();
243 
244 		// change the file in slave
245 		target.getRepository().updateRef(Constants.HEAD).link(
246 				"refs/heads/basedOnMaster");
247 		writeToFile(targetFile, "Slave change");
248 		target.add().addFilepattern("SomeFile.txt").call();
249 		target.commit().setMessage("Source change in based on master").call();
250 
251 		res = target.pull().call();
252 
253 		String sourceChangeString = "Master change\n>>>>>>> branch 'master' of local repository";
254 
255 		assertNull(res.getFetchResult());
256 		assertEquals(res.getMergeResult().getMergeStatus(),
257 				MergeStatus.CONFLICTING);
258 		String result = "<<<<<<< HEAD\nSlave change\n=======\n"
259 				+ sourceChangeString + "\n";
260 		assertFileContentsEqual(targetFile, result);
261 		assertEquals(RepositoryState.MERGING, target.getRepository()
262 				.getRepositoryState());
263 	}
264 
265 	@Test(expected = NoHeadException.class)
266 	public void testPullEmptyRepository() throws Exception {
267 		Repository empty = createWorkRepository();
268 		RefUpdate delete = empty.updateRef(Constants.HEAD, true);
269 		delete.setForceUpdate(true);
270 		delete.delete();
271 		Git.wrap(empty).pull().call();
272 	}
273 
274 	@Test
275 	public void testPullMergeProgrammaticConfiguration() throws Exception {
276 		// create another commit on another branch in source
277 		source.checkout().setCreateBranch(true).setName("other").call();
278 		sourceFile = new File(db.getWorkTree(), "file2.txt");
279 		writeToFile(sourceFile, "content");
280 		source.add().addFilepattern("file2.txt").call();
281 		RevCommit sourceCommit = source.commit()
282 				.setMessage("source commit on branch other").call();
283 
284 		File targetFile2 = new File(dbTarget.getWorkTree(), "OtherFile.txt");
285 		writeToFile(targetFile2, "Unconflicting change");
286 		target.add().addFilepattern("OtherFile.txt").call();
287 		RevCommit targetCommit = target.commit()
288 				.setMessage("Unconflicting change in local").call();
289 
290 		PullResult res = target.pull().setRemote("origin")
291 				.setRemoteBranchName("other")
292 				.setRebase(false).call();
293 
294 		MergeResult mergeResult = res.getMergeResult();
295 		ObjectId[] mergedCommits = mergeResult.getMergedCommits();
296 		assertEquals(targetCommit.getId(), mergedCommits[0]);
297 		assertEquals(sourceCommit.getId(), mergedCommits[1]);
298 		try (RevWalk rw = new RevWalk(dbTarget)) {
299 			RevCommit mergeCommit = rw.parseCommit(mergeResult.getNewHead());
300 			String message = "Merge branch 'other' of "
301 					+ db.getWorkTree().getAbsolutePath();
302 			assertEquals(message, mergeCommit.getShortMessage());
303 		}
304 	}
305 
306 	@Test
307 	public void testPullMergeProgrammaticConfigurationImpliedTargetBranch()
308 			throws Exception {
309 		// create another commit on another branch in source
310 		source.checkout().setCreateBranch(true).setName("other").call();
311 		sourceFile = new File(db.getWorkTree(), "file2.txt");
312 		writeToFile(sourceFile, "content");
313 		source.add().addFilepattern("file2.txt").call();
314 		RevCommit sourceCommit = source.commit()
315 				.setMessage("source commit on branch other").call();
316 
317 		target.checkout().setCreateBranch(true).setName("other").call();
318 		File targetFile2 = new File(dbTarget.getWorkTree(), "OtherFile.txt");
319 		writeToFile(targetFile2, "Unconflicting change");
320 		target.add().addFilepattern("OtherFile.txt").call();
321 		RevCommit targetCommit = target.commit()
322 				.setMessage("Unconflicting change in local").call();
323 
324 		// the source branch "other" matching the target branch should be
325 		// implied
326 		PullResult res = target.pull().setRemote("origin").setRebase(false)
327 				.call();
328 
329 		MergeResult mergeResult = res.getMergeResult();
330 		ObjectId[] mergedCommits = mergeResult.getMergedCommits();
331 		assertEquals(targetCommit.getId(), mergedCommits[0]);
332 		assertEquals(sourceCommit.getId(), mergedCommits[1]);
333 		try (RevWalk rw = new RevWalk(dbTarget)) {
334 			RevCommit mergeCommit = rw.parseCommit(mergeResult.getNewHead());
335 			String message = "Merge branch 'other' of "
336 					+ db.getWorkTree().getAbsolutePath() + " into other";
337 			assertEquals(message, mergeCommit.getShortMessage());
338 		}
339 	}
340 
341 	private enum TestPullMode {
342 		MERGE, REBASE, REBASE_PREASERVE
343 	}
344 
345 	@Test
346 	/** global rebase config should be respected */
347 	public void testPullWithRebasePreserve1Config() throws Exception {
348 		Callable<PullResult> setup = new Callable<PullResult>() {
349 			public PullResult call() throws Exception {
350 				StoredConfig config = dbTarget.getConfig();
351 				config.setString("pull", null, "rebase", "preserve");
352 				config.save();
353 				return target.pull().call();
354 			}
355 		};
356 		doTestPullWithRebase(setup, TestPullMode.REBASE_PREASERVE);
357 	}
358 
359 	@Test
360 	/** the branch-local config should win over the global config */
361 	public void testPullWithRebasePreserveConfig2() throws Exception {
362 		Callable<PullResult> setup = new Callable<PullResult>() {
363 			public PullResult call() throws Exception {
364 				StoredConfig config = dbTarget.getConfig();
365 				config.setString("pull", null, "rebase", "false");
366 				config.setString("branch", "master", "rebase", "preserve");
367 				config.save();
368 				return target.pull().call();
369 			}
370 		};
371 		doTestPullWithRebase(setup, TestPullMode.REBASE_PREASERVE);
372 	}
373 
374 	@Test
375 	/** the branch-local config should be respected */
376 	public void testPullWithRebasePreserveConfig3() throws Exception {
377 		Callable<PullResult> setup = new Callable<PullResult>() {
378 			public PullResult call() throws Exception {
379 				StoredConfig config = dbTarget.getConfig();
380 				config.setString("branch", "master", "rebase", "preserve");
381 				config.save();
382 				return target.pull().call();
383 			}
384 		};
385 		doTestPullWithRebase(setup, TestPullMode.REBASE_PREASERVE);
386 	}
387 
388 	@Test
389 	/** global rebase config should be respected */
390 	public void testPullWithRebaseConfig1() throws Exception {
391 		Callable<PullResult> setup = new Callable<PullResult>() {
392 			public PullResult call() throws Exception {
393 				StoredConfig config = dbTarget.getConfig();
394 				config.setString("pull", null, "rebase", "true");
395 				config.save();
396 				return target.pull().call();
397 			}
398 		};
399 		doTestPullWithRebase(setup, TestPullMode.REBASE);
400 	}
401 
402 	@Test
403 	/** the branch-local config should win over the global config */
404 	public void testPullWithRebaseConfig2() throws Exception {
405 		Callable<PullResult> setup = new Callable<PullResult>() {
406 			public PullResult call() throws Exception {
407 				StoredConfig config = dbTarget.getConfig();
408 				config.setString("pull", null, "rebase", "preserve");
409 				config.setString("branch", "master", "rebase", "true");
410 				config.save();
411 				return target.pull().call();
412 			}
413 		};
414 		doTestPullWithRebase(setup, TestPullMode.REBASE);
415 	}
416 
417 	@Test
418 	/** the branch-local config should be respected */
419 	public void testPullWithRebaseConfig3() throws Exception {
420 		Callable<PullResult> setup = new Callable<PullResult>() {
421 			public PullResult call() throws Exception {
422 				StoredConfig config = dbTarget.getConfig();
423 				config.setString("branch", "master", "rebase", "true");
424 				config.save();
425 				return target.pull().call();
426 			}
427 		};
428 		doTestPullWithRebase(setup, TestPullMode.REBASE);
429 	}
430 
431 	@Test
432 	/** without config it should merge */
433 	public void testPullWithoutConfig() throws Exception {
434 		Callable<PullResult> setup = new Callable<PullResult>() {
435 			public PullResult call() throws Exception {
436 				return target.pull().call();
437 			}
438 		};
439 		doTestPullWithRebase(setup, TestPullMode.MERGE);
440 	}
441 
442 	@Test
443 	/** the branch local config should win over the global config */
444 	public void testPullWithMergeConfig() throws Exception {
445 		Callable<PullResult> setup = new Callable<PullResult>() {
446 			public PullResult call() throws Exception {
447 				StoredConfig config = dbTarget.getConfig();
448 				config.setString("pull", null, "rebase", "true");
449 				config.setString("branch", "master", "rebase", "false");
450 				config.save();
451 				return target.pull().call();
452 			}
453 		};
454 		doTestPullWithRebase(setup, TestPullMode.MERGE);
455 	}
456 
457 	@Test
458 	/** the branch local config should win over the global config */
459 	public void testPullWithMergeConfig2() throws Exception {
460 		Callable<PullResult> setup = new Callable<PullResult>() {
461 			public PullResult call() throws Exception {
462 				StoredConfig config = dbTarget.getConfig();
463 				config.setString("pull", null, "rebase", "false");
464 				config.save();
465 				return target.pull().call();
466 			}
467 		};
468 		doTestPullWithRebase(setup, TestPullMode.MERGE);
469 	}
470 
471 	private void doTestPullWithRebase(Callable<PullResult> pullSetup,
472 			TestPullMode expectedPullMode) throws Exception {
473 		// simple upstream change
474 		writeToFile(sourceFile, "content");
475 		source.add().addFilepattern(sourceFile.getName()).call();
476 		RevCommit sourceCommit = source.commit().setMessage("source commit")
477 				.call();
478 
479 		// create a merge commit in target
480 		File loxalFile = new File(dbTarget.getWorkTree(), "local.txt");
481 		writeToFile(loxalFile, "initial\n");
482 		target.add().addFilepattern("local.txt").call();
483 		RevCommit t1 = target.commit().setMessage("target commit 1").call();
484 
485 		target.checkout().setCreateBranch(true).setName("side").call();
486 
487 		String newContent = "initial\n" + "and more\n";
488 		writeToFile(loxalFile, newContent);
489 		target.add().addFilepattern("local.txt").call();
490 		RevCommit t2 = target.commit().setMessage("target commit 2").call();
491 
492 		target.checkout().setName("master").call();
493 
494 		MergeResult mergeResult = target.merge()
495 				.setFastForward(MergeCommand.FastForwardMode.NO_FF).include(t2)
496 				.call();
497 		assertEquals(MergeStatus.MERGED, mergeResult.getMergeStatus());
498 		assertFileContentsEqual(loxalFile, newContent);
499 		ObjectId merge = mergeResult.getNewHead();
500 
501 		// pull
502 		PullResult res = pullSetup.call();
503 		assertNotNull(res.getFetchResult());
504 
505 		if (expectedPullMode == TestPullMode.MERGE) {
506 			assertEquals(MergeStatus.MERGED, res.getMergeResult()
507 					.getMergeStatus());
508 			assertNull(res.getRebaseResult());
509 		} else {
510 			assertNull(res.getMergeResult());
511 			assertEquals(RebaseResult.OK_RESULT, res.getRebaseResult());
512 		}
513 		assertFileContentsEqual(sourceFile, "content");
514 
515 		try (RevWalk rw = new RevWalk(dbTarget)) {
516 			rw.sort(RevSort.TOPO);
517 			rw.markStart(rw.parseCommit(dbTarget.resolve("refs/heads/master")));
518 
519 			RevCommit next;
520 			if (expectedPullMode == TestPullMode.MERGE) {
521 				next = rw.next();
522 				assertEquals(2, next.getParentCount());
523 				assertEquals(merge, next.getParent(0));
524 				assertEquals(sourceCommit, next.getParent(1));
525 				// since both parents are known do no further checks here
526 			} else {
527 				if (expectedPullMode == TestPullMode.REBASE_PREASERVE) {
528 					next = rw.next();
529 					assertEquals(2, next.getParentCount());
530 				}
531 				next = rw.next();
532 				assertEquals(t2.getShortMessage(), next.getShortMessage());
533 				next = rw.next();
534 				assertEquals(t1.getShortMessage(), next.getShortMessage());
535 				next = rw.next();
536 				assertEquals(sourceCommit, next);
537 				next = rw.next();
538 				assertEquals("Initial commit for source",
539 						next.getShortMessage());
540 				next = rw.next();
541 				assertNull(next);
542 			}
543 		}
544 	}
545 
546 	@Override
547 	@Before
548 	public void setUp() throws Exception {
549 		super.setUp();
550 		dbTarget = createWorkRepository();
551 		source = new Git(db);
552 		target = new Git(dbTarget);
553 
554 		// put some file in the source repo
555 		sourceFile = new File(db.getWorkTree(), "SomeFile.txt");
556 		writeToFile(sourceFile, "Hello world");
557 		// and commit it
558 		source.add().addFilepattern("SomeFile.txt").call();
559 		source.commit().setMessage("Initial commit for source").call();
560 
561 		// configure the target repo to connect to the source via "origin"
562 		StoredConfig targetConfig = dbTarget.getConfig();
563 		targetConfig.setString("branch", "master", "remote", "origin");
564 		targetConfig
565 				.setString("branch", "master", "merge", "refs/heads/master");
566 		RemoteConfig config = new RemoteConfig(targetConfig, "origin");
567 
568 		config
569 				.addURI(new URIish(source.getRepository().getWorkTree()
570 						.getAbsolutePath()));
571 		config.addFetchRefSpec(new RefSpec(
572 				"+refs/heads/*:refs/remotes/origin/*"));
573 		config.update(targetConfig);
574 		targetConfig.save();
575 
576 		targetFile = new File(dbTarget.getWorkTree(), "SomeFile.txt");
577 		// make sure we have the same content
578 		target.pull().call();
579 		assertFileContentsEqual(targetFile, "Hello world");
580 	}
581 
582 	private static void writeToFile(File actFile, String string)
583 			throws IOException {
584 		FileOutputStream fos = null;
585 		try {
586 			fos = new FileOutputStream(actFile);
587 			fos.write(string.getBytes("UTF-8"));
588 			fos.close();
589 		} finally {
590 			if (fos != null)
591 				fos.close();
592 		}
593 	}
594 
595 	private static void assertFileContentsEqual(File actFile, String string)
596 			throws IOException {
597 		ByteArrayOutputStream bos = new ByteArrayOutputStream();
598 		FileInputStream fis = null;
599 		byte[] buffer = new byte[100];
600 		try {
601 			fis = new FileInputStream(actFile);
602 			int read = fis.read(buffer);
603 			while (read > 0) {
604 				bos.write(buffer, 0, read);
605 				read = fis.read(buffer);
606 			}
607 			String content = new String(bos.toByteArray(), "UTF-8");
608 			assertEquals(string, content);
609 		} finally {
610 			if (fis != null)
611 				fis.close();
612 		}
613 	}
614 }