Merge "Reduce memory held and speed up DfsGarbageCollector."
diff --git a/org.eclipse.jgit.packaging/pom.xml b/org.eclipse.jgit.packaging/pom.xml
index a4ff8a4..a8f6ab7 100644
--- a/org.eclipse.jgit.packaging/pom.xml
+++ b/org.eclipse.jgit.packaging/pom.xml
@@ -62,7 +62,7 @@
     <tycho-version>0.16.0</tycho-version>
     <jetty-version>7.6.0.v20120127</jetty-version>
     <args4j-version>2.0.12</args4j-version>
-    <jsch-version>0.1.44-1</jsch-version>
+    <jsch-version>0.1.46</jsch-version>
     <eclipse-site>http://download.eclipse.org/releases/indigo</eclipse-site>
     <orbit-site>http://download.eclipse.org/tools/orbit/downloads/drops/R20120526062928/repository/</orbit-site>
   </properties>
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NL1.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NL1.patch
new file mode 100644
index 0000000..ba5a4fc
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NL1.patch
@@ -0,0 +1,10 @@
+diff --git a/NL1 b/NL1
+index 68abad7..b14088c 100644
+--- a/NL1
++++ b/NL1
+@@ -1,4 +1,4 @@
+ a

+-b

++d

+ c

+ 

diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NL1_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NL1_PostImage
new file mode 100644
index 0000000..b14088c
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NL1_PostImage
@@ -0,0 +1,4 @@
+a

+d

+c

+

diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NL1_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NL1_PreImage
new file mode 100644
index 0000000..68abad7
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NL1_PreImage
@@ -0,0 +1,4 @@
+a

+b

+c

+

diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ApplyCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ApplyCommandTest.java
index 5513b44..a0a4ddc 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ApplyCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ApplyCommandTest.java
@@ -178,6 +178,16 @@ public void testModifyZ() throws Exception {
 				b.getString(0, b.size(), false));
 	}
 
+	@Test
+	public void testModifyNL1() throws Exception {
+		ApplyResult result = init("NL1");
+		assertEquals(1, result.getUpdatedFiles().size());
+		assertEquals(new File(db.getWorkTree(), "NL1"), result
+				.getUpdatedFiles().get(0));
+		checkFile(new File(db.getWorkTree(), "NL1"),
+				b.getString(0, b.size(), false));
+	}
+
 	private static byte[] readFile(final String patchFile) throws IOException {
 		final InputStream in = DiffFormatterReflowTest.class
 				.getResourceAsStream(patchFile);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
index ba97e90..fbaef76 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
@@ -1059,7 +1059,6 @@ public void testRebaseWithUntrackedFile() throws Exception {
 	}
 
 	@Test
-	@SuppressWarnings("null")
 	public void testRebaseWithUnstagedTopicChange() throws Exception {
 		// create file1, add and commit
 		writeTrashFile(FILE1, "file1");
@@ -1084,19 +1083,14 @@ public void testRebaseWithUnstagedTopicChange() throws Exception {
 		writeTrashFile("file2", "unstaged file2");
 
 		// rebase
-		JGitInternalException exception = null;
-		try {
-			git.rebase().setUpstream("refs/heads/master").call();
-		} catch (JGitInternalException e) {
-			exception = e;
-		}
-		assertNotNull(exception);
-		assertEquals("Checkout conflict with files: \nfile2",
-				exception.getMessage());
+		RebaseResult result = git.rebase().setUpstream("refs/heads/master")
+				.call();
+		assertEquals(Status.CONFLICTS, result.getStatus());
+		assertEquals(1, result.getConflicts().size());
+		assertEquals("file2", result.getConflicts().get(0));
 	}
 
 	@Test
-	@SuppressWarnings("null")
 	public void testRebaseWithUncommittedTopicChange() throws Exception {
 		// create file1, add and commit
 		writeTrashFile(FILE1, "file1");
@@ -1122,23 +1116,17 @@ public void testRebaseWithUncommittedTopicChange() throws Exception {
 		git.add().addFilepattern("file2").call();
 		// do not commit
 
-		// rebase
-		JGitInternalException exception = null;
-		try {
-			git.rebase().setUpstream("refs/heads/master").call();
-		} catch (JGitInternalException e) {
-			exception = e;
-		}
-		assertNotNull(exception);
-		assertEquals("Checkout conflict with files: \nfile2",
-				exception.getMessage());
+		RebaseResult result = git.rebase().setUpstream("refs/heads/master")
+				.call();
+		assertEquals(Status.CONFLICTS, result.getStatus());
+		assertEquals(1, result.getConflicts().size());
+		assertEquals("file2", result.getConflicts().get(0));
 
 		checkFile(uncommittedFile, "uncommitted file2");
 		assertEquals(RepositoryState.SAFE, git.getRepository().getRepositoryState());
 	}
 
 	@Test
-	@SuppressWarnings("null")
 	public void testRebaseWithUnstagedMasterChange() throws Exception {
 		// create file1, add and commit
 		writeTrashFile(FILE1, "file1");
@@ -1163,19 +1151,14 @@ public void testRebaseWithUnstagedMasterChange() throws Exception {
 		writeTrashFile(FILE1, "unstaged modified file1");
 
 		// rebase
-		JGitInternalException exception = null;
-		try {
-			git.rebase().setUpstream("refs/heads/master").call();
-		} catch (JGitInternalException e) {
-			exception = e;
-		}
-		assertNotNull(exception);
-		assertEquals("Checkout conflict with files: \nfile1",
-				exception.getMessage());
+		RebaseResult result = git.rebase().setUpstream("refs/heads/master")
+				.call();
+		assertEquals(Status.CONFLICTS, result.getStatus());
+		assertEquals(1, result.getConflicts().size());
+		assertEquals(FILE1, result.getConflicts().get(0));
 	}
 
 	@Test
-	@SuppressWarnings("null")
 	public void testRebaseWithUncommittedMasterChange() throws Exception {
 		// create file1, add and commit
 		writeTrashFile(FILE1, "file1");
@@ -1202,15 +1185,11 @@ public void testRebaseWithUncommittedMasterChange() throws Exception {
 		// do not commit
 
 		// rebase
-		JGitInternalException exception = null;
-		try {
-			git.rebase().setUpstream("refs/heads/master").call();
-		} catch (JGitInternalException e) {
-			exception = e;
-		}
-		assertNotNull(exception);
-		assertEquals("Checkout conflict with files: \nfile1",
-				exception.getMessage());
+		RebaseResult result = git.rebase().setUpstream("refs/heads/master")
+				.call();
+		assertEquals(Status.CONFLICTS, result.getStatus());
+		assertEquals(1, result.getConflicts().size());
+		assertEquals(FILE1, result.getConflicts().get(0));
 	}
 
 	@Test
@@ -1496,14 +1475,13 @@ public void testRebaseShouldLeaveWorkspaceUntouchedWithUnstagedChangesConflict()
 		File theFile = writeTrashFile(FILE1, "dirty the file");
 
 		// and attempt to rebase
-		try {
-			RebaseResult rebaseResult = git.rebase()
+		RebaseResult rebaseResult = git.rebase()
 					.setUpstream("refs/heads/master").call();
-			fail("Checkout with conflict should have occured, not "
-					+ rebaseResult.getStatus());
-		} catch (JGitInternalException e) {
-			checkFile(theFile, "dirty the file");
-		}
+		assertEquals(Status.CONFLICTS, rebaseResult.getStatus());
+		assertEquals(1, rebaseResult.getConflicts().size());
+		assertEquals(FILE1, rebaseResult.getConflicts().get(0));
+
+		checkFile(theFile, "dirty the file");
 
 		assertEquals(RepositoryState.SAFE, git.getRepository()
 				.getRepositoryState());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffEntryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffEntryTest.java
index cb7cad8..4c9c54f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffEntryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffEntryTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, Dariusz Luksza <dariusz@luksza.org>
+ * Copyright (C) 2011, 2013 Dariusz Luksza <dariusz@luksza.org>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -47,6 +47,7 @@
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 
@@ -65,6 +66,8 @@
 import org.eclipse.jgit.treewalk.EmptyTreeIterator;
 import org.eclipse.jgit.treewalk.FileTreeIterator;
 import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
+import org.eclipse.jgit.treewalk.filter.TreeFilter;
 import org.eclipse.jgit.util.FileUtils;
 import org.junit.Test;
 
@@ -265,6 +268,71 @@ public void shouldListChangesInWorkingTree() throws Exception {
 		assertThat(entry.getNewPath(), is("b.txt"));
 	}
 
+	@Test
+	public void shouldMarkEntriesWhenGivenMarkTreeFilter() throws Exception {
+		// given
+		Git git = new Git(db);
+		RevCommit c1 = git.commit().setMessage("initial commit").call();
+		FileUtils.mkdir(new File(db.getWorkTree(), "b"));
+		writeTrashFile("a.txt", "a");
+		writeTrashFile("b/1.txt", "b1");
+		writeTrashFile("b/2.txt", "b2");
+		writeTrashFile("c.txt", "c");
+		git.add().addFilepattern("a.txt").addFilepattern("b")
+				.addFilepattern("c.txt").call();
+		RevCommit c2 = git.commit().setMessage("second commit").call();
+		TreeFilter filterA = PathFilterGroup.createFromStrings("a.txt");
+		TreeFilter filterB = PathFilterGroup.createFromStrings("b");
+		TreeFilter filterB2 = PathFilterGroup.createFromStrings("b/2.txt");
+
+		// when
+		TreeWalk walk = new TreeWalk(db);
+		walk.addTree(c1.getTree());
+		walk.addTree(c2.getTree());
+		List<DiffEntry> result = DiffEntry.scan(walk, true, new TreeFilter[] {
+				filterA, filterB, filterB2 });
+
+		// then
+		assertThat(result, notNullValue());
+		assertEquals(5, result.size());
+
+		DiffEntry entryA = result.get(0);
+		DiffEntry entryB = result.get(1);
+		DiffEntry entryB1 = result.get(2);
+		DiffEntry entryB2 = result.get(3);
+		DiffEntry entryC = result.get(4);
+
+		assertThat(entryA.getNewPath(), is("a.txt"));
+		assertTrue(entryA.isMarked(0));
+		assertFalse(entryA.isMarked(1));
+		assertFalse(entryA.isMarked(2));
+		assertEquals(1, entryA.getTreeFilterMarks());
+
+		assertThat(entryB.getNewPath(), is("b"));
+		assertFalse(entryB.isMarked(0));
+		assertTrue(entryB.isMarked(1));
+		assertTrue(entryB.isMarked(2));
+		assertEquals(6, entryB.getTreeFilterMarks());
+
+		assertThat(entryB1.getNewPath(), is("b/1.txt"));
+		assertFalse(entryB1.isMarked(0));
+		assertTrue(entryB1.isMarked(1));
+		assertFalse(entryB1.isMarked(2));
+		assertEquals(2, entryB1.getTreeFilterMarks());
+
+		assertThat(entryB2.getNewPath(), is("b/2.txt"));
+		assertFalse(entryB2.isMarked(0));
+		assertTrue(entryB2.isMarked(1));
+		assertTrue(entryB2.isMarked(2));
+		assertEquals(6, entryB2.getTreeFilterMarks());
+
+		assertThat(entryC.getNewPath(), is("c.txt"));
+		assertFalse(entryC.isMarked(0));
+		assertFalse(entryC.isMarked(1));
+		assertFalse(entryC.isMarked(2));
+		assertEquals(0, entryC.getTreeFilterMarks());
+	}
+
 	@Test(expected = IllegalArgumentException.class)
 	public void shouldThrowIAEWhenTreeWalkHasLessThanTwoTrees()
 			throws Exception {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeMessageFormatterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeMessageFormatterTest.java
index 73fb838..88ace76 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeMessageFormatterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeMessageFormatterTest.java
@@ -185,4 +185,46 @@ public void testIntoSymbolicRefHeadPointingToMaster() throws IOException {
 		String message = formatter.format(Arrays.asList(a), head);
 		assertEquals("Merge branch 'a'", message);
 	}
+
+	@Test
+	public void testFormatWithConflictsNoFooter() {
+		String originalMessage = "Header Line\n\nCommit body\n";
+		String message = formatter.formatWithConflicts(originalMessage,
+				Arrays.asList(new String[] { "path1" }));
+		assertEquals("Header Line\n\nCommit body\n\nConflicts:\n\tpath1\n",
+				message);
+	}
+
+	@Test
+	public void testFormatWithConflictsNoFooterNoLineBreak() {
+		String originalMessage = "Header Line\n\nCommit body";
+		String message = formatter.formatWithConflicts(originalMessage,
+				Arrays.asList(new String[] { "path1" }));
+		assertEquals("Header Line\n\nCommit body\n\nConflicts:\n\tpath1\n",
+				message);
+	}
+
+	@Test
+	public void testFormatWithConflictsWithFooters() {
+		String originalMessage = "Header Line\n\nCommit body\n\nChangeId:"
+				+ " I123456789123456789123456789123456789\nBug:1234567\n";
+		String message = formatter.formatWithConflicts(originalMessage,
+				Arrays.asList(new String[] { "path1" }));
+		assertEquals(
+				"Header Line\n\nCommit body\n\nConflicts:\n\tpath1\n\n"
+						+ "ChangeId: I123456789123456789123456789123456789\nBug:1234567\n",
+				message);
+	}
+
+	@Test
+	public void testFormatWithConflictsWithFooterlikeLineInBody() {
+		String originalMessage = "Header Line\n\nCommit body\nBug:1234567\nMore Body\n\nChangeId:"
+				+ " I123456789123456789123456789123456789\nBug:1234567\n";
+		String message = formatter.formatWithConflicts(originalMessage,
+				Arrays.asList(new String[] { "path1" }));
+		assertEquals(
+				"Header Line\n\nCommit body\nBug:1234567\nMore Body\n\nConflicts:\n\tpath1\n\n"
+						+ "ChangeId: I123456789123456789123456789123456789\nBug:1234567\n",
+				message);
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/ChangeIdUtilTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/ChangeIdUtilTest.java
index 4cdc02a..54f3114 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/ChangeIdUtilTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/ChangeIdUtilTest.java
@@ -123,15 +123,65 @@ public void testId() throws IOException {
 	@Test
 	public void testHasChangeid() throws Exception {
 		assertEquals(
-				"has changeid\n\nBug: 33\nmore text\nSigned-off-by: me@you.too\nChange-Id: I0123456789012345678901234567890123456789\nAnd then some\n",
-				call("has changeid\n\nBug: 33\nmore text\nSigned-off-by: me@you.too\nChange-Id: I0123456789012345678901234567890123456789\nAnd then some\n"));
+				"has changeid\nmore text\n\nBug: 33\nSigned-off-by: me@you.too\n"
+						+ "Change-Id: I0123456789012345678901234567890123456789\n",
+				call("has changeid\nmore text\n\nBug: 33\nSigned-off-by: me@you.too\n"
+						+ "Change-Id: I0123456789012345678901234567890123456789\n"));
 	}
 
 	@Test
 	public void testHasChangeidWithReplacement() throws Exception {
 		assertEquals(
-				"has changeid\n\nBug: 33\nmore text\nSigned-off-by: me@you.too\nChange-Id: I988d2d7a6f2c0578fccabd4ebd3cec0768bc7f9f\nAnd then some\n",
-				call("has changeid\n\nBug: 33\nmore text\nSigned-off-by: me@you.too\nChange-Id: I0123456789012345678901234567890123456789\nAnd then some\n",
+				"has changeid\nmore text\n\nSigned-off-by: me@you.too\n"
+						+ "Change-Id: I2178563fada5edb2c99a8d8c0d619471b050ec24\nBug: 33\n",
+				call("has changeid\nmore text\n\nSigned-off-by: me@you.too\n"
+						+ "Change-Id: I0123456789012345678901234567890123456789\nBug: 33\n",
+						true));
+	}
+
+	@Test
+	public void testHasChangeidWithReplacementInLastLine() throws Exception {
+		assertEquals(
+				"has changeid\nmore text\n\nBug: 33\nSigned-off-by: me@you.too\n"
+						+ "Change-Id: I1d6578f4c96e3db4dd707705fe3d17bf658c4758\n",
+				call("has changeid\nmore text\n\nBug: 33\nSigned-off-by: me@you.too\n"
+						+ "Change-Id: I0123456789012345678901234567890123456789\n",
+						true));
+	}
+
+	@Test
+	public void testHasChangeidWithReplacementInLastLineNoLineBreak()
+			throws Exception {
+		assertEquals(
+				"has changeid\nmore text\n\nBug: 33\nSigned-off-by: me@you.too\n"
+						+ "Change-Id: I1d6578f4c96e3db4dd707705fe3d17bf658c4758",
+				call("has changeid\nmore text\n\nBug: 33\nSigned-off-by: me@you.too\n"
+						+ "Change-Id: I0123456789012345678901234567890123456789",
+						true));
+	}
+
+	@Test
+	public void testHasChangeidWithSpacesBeforeId() throws Exception {
+		assertEquals(
+				"has changeid\nmore text\n\nBug: 33\nSigned-off-by: me@you.too\n"
+						+ "Change-Id: Ie7575eaf450fdd0002df2e642426faf251de3ad9\n",
+				call("has changeid\nmore text\n\nBug: 33\nSigned-off-by: me@you.too\n"
+						+ "Change-Id:    I0123456789012345678901234567890123456789\n",
+						true));
+	}
+
+	@Test
+	public void testHasChangeidWithReplacementWithChangeIdInCommitMessage()
+			throws Exception {
+		assertEquals(
+				"has changeid\nmore text\n"
+						+ "Change-Id: I0123456789012345678901234567890123456789\n\n"
+						+ "Bug: 33\nSigned-off-by: me@you.too\n"
+						+ "Change-Id: Ie48d10d59ef67995ca89688ac0171b88f10dd520\n",
+				call("has changeid\nmore text\n"
+						+ "Change-Id: I0123456789012345678901234567890123456789\n\n"
+						+ "Bug: 33\nSigned-off-by: me@you.too\n"
+						+ "Change-Id: I0123456789012345678901234567890123456789\n",
 						true));
 	}
 
@@ -587,6 +637,34 @@ public void testWithEndingURL() throws Exception {
 						"git://example.com/ fixes this\n"));
 	}
 
+	@Test
+	public void testIndexOfChangeId() {
+		assertEquals(3, ChangeIdUtil.indexOfChangeId("x\n" + "\n"
+				+ "Change-Id: I3b7e4e16b503ce00f07ba6ad01d97a356dad7701\n",
+				"\n"));
+		assertEquals(5, ChangeIdUtil.indexOfChangeId("x\r\n" + "\r\n"
+				+ "Change-Id: I3b7e4e16b503ce00f07ba6ad01d97a356dad7701\r\n",
+				"\r\n"));
+		assertEquals(3, ChangeIdUtil.indexOfChangeId("x\r" + "\r"
+				+ "Change-Id: I3b7e4e16b503ce00f07ba6ad01d97a356dad7701\r",
+				"\r"));
+		assertEquals(8, ChangeIdUtil.indexOfChangeId("x\ny\n\nz\n" + "\n"
+				+ "Change-Id: I3b7e4e16b503ce00f07ba6ad01d97a356dad7701\n",
+				"\n"));
+	}
+
+	@Test
+	public void testIndexOfFirstFooterLine() {
+		assertEquals(
+				2,
+				ChangeIdUtil.indexOfFirstFooterLine(new String[] { "a", "",
+						"Bug: 42", "Signed-Off-By: j.developer@a.com" }));
+		assertEquals(
+				3,
+				ChangeIdUtil.indexOfFirstFooterLine(new String[] { "a",
+						"Bug: 42", "", "Signed-Off-By: j.developer@a.com" }));
+	}
+
 	private void hookDoesNotModify(final String in) throws Exception {
 		assertEquals(in, call(in));
 	}
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
index 48cb487..6673a8c 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -477,6 +477,7 @@
 transportProtoSFTP=SFTP
 transportProtoSSH=SSH
 treeEntryAlreadyExists=Tree entry "{0}" already exists.
+treeFilterMarkerTooManyFilters=Too many markTreeFilters passed, maximum number is {0} (passed {1})
 treeIteratorDoesNotSupportRemove=TreeIterator does not support remove()
 treeWalkMustHaveExactlyTwoTrees=TreeWalk should have exactly two trees.
 truncatedHunkLinesMissingForAncestor=Truncated hunk, at least {0} lines missing for ancestor {1}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java
index 2147a2e..6a945e4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java
@@ -238,13 +238,10 @@ private void apply(File f, FileHeader fh)
 		if (!isChanged(oldLines, newLines))
 			return; // don't touch the file
 		StringBuilder sb = new StringBuilder();
-		final String eol = rt.size() == 0
-				|| (rt.size() == 1 && rt.isMissingNewlineAtEnd()) ? "\n" : rt //$NON-NLS-1$
-				.getLineDelimiter();
 		for (String l : newLines) {
-			sb.append(l);
-			if (eol != null)
-				sb.append(eol);
+			// don't bother handling line endings - if it was windows, the \r is
+			// still there!
+			sb.append(l).append('\n');
 		}
 		sb.deleteCharAt(sb.length() - 1);
 		FileWriter fw = new FileWriter(f);
@@ -252,7 +249,7 @@ private void apply(File f, FileHeader fh)
 		fw.close();
 	}
 
-	private boolean isChanged(List<String> ol, List<String> nl) {
+	private static boolean isChanged(List<String> ol, List<String> nl) {
 		if (ol.size() != nl.size())
 			return true;
 		for (int i = 0; i < ol.size(); i++)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java
index 1c930eb..8227216 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java
@@ -45,6 +45,7 @@
 
 import java.text.MessageFormat;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 import org.eclipse.jgit.internal.JGitText;
@@ -177,6 +178,21 @@ public String toString() {
 			public boolean isSuccessful() {
 				return false;
 			}
+		},
+		/**
+		 * Status representing a checkout conflict, meaning that nothing could
+		 * be merged, as the pre-scan for the trees already failed for certain
+		 * files (i.e. local modifications prevent checkout of files).
+		 */
+		CHECKOUT_CONFLICT {
+			public String toString() {
+				return "Checkout Conflict";
+			}
+
+			@Override
+			public boolean isSuccessful() {
+				return false;
+			}
 		};
 
 		/**
@@ -201,6 +217,8 @@ public boolean isSuccessful() {
 
 	private Map<String, MergeFailureReason> failingPaths;
 
+	private List<String> checkoutConflicts;
+
 	/**
 	 * @param newHead
 	 *            the object the head points at after the merge
@@ -295,6 +313,18 @@ public MergeResult(ObjectId newHead, ObjectId base,
 	}
 
 	/**
+	 * Creates a new result that represents a checkout conflict before the
+	 * operation even started for real.
+	 *
+	 * @param checkoutConflicts
+	 *            the conflicting files
+	 */
+	public MergeResult(List<String> checkoutConflicts) {
+		this.checkoutConflicts = checkoutConflicts;
+		this.mergeStatus = MergeStatus.CHECKOUT_CONFLICT;
+	}
+
+	/**
 	 * @return the object the head points at after the merge
 	 */
 	public ObjectId getNewHead() {
@@ -450,4 +480,14 @@ public void addConflict(String path, org.eclipse.jgit.merge.MergeResult<?> lowLe
 	public Map<String, MergeFailureReason> getFailingPaths() {
 		return failingPaths;
 	}
+
+	/**
+	 * Returns a list of paths that cause a checkout conflict. These paths
+	 * prevent the operation from even starting.
+	 *
+	 * @return the list of files that caused the checkout conflict.
+	 */
+	public List<String> getCheckoutConflicts() {
+		return checkoutConflicts;
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
index 3f7feb9..5158c85 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
@@ -355,6 +355,8 @@ public RebaseResult call() throws GitAPIException, NoHeadException,
 				return RebaseResult.OK_RESULT;
 			}
 			return RebaseResult.FAST_FORWARD_RESULT;
+		} catch (CheckoutConflictException cce) {
+			return new RebaseResult(cce.getConflictingPaths());
 		} catch (IOException ioe) {
 			throw new JGitInternalException(ioe.getMessage(), ioe);
 		}
@@ -880,13 +882,18 @@ private String readFile(File directory, String fileName) throws IOException {
 		return RawParseUtils.decode(content, 0, end);
 	}
 
-	private boolean checkoutCommit(RevCommit commit) throws IOException {
+	private boolean checkoutCommit(RevCommit commit) throws IOException,
+			CheckoutConflictException {
 		try {
 			RevCommit head = walk.parseCommit(repo.resolve(Constants.HEAD));
 			DirCacheCheckout dco = new DirCacheCheckout(repo, head.getTree(),
 					repo.lockDirCache(), commit.getTree());
 			dco.setFailOnConflict(true);
-			dco.checkout();
+			try {
+				dco.checkout();
+			} catch (org.eclipse.jgit.errors.CheckoutConflictException cce) {
+				throw new CheckoutConflictException(dco.getConflicts(), cce);
+			}
 			// update the HEAD
 			RefUpdate refUpdate = repo.updateRef(Constants.HEAD, true);
 			refUpdate.setExpectedOldObjectId(head);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseResult.java
index a09f8c2..5965557 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseResult.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseResult.java
@@ -42,6 +42,7 @@
  */
 package org.eclipse.jgit.api;
 
+import java.util.List;
 import java.util.Map;
 
 import org.eclipse.jgit.merge.ResolveMerger;
@@ -93,6 +94,15 @@ public boolean isSuccessful() {
 			}
 		},
 		/**
+		 * Conflicts: checkout of target HEAD failed
+		 */
+		CONFLICTS {
+			@Override
+			public boolean isSuccessful() {
+				return false;
+			}
+		},
+		/**
 		 * Already up-to-date
 		 */
 		UP_TO_DATE {
@@ -148,6 +158,8 @@ public boolean isSuccessful() {
 
 	private Map<String, MergeFailureReason> failingPaths;
 
+	private List<String> conflicts;
+
 	private RebaseResult(Status status) {
 		this.status = status;
 		currentCommit = null;
@@ -177,6 +189,18 @@ private RebaseResult(Status status) {
 	}
 
 	/**
+	 * Create <code>RebaseResult</code> with status {@link Status#CONFLICTS}
+	 *
+	 * @param conflicts
+	 *            the list of conflicting paths
+	 */
+	RebaseResult(List<String> conflicts) {
+		status = Status.CONFLICTS;
+		currentCommit = null;
+		this.conflicts = conflicts;
+	}
+
+	/**
 	 * @return the overall status
 	 */
 	public Status getStatus() {
@@ -199,4 +223,11 @@ public RevCommit getCurrentCommit() {
 	public Map<String, MergeFailureReason> getFailingPaths() {
 		return failingPaths;
 	}
+
+	/**
+	 * @return the list of conflicts if status is {@link Status#CONFLICTS}
+	 */
+	public List<String> getConflicts() {
+		return conflicts;
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java
index cc8d285..a3d4e09 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2010, Google Inc.
+ * Copyright (C) 2008-2013, Google Inc.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -55,6 +55,8 @@
 import org.eclipse.jgit.lib.MutableObjectId;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.filter.TreeFilter;
+import org.eclipse.jgit.treewalk.filter.TreeFilterMarker;
 
 /** A value class representing a change to a file */
 public class DiffEntry {
@@ -123,7 +125,7 @@ public static List<DiffEntry> scan(TreeWalk walk) throws IOException {
 	 *            when {@code includeTrees} parameter is {@code true} it can't
 	 *            be recursive.
 	 * @param includeTrees
-	 *            include tree object's.
+	 *            include tree objects.
 	 * @return headers describing the changed files.
 	 * @throws IOException
 	 *             the repository cannot be accessed.
@@ -134,6 +136,36 @@ public static List<DiffEntry> scan(TreeWalk walk) throws IOException {
 	 */
 	public static List<DiffEntry> scan(TreeWalk walk, boolean includeTrees)
 			throws IOException {
+		return scan(walk, includeTrees, null);
+	}
+
+	/**
+	 * Convert the TreeWalk into DiffEntry headers, depending on
+	 * {@code includeTrees} it will add tree objects into result or not.
+	 *
+	 * @param walk
+	 *            the TreeWalk to walk through. Must have exactly two trees and
+	 *            when {@code includeTrees} parameter is {@code true} it can't
+	 *            be recursive.
+	 * @param includeTrees
+	 *            include tree objects.
+	 * @param markTreeFilters
+	 *            array of tree filters which will be tested for each entry. If
+	 *            an entry matches, the entry will later return true when
+	 *            queried through {{@link #isMarked(int)} (with the index from
+	 *            this passed array).
+	 * @return headers describing the changed files.
+	 * @throws IOException
+	 *             the repository cannot be accessed.
+	 * @throws IllegalArgumentException
+	 *             when {@code includeTrees} is true and given TreeWalk is
+	 *             recursive. Or when given TreeWalk doesn't have exactly two
+	 *             trees
+	 * @since 2.3
+	 */
+	public static List<DiffEntry> scan(TreeWalk walk, boolean includeTrees,
+			TreeFilter[] markTreeFilters)
+			throws IOException {
 		if (walk.getTreeCount() != 2)
 			throw new IllegalArgumentException(
 					JGitText.get().treeWalkMustHaveExactlyTwoTrees);
@@ -141,6 +173,12 @@ public static List<DiffEntry> scan(TreeWalk walk, boolean includeTrees)
 			throw new IllegalArgumentException(
 					JGitText.get().cannotBeRecursiveWhenTreesAreIncluded);
 
+		TreeFilterMarker treeFilterMarker;
+		if (markTreeFilters != null && markTreeFilters.length > 0)
+			treeFilterMarker = new TreeFilterMarker(markTreeFilters);
+		else
+			treeFilterMarker = null;
+
 		List<DiffEntry> r = new ArrayList<DiffEntry>();
 		MutableObjectId idBuf = new MutableObjectId();
 		while (walk.next()) {
@@ -156,6 +194,9 @@ public static List<DiffEntry> scan(TreeWalk walk, boolean includeTrees)
 			entry.newMode = walk.getFileMode(1);
 			entry.newPath = entry.oldPath = walk.getPathString();
 
+			if (treeFilterMarker != null)
+				entry.treeFilterMarks = treeFilterMarker.getMarks(walk);
+
 			if (entry.oldMode == FileMode.MISSING) {
 				entry.oldPath = DiffEntry.DEV_NULL;
 				entry.changeType = ChangeType.ADD;
@@ -295,6 +336,12 @@ static DiffEntry pair(ChangeType changeType, DiffEntry src, DiffEntry dst,
 	protected AbbreviatedObjectId newId;
 
 	/**
+	 * Bitset for marked flags of tree filters passed to
+	 * {@link #scan(TreeWalk, boolean, TreeFilter...)}
+	 */
+	private int treeFilterMarks = 0;
+
+	/**
 	 * Get the old name associated with this file.
 	 * <p>
 	 * The meaning of the old name can differ depending on the semantic meaning
@@ -397,6 +444,48 @@ public AbbreviatedObjectId getNewId() {
 	}
 
 	/**
+	 * Whether the mark tree filter with the specified index matched during scan
+	 * or not, see {@link #scan(TreeWalk, boolean, TreeFilter...)}. Example:
+	 * <p>
+	 *
+	 * <pre>
+	 * TreeFilter filterA = ...;
+	 * TreeFilter filterB = ...;
+	 * List<DiffEntry> entries = DiffEntry.scan(walk, false, filterA, filterB);
+	 * DiffEntry entry = entries.get(0);
+	 * boolean filterAMatched = entry.isMarked(0);
+	 * boolean filterBMatched = entry.isMarked(1);
+	 * </pre>
+	 * <p>
+	 * Note that 0 corresponds to filterA because it was the first filter that
+	 * was passed to scan.
+	 * <p>
+	 * To query more than one flag at once, see {@link #getTreeFilterMarks()}.
+	 *
+	 * @param index
+	 *            the index of the tree filter to check for (must be between 0
+	 *            and {@link Integer#SIZE}).
+	 *
+	 * @return true, if the tree filter matched; false if not
+	 * @since 2.3
+	 */
+	public boolean isMarked(int index) {
+		return (treeFilterMarks & (1L << index)) != 0;
+	}
+
+	/**
+	 * Get the raw tree filter marks, as set during
+	 * {@link #scan(TreeWalk, boolean, TreeFilter...)}. See
+	 * {@link #isMarked(int)} to query each mark individually.
+	 *
+	 * @return the bitset of tree filter marks
+	 * @since 2.3
+	 */
+	public int getTreeFilterMarks() {
+		return treeFilterMarks;
+	}
+
+	/**
 	 * Get the object id.
 	 *
 	 * @param side
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
index 2876843..d402f13 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010, Sasa Zivkov <sasa.zivkov@sap.com>
+ * Copyright (C) 2010, 2013 Sasa Zivkov <sasa.zivkov@sap.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -538,6 +538,7 @@ public static JGitText get() {
 	/***/ public String transportProtoSFTP;
 	/***/ public String transportProtoSSH;
 	/***/ public String treeEntryAlreadyExists;
+	/***/ public String treeFilterMarkerTooManyFilters;
 	/***/ public String treeIteratorDoesNotSupportRemove;
 	/***/ public String treeWalkMustHaveExactlyTwoTrees;
 	/***/ public String truncatedHunkLinesMissingForAncestor;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java
index 29fa8d6..d94e728 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java
@@ -48,6 +48,7 @@
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.util.ChangeIdUtil;
 import org.eclipse.jgit.util.StringUtils;
 
 /**
@@ -133,14 +134,26 @@ else if (ref.getName().equals(ref.getObjectId().getName()))
 	 */
 	public String formatWithConflicts(String message,
 			List<String> conflictingPaths) {
-		StringBuilder sb = new StringBuilder(message);
-		if (!message.endsWith("\n") && message.length() != 0) //$NON-NLS-1$
-			sb.append("\n"); //$NON-NLS-1$
-		sb.append("\n"); //$NON-NLS-1$
-		sb.append("Conflicts:\n");
+		StringBuilder sb = new StringBuilder();
+		String[] lines = message.split("\n"); //$NON-NLS-1$
+		int firstFooterLine = ChangeIdUtil.indexOfFirstFooterLine(lines);
+		for (int i = 0; i < firstFooterLine; i++)
+			sb.append(lines[i]).append('\n');
+		if (firstFooterLine == lines.length && message.length() != 0)
+			sb.append('\n');
+		addConflictsMessage(conflictingPaths, sb);
+		if (firstFooterLine < lines.length)
+			sb.append('\n');
+		for (int i = firstFooterLine; i < lines.length; i++)
+			sb.append(lines[i]).append('\n');
+		return sb.toString();
+	}
+
+	private static void addConflictsMessage(List<String> conflictingPaths,
+			StringBuilder sb) {
+		sb.append("Conflicts:\n"); //$NON-NLS-1$
 		for (String conflictingPath : conflictingPaths)
 			sb.append('\t').append(conflictingPath).append('\n');
-		return sb.toString();
 	}
 
 	private static String joinNames(List<String> names, String singular,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsOutputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsOutputStream.java
index 9a2aff4..2070d80 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsOutputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsOutputStream.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, Google Inc.
+ * Copyright (C) 2011, 2012 Google Inc. and others.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -47,10 +47,12 @@
 import java.io.OutputStream;
 import java.nio.ByteBuffer;
 
+import org.eclipse.jgit.storage.pack.PackExt;
+
 /**
  * Output stream to create a file on the DFS.
  *
- * @see DfsObjDatabase#writeFile(DfsPackDescription, String)
+ * @see DfsObjDatabase#writeFile(DfsPackDescription, PackExt)
  */
 public abstract class DfsOutputStream extends OutputStream {
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackDescription.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackDescription.java
index 3d3d80d..a793d3c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackDescription.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackDescription.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, Google Inc.
+ * Copyright (C) 2011, 2013 Google Inc., and others.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -84,7 +84,7 @@ public class DfsPackDescription implements Comparable<DfsPackDescription> {
 	 * <p>
 	 * The corresponding index file is assumed to exist. If this is not true
 	 * implementors must extend the class and override
-	 * {@link #getFileName(String)}.
+	 * {@link #getFileName(PackExt)}.
 	 * <p>
 	 * Callers should also try to fill in other fields if they are reasonably
 	 * free to access at the time this instance is being initialized.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/TreeFilterMarker.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/TreeFilterMarker.java
new file mode 100644
index 0000000..59515dc
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/TreeFilterMarker.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2013, Robin Stocker <robin@nibor.org>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.treewalk.filter;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.errors.StopWalkException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.treewalk.TreeWalk;
+
+/**
+ * For testing an array of {@link TreeFilter} during a {@link TreeWalk} for each
+ * entry and returning the result as a bitmask.
+ *
+ * @since 2.3
+ */
+public class TreeFilterMarker {
+
+	private final TreeFilter[] filters;
+
+	/**
+	 * Construct a TreeFilterMarker. Note that it is stateful and can only be
+	 * used for one walk loop.
+	 *
+	 * @param markTreeFilters
+	 *            the filters to use for marking, must not have more elements
+	 *            than {@link Integer#SIZE}.
+	 * @throws IllegalArgumentException
+	 *             if more tree filters are passed than possible
+	 */
+	public TreeFilterMarker(TreeFilter[] markTreeFilters) {
+		if (markTreeFilters.length > Integer.SIZE) {
+			throw new IllegalArgumentException(MessageFormat.format(
+					JGitText.get().treeFilterMarkerTooManyFilters,
+					Integer.valueOf(Integer.SIZE),
+					Integer.valueOf(markTreeFilters.length)));
+		}
+		filters = new TreeFilter[markTreeFilters.length];
+		System.arraycopy(markTreeFilters, 0, filters, 0, markTreeFilters.length);
+	}
+
+	/**
+	 * Test the filters against the walk. Returns a bitmask where each bit
+	 * represents the result of a call to {@link TreeFilter#include(TreeWalk)},
+	 * ordered by the index for which the tree filters were passed in the
+	 * constructor.
+	 *
+	 * @param walk
+	 *            the walk from which to test the current entry
+	 * @return the marks bitmask
+	 * @throws MissingObjectException
+	 *             as thrown by {@link TreeFilter#include(TreeWalk)}
+	 * @throws IncorrectObjectTypeException
+	 *             as thrown by {@link TreeFilter#include(TreeWalk)}
+	 * @throws IOException
+	 *             as thrown by {@link TreeFilter#include(TreeWalk)}
+	 */
+	public int getMarks(TreeWalk walk) throws MissingObjectException,
+			IncorrectObjectTypeException, IOException {
+		int marks = 0;
+		for (int index = 0; index < filters.length; index++) {
+			TreeFilter filter = filters[index];
+			if (filter != null) {
+				try {
+					boolean marked = filter.include(walk);
+					if (marked)
+						marks |= (1L << index);
+				} catch (StopWalkException e) {
+					// Don't check tree filter anymore, it will no longer
+					// match
+					filters[index] = null;
+				}
+			}
+		}
+		return marks;
+	}
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java
index a869b2b..41bb4cc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java
@@ -161,36 +161,26 @@ public static String insertId(String message, ObjectId changeId) {
 	 */
 	public static String insertId(String message, ObjectId changeId,
 			boolean replaceExisting) {
-		if (message.indexOf(CHANGE_ID) > 0) {
-			if (replaceExisting) {
-				int i = message.indexOf(CHANGE_ID) + 10;
-				while (message.charAt(i) == ' ')
-					i++;
-				String oldId = message.length() == (i + 40) ?
-						message.substring(i) : message.substring(i, i + 41);
-				message = message.replace(oldId, "I" + changeId.getName()); //$NON-NLS-1$
+		int indexOfChangeId = indexOfChangeId(message, "\n"); //$NON-NLS-1$
+		if (indexOfChangeId > 0) {
+			if (!replaceExisting)
+				return message;
+			else {
+				StringBuilder ret = new StringBuilder(message.substring(0,
+						indexOfChangeId));
+				ret.append(CHANGE_ID);
+				ret.append(" I"); //$NON-NLS-1$
+				ret.append(ObjectId.toString(changeId));
+				int indexOfNextLineBreak = message.indexOf("\n", //$NON-NLS-1$
+						indexOfChangeId);
+				if (indexOfNextLineBreak > 0)
+					ret.append(message.substring(indexOfNextLineBreak));
+				return ret.toString();
 			}
-			return message;
 		}
 
 		String[] lines = message.split("\n"); //$NON-NLS-1$
-		int footerFirstLine = lines.length;
-		for (int i = lines.length - 1; i > 1; --i) {
-			if (footerPattern.matcher(lines[i]).matches()) {
-				footerFirstLine = i;
-				continue;
-			}
-			if (footerFirstLine != lines.length && lines[i].length() == 0) {
-				break;
-			}
-			if (footerFirstLine != lines.length
-					&& includeInFooterPattern.matcher(lines[i]).matches()) {
-				footerFirstLine = i + 1;
-				continue;
-			}
-			footerFirstLine = lines.length;
-			break;
-		}
+		int footerFirstLine = indexOfFirstFooterLine(lines);
 		int insertAfter = footerFirstLine;
 		for (int i = footerFirstLine; i < lines.length; ++i) {
 			if (issuePattern.matcher(lines[i]).matches()) {
@@ -217,4 +207,57 @@ public static String insertId(String message, ObjectId changeId,
 		}
 		return ret.toString();
 	}
+
+	/**
+	 * Find the index in the String {@code} message} where the Change-Id entry
+	 * begins
+	 *
+	 * @param message
+	 * @param delimiter
+	 *            the line delimiter, like "\n" or "\r\n", needed to find the
+	 *            footer
+	 * @return the index of the ChangeId footer in the message, or -1 if no
+	 *         ChangeId footer available
+	 */
+	public static int indexOfChangeId(String message, String delimiter) {
+		String[] lines = message.split(delimiter);
+		int footerFirstLine = indexOfFirstFooterLine(lines);
+		if (footerFirstLine == lines.length)
+			return -1;
+
+		int indexOfFooter = 0;
+		for (int i = 0; i < footerFirstLine; ++i)
+			indexOfFooter += lines[i].length() + delimiter.length();
+		return message.indexOf(CHANGE_ID, indexOfFooter);
+	}
+
+	/**
+	 * Find the index of the first line of the footer paragraph in an array of
+	 * the lines, or lines.length if no footer is available
+	 *
+	 * @param lines
+	 *            the commit message split into lines and the line delimiters
+	 *            stripped off
+	 * @return the index of the first line of the footer paragraph, or
+	 *         lines.length if no footer is available
+	 */
+	public static int indexOfFirstFooterLine(String[] lines) {
+		int footerFirstLine = lines.length;
+		for (int i = lines.length - 1; i > 1; --i) {
+			if (footerPattern.matcher(lines[i]).matches()) {
+				footerFirstLine = i;
+				continue;
+			}
+			if (footerFirstLine != lines.length && lines[i].length() == 0)
+				break;
+			if (footerFirstLine != lines.length
+					&& includeInFooterPattern.matcher(lines[i]).matches()) {
+				footerFirstLine = i + 1;
+				continue;
+			}
+			footerFirstLine = lines.length;
+			break;
+		}
+		return footerFirstLine;
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
index ce62628..73f9161 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
@@ -90,7 +90,7 @@ protected File discoverGitPrefix() {
 		String path = SystemReader.getInstance().getenv("PATH"); //$NON-NLS-1$
 		File gitExe = searchPath(path, "git.exe", "git.cmd"); //$NON-NLS-1$ //$NON-NLS-2$
 		if (gitExe != null)
-			return gitExe.getParentFile().getParentFile();
+			return resolveGrandparentFile(gitExe);
 
 		// This isn't likely to work, if bash is in $PATH, git should
 		// also be in $PATH. But its worth trying.
@@ -102,7 +102,16 @@ protected File discoverGitPrefix() {
 			// The path may be in cygwin/msys notation so resolve it right away
 			gitExe = resolve(null, w);
 			if (gitExe != null)
-				return gitExe.getParentFile().getParentFile();
+				return resolveGrandparentFile(gitExe);
+		}
+		return null;
+	}
+
+	private static File resolveGrandparentFile(File grandchild) {
+		if (grandchild != null) {
+			File parent = grandchild.getParentFile();
+			if (parent != null)
+				return parent.getParentFile();
 		}
 		return null;
 	}
@@ -115,7 +124,8 @@ protected File userHomeImpl() {
 		String homeDrive = SystemReader.getInstance().getenv("HOMEDRIVE"); //$NON-NLS-1$
 		if (homeDrive != null) {
 			String homePath = SystemReader.getInstance().getenv("HOMEPATH"); //$NON-NLS-1$
-			return new File(homeDrive, homePath);
+			if (homePath != null)
+				return new File(homeDrive, homePath);
 		}
 
 		String homeShare = SystemReader.getInstance().getenv("HOMESHARE"); //$NON-NLS-1$
diff --git a/pom.xml b/pom.xml
index e452181..4738938 100644
--- a/pom.xml
+++ b/pom.xml
@@ -73,6 +73,12 @@
       <name>Christian Halstrick</name>
     </developer>
     <developer>
+      <name>Colby Ranger</name>
+    </developer>
+    <developer>
+      <name>Dave Borowitz</name>
+    </developer>
+    <developer>
       <name>Gunnar Wagenknecht</name>
     </developer>
     <developer>
@@ -170,7 +176,7 @@
     <bundle-manifest>${project.build.directory}/META-INF/MANIFEST.MF</bundle-manifest>
 
     <jgit-last-release-version>2.1.0.201209190230-r</jgit-last-release-version>
-    <jsch-version>0.1.44-1</jsch-version>
+    <jsch-version>0.1.46</jsch-version>
     <junit-version>4.5</junit-version>
     <args4j-version>2.0.12</args4j-version>
     <commons-compress-version>1.3</commons-compress-version>