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 			@Override
350 			public PullResult call() throws Exception {
351 				StoredConfig config = dbTarget.getConfig();
352 				config.setString("pull", null, "rebase", "preserve");
353 				config.save();
354 				return target.pull().call();
355 			}
356 		};
357 		doTestPullWithRebase(setup, TestPullMode.REBASE_PREASERVE);
358 	}
359 
360 	@Test
361 	/** the branch-local config should win over the global config */
362 	public void testPullWithRebasePreserveConfig2() throws Exception {
363 		Callable<PullResult> setup = new Callable<PullResult>() {
364 			@Override
365 			public PullResult call() throws Exception {
366 				StoredConfig config = dbTarget.getConfig();
367 				config.setString("pull", null, "rebase", "false");
368 				config.setString("branch", "master", "rebase", "preserve");
369 				config.save();
370 				return target.pull().call();
371 			}
372 		};
373 		doTestPullWithRebase(setup, TestPullMode.REBASE_PREASERVE);
374 	}
375 
376 	@Test
377 	/** the branch-local config should be respected */
378 	public void testPullWithRebasePreserveConfig3() throws Exception {
379 		Callable<PullResult> setup = new Callable<PullResult>() {
380 			@Override
381 			public PullResult call() throws Exception {
382 				StoredConfig config = dbTarget.getConfig();
383 				config.setString("branch", "master", "rebase", "preserve");
384 				config.save();
385 				return target.pull().call();
386 			}
387 		};
388 		doTestPullWithRebase(setup, TestPullMode.REBASE_PREASERVE);
389 	}
390 
391 	@Test
392 	/** global rebase config should be respected */
393 	public void testPullWithRebaseConfig1() throws Exception {
394 		Callable<PullResult> setup = new Callable<PullResult>() {
395 			@Override
396 			public PullResult call() throws Exception {
397 				StoredConfig config = dbTarget.getConfig();
398 				config.setString("pull", null, "rebase", "true");
399 				config.save();
400 				return target.pull().call();
401 			}
402 		};
403 		doTestPullWithRebase(setup, TestPullMode.REBASE);
404 	}
405 
406 	@Test
407 	/** the branch-local config should win over the global config */
408 	public void testPullWithRebaseConfig2() throws Exception {
409 		Callable<PullResult> setup = new Callable<PullResult>() {
410 			@Override
411 			public PullResult call() throws Exception {
412 				StoredConfig config = dbTarget.getConfig();
413 				config.setString("pull", null, "rebase", "preserve");
414 				config.setString("branch", "master", "rebase", "true");
415 				config.save();
416 				return target.pull().call();
417 			}
418 		};
419 		doTestPullWithRebase(setup, TestPullMode.REBASE);
420 	}
421 
422 	@Test
423 	/** the branch-local config should be respected */
424 	public void testPullWithRebaseConfig3() throws Exception {
425 		Callable<PullResult> setup = new Callable<PullResult>() {
426 			@Override
427 			public PullResult call() throws Exception {
428 				StoredConfig config = dbTarget.getConfig();
429 				config.setString("branch", "master", "rebase", "true");
430 				config.save();
431 				return target.pull().call();
432 			}
433 		};
434 		doTestPullWithRebase(setup, TestPullMode.REBASE);
435 	}
436 
437 	@Test
438 	/** without config it should merge */
439 	public void testPullWithoutConfig() throws Exception {
440 		Callable<PullResult> setup = new Callable<PullResult>() {
441 			@Override
442 			public PullResult call() throws Exception {
443 				return target.pull().call();
444 			}
445 		};
446 		doTestPullWithRebase(setup, TestPullMode.MERGE);
447 	}
448 
449 	@Test
450 	/** the branch local config should win over the global config */
451 	public void testPullWithMergeConfig() throws Exception {
452 		Callable<PullResult> setup = new Callable<PullResult>() {
453 			@Override
454 			public PullResult call() throws Exception {
455 				StoredConfig config = dbTarget.getConfig();
456 				config.setString("pull", null, "rebase", "true");
457 				config.setString("branch", "master", "rebase", "false");
458 				config.save();
459 				return target.pull().call();
460 			}
461 		};
462 		doTestPullWithRebase(setup, TestPullMode.MERGE);
463 	}
464 
465 	@Test
466 	/** the branch local config should win over the global config */
467 	public void testPullWithMergeConfig2() throws Exception {
468 		Callable<PullResult> setup = new Callable<PullResult>() {
469 			@Override
470 			public PullResult call() throws Exception {
471 				StoredConfig config = dbTarget.getConfig();
472 				config.setString("pull", null, "rebase", "false");
473 				config.save();
474 				return target.pull().call();
475 			}
476 		};
477 		doTestPullWithRebase(setup, TestPullMode.MERGE);
478 	}
479 
480 	private void doTestPullWithRebase(Callable<PullResult> pullSetup,
481 			TestPullMode expectedPullMode) throws Exception {
482 		// simple upstream change
483 		writeToFile(sourceFile, "content");
484 		source.add().addFilepattern(sourceFile.getName()).call();
485 		RevCommit sourceCommit = source.commit().setMessage("source commit")
486 				.call();
487 
488 		// create a merge commit in target
489 		File loxalFile = new File(dbTarget.getWorkTree(), "local.txt");
490 		writeToFile(loxalFile, "initial\n");
491 		target.add().addFilepattern("local.txt").call();
492 		RevCommit t1 = target.commit().setMessage("target commit 1").call();
493 
494 		target.checkout().setCreateBranch(true).setName("side").call();
495 
496 		String newContent = "initial\n" + "and more\n";
497 		writeToFile(loxalFile, newContent);
498 		target.add().addFilepattern("local.txt").call();
499 		RevCommit t2 = target.commit().setMessage("target commit 2").call();
500 
501 		target.checkout().setName("master").call();
502 
503 		MergeResult mergeResult = target.merge()
504 				.setFastForward(MergeCommand.FastForwardMode.NO_FF).include(t2)
505 				.call();
506 		assertEquals(MergeStatus.MERGED, mergeResult.getMergeStatus());
507 		assertFileContentsEqual(loxalFile, newContent);
508 		ObjectId merge = mergeResult.getNewHead();
509 
510 		// pull
511 		PullResult res = pullSetup.call();
512 		assertNotNull(res.getFetchResult());
513 
514 		if (expectedPullMode == TestPullMode.MERGE) {
515 			assertEquals(MergeStatus.MERGED, res.getMergeResult()
516 					.getMergeStatus());
517 			assertNull(res.getRebaseResult());
518 		} else {
519 			assertNull(res.getMergeResult());
520 			assertEquals(RebaseResult.OK_RESULT, res.getRebaseResult());
521 		}
522 		assertFileContentsEqual(sourceFile, "content");
523 
524 		try (RevWalk rw = new RevWalk(dbTarget)) {
525 			rw.sort(RevSort.TOPO);
526 			rw.markStart(rw.parseCommit(dbTarget.resolve("refs/heads/master")));
527 
528 			RevCommit next;
529 			if (expectedPullMode == TestPullMode.MERGE) {
530 				next = rw.next();
531 				assertEquals(2, next.getParentCount());
532 				assertEquals(merge, next.getParent(0));
533 				assertEquals(sourceCommit, next.getParent(1));
534 				// since both parents are known do no further checks here
535 			} else {
536 				if (expectedPullMode == TestPullMode.REBASE_PREASERVE) {
537 					next = rw.next();
538 					assertEquals(2, next.getParentCount());
539 				}
540 				next = rw.next();
541 				assertEquals(t2.getShortMessage(), next.getShortMessage());
542 				next = rw.next();
543 				assertEquals(t1.getShortMessage(), next.getShortMessage());
544 				next = rw.next();
545 				assertEquals(sourceCommit, next);
546 				next = rw.next();
547 				assertEquals("Initial commit for source",
548 						next.getShortMessage());
549 				next = rw.next();
550 				assertNull(next);
551 			}
552 		}
553 	}
554 
555 	@Override
556 	@Before
557 	public void setUp() throws Exception {
558 		super.setUp();
559 		dbTarget = createWorkRepository();
560 		source = new Git(db);
561 		target = new Git(dbTarget);
562 
563 		// put some file in the source repo
564 		sourceFile = new File(db.getWorkTree(), "SomeFile.txt");
565 		writeToFile(sourceFile, "Hello world");
566 		// and commit it
567 		source.add().addFilepattern("SomeFile.txt").call();
568 		source.commit().setMessage("Initial commit for source").call();
569 
570 		// configure the target repo to connect to the source via "origin"
571 		StoredConfig targetConfig = dbTarget.getConfig();
572 		targetConfig.setString("branch", "master", "remote", "origin");
573 		targetConfig
574 				.setString("branch", "master", "merge", "refs/heads/master");
575 		RemoteConfig config = new RemoteConfig(targetConfig, "origin");
576 
577 		config
578 				.addURI(new URIish(source.getRepository().getWorkTree()
579 						.getAbsolutePath()));
580 		config.addFetchRefSpec(new RefSpec(
581 				"+refs/heads/*:refs/remotes/origin/*"));
582 		config.update(targetConfig);
583 		targetConfig.save();
584 
585 		targetFile = new File(dbTarget.getWorkTree(), "SomeFile.txt");
586 		// make sure we have the same content
587 		target.pull().call();
588 		assertFileContentsEqual(targetFile, "Hello world");
589 	}
590 
591 	private static void writeToFile(File actFile, String string)
592 			throws IOException {
593 		FileOutputStream fos = null;
594 		try {
595 			fos = new FileOutputStream(actFile);
596 			fos.write(string.getBytes("UTF-8"));
597 			fos.close();
598 		} finally {
599 			if (fos != null)
600 				fos.close();
601 		}
602 	}
603 
604 	private static void assertFileContentsEqual(File actFile, String string)
605 			throws IOException {
606 		ByteArrayOutputStream bos = new ByteArrayOutputStream();
607 		FileInputStream fis = null;
608 		byte[] buffer = new byte[100];
609 		try {
610 			fis = new FileInputStream(actFile);
611 			int read = fis.read(buffer);
612 			while (read > 0) {
613 				bos.write(buffer, 0, read);
614 				read = fis.read(buffer);
615 			}
616 			String content = new String(bos.toByteArray(), "UTF-8");
617 			assertEquals(string, content);
618 		} finally {
619 			if (fis != null)
620 				fis.close();
621 		}
622 	}
623 }