Git development
 help / color / mirror / Atom feed
* [JGIT PATCH 02/12] Add tests to cover more methods of BinaryHunk
From: Shawn O. Pearce @ 2008-12-12 22:05 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git
In-Reply-To: <1229119558-1293-2-git-send-email-spearce@spearce.org>

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../tst/org/spearce/jgit/patch/PatchTest.java      |   16 ++++++++++++----
 1 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
index 5850364..ebd23b4 100644
--- a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
@@ -199,10 +199,18 @@ assertTrue(fh.getNewName().startsWith(
 			assertTrue(fh.getHunks().isEmpty());
 			assertTrue(fh.hasMetaDataChanges());
 
-			assertNotNull(fh.getForwardBinaryHunk());
-			assertNotNull(fh.getReverseBinaryHunk());
-			assertEquals(binsizes[i], fh.getForwardBinaryHunk().getSize());
-			assertEquals(0, fh.getReverseBinaryHunk().getSize());
+			final BinaryHunk fwd = fh.getForwardBinaryHunk();
+			final BinaryHunk rev = fh.getReverseBinaryHunk();
+			assertNotNull(fwd);
+			assertNotNull(rev);
+			assertEquals(binsizes[i], fwd.getSize());
+			assertEquals(0, rev.getSize());
+
+			assertSame(fh, fwd.getFileHeader());
+			assertSame(fh, rev.getFileHeader());
+
+			assertSame(BinaryHunk.Type.LITERAL_DEFLATED, fwd.getType());
+			assertSame(BinaryHunk.Type.LITERAL_DEFLATED, rev.getType());
 		}
 
 		final FileHeader fh = p.getFiles().get(4);
-- 
1.6.1.rc2.306.ge5d5e

^ permalink raw reply related

* [JGIT PATCH 03/12] Add a simple toString to FormatError to facilitate debugging
From: Shawn O. Pearce @ 2008-12-12 22:05 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git
In-Reply-To: <1229119558-1293-3-git-send-email-spearce@spearce.org>

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../src/org/spearce/jgit/patch/FormatError.java    |   14 ++++++++++++++
 1 files changed, 14 insertions(+), 0 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/FormatError.java b/org.spearce.jgit/src/org/spearce/jgit/patch/FormatError.java
index e6f0a03..ab75c63 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/patch/FormatError.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/FormatError.java
@@ -92,4 +92,18 @@ public String getLineText() {
 		final int eol = RawParseUtils.nextLF(buf, offset);
 		return RawParseUtils.decode(Constants.CHARSET, buf, offset, eol);
 	}
+
+	@Override
+	public String toString() {
+		final StringBuilder r = new StringBuilder();
+		r.append(getSeverity().name().toLowerCase());
+		r.append(": at offset ");
+		r.append(getOffset());
+		r.append(": ");
+		r.append(getMessage());
+		r.append("\n");
+		r.append("  in ");
+		r.append(getLineText());
+		return r.toString();
+	}
 }
-- 
1.6.1.rc2.306.ge5d5e

^ permalink raw reply related

* [JGIT PATCH 04/12] Allow FileHeader to create its HunkHeader children
From: Shawn O. Pearce @ 2008-12-12 22:05 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git
In-Reply-To: <1229119558-1293-4-git-send-email-spearce@spearce.org>

By using a factory method on FileHeader we can later subclass the
FileHeader class to handle "diff --cc" style patches, and let it
create its own subclass of HunkHeader to handle the specialized
form of the n-way diff.

The getParentCount() method is hard-coded to return 1 in the 2-way
diff case as there is exactly one parent.  But in a "diff --cc" we
need to verify the hunk header has the same number of parents as
the file header in order to parse the hunk.  So a subclass of the
FileHeader would need to override getParentCount() to return the
actual number of '@' symbols (less 1) that should appear in each
hunk header line.  (E.g. a 3-way diff shows "@@@ -" so the parent
count should be 2.)

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../src/org/spearce/jgit/patch/FileHeader.java     |    8 ++++++++
 .../src/org/spearce/jgit/patch/Patch.java          |    4 ++--
 2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java
index 5fe2acf..f93129d 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java
@@ -169,6 +169,10 @@ FileHeader(final byte[] b, final int offset) {
 		patchType = PatchType.UNIFIED;
 	}
 
+	int getParentCount() {
+		return 1;
+	}
+
 	/**
 	 * Get the old name associated with this file.
 	 * <p>
@@ -274,6 +278,10 @@ void addHunk(final HunkHeader h) {
 		hunks.add(h);
 	}
 
+	HunkHeader newHunkHeader(final int offset) {
+		return new HunkHeader(this, offset);
+	}
+
 	/** @return if a {@link PatchType#GIT_BINARY}, the new-image delta/literal */
 	public BinaryHunk getForwardBinaryHunk() {
 		return forwardBinaryHunk;
diff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java
index 77ae02f..05d034d 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java
@@ -281,8 +281,8 @@ private int parseHunks(final FileHeader fh, int c, final int end) {
 			if (match(buf, c, NEW_NAME) >= 0)
 				break;
 
-			if (isHunkHdr(buf, c, end) == 1) {
-				final HunkHeader h = new HunkHeader(fh, c);
+			if (isHunkHdr(buf, c, end) == fh.getParentCount()) {
+				final HunkHeader h = fh.newHunkHeader(c);
 				h.parseHeader(end);
 				c = h.parseBody(this, end);
 				h.endOffset = c;
-- 
1.6.1.rc2.306.ge5d5e

^ permalink raw reply related

* [JGIT PATCH 05/12] Refactor the old/pre-image data in HunkHeader to support >1 ancestor
From: Shawn O. Pearce @ 2008-12-12 22:05 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git
In-Reply-To: <1229119558-1293-5-git-send-email-spearce@spearce.org>

The "diff --cc" format uses more than one ancestor in each hunk,
so we need to expand the hunk header information in a way that
allows access to the data for each ancestor.  This change moves
the information relative to the old/pre-image ancestor in a 2-way
patch into an OldImage object.  In a 2-way patch we only have
one OldImage, but in a "diff --cc" patch we will have more than
one of these available in each hunk.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../spearce/jgit/patch/EGitPatchHistoryTest.java   |    4 +-
 .../tst/org/spearce/jgit/patch/PatchTest.java      |   36 +++---
 .../src/org/spearce/jgit/patch/HunkHeader.java     |  124 ++++++++++++--------
 3 files changed, 95 insertions(+), 69 deletions(-)

diff --git a/org.spearce.jgit.test/exttst/org/spearce/jgit/patch/EGitPatchHistoryTest.java b/org.spearce.jgit.test/exttst/org/spearce/jgit/patch/EGitPatchHistoryTest.java
index d0c2632..b170dc2 100644
--- a/org.spearce.jgit.test/exttst/org/spearce/jgit/patch/EGitPatchHistoryTest.java
+++ b/org.spearce.jgit.test/exttst/org/spearce/jgit/patch/EGitPatchHistoryTest.java
@@ -110,8 +110,8 @@ void onCommit(String cid, byte[] buf) {
 				assertNotNull("No " + nid, s);
 				int added = 0, deleted = 0;
 				for (final HunkHeader h : fh.getHunks()) {
-					added += h.getLinesAdded();
-					deleted += h.getLinesDeleted();
+					added += h.getOldImage().getLinesAdded();
+					deleted += h.getOldImage().getLinesDeleted();
 				}
 
 				if (s.added == added) {
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
index ebd23b4..4eceeb5 100644
--- a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
@@ -82,14 +82,14 @@ assertSame(FileHeader.PatchType.UNIFIED, fRepositoryConfigTest
 			final HunkHeader h = fRepositoryConfigTest.getHunks().get(0);
 			assertSame(fRepositoryConfigTest, h.getFileHeader());
 			assertEquals(921, h.startOffset);
-			assertEquals(109, h.getOldStartLine());
-			assertEquals(4, h.getOldLineCount());
+			assertEquals(109, h.getOldImage().getStartLine());
+			assertEquals(4, h.getOldImage().getLineCount());
 			assertEquals(109, h.getNewStartLine());
 			assertEquals(11, h.getNewLineCount());
 
 			assertEquals(4, h.getLinesContext());
-			assertEquals(7, h.getLinesAdded());
-			assertEquals(0, h.getLinesDeleted());
+			assertEquals(7, h.getOldImage().getLinesAdded());
+			assertEquals(0, h.getOldImage().getLinesDeleted());
 
 			assertEquals(1490, h.endOffset);
 		}
@@ -105,42 +105,42 @@ assertSame(FileHeader.PatchType.UNIFIED, fRepositoryConfig
 			final HunkHeader h = fRepositoryConfig.getHunks().get(0);
 			assertSame(fRepositoryConfig, h.getFileHeader());
 			assertEquals(1803, h.startOffset);
-			assertEquals(236, h.getOldStartLine());
-			assertEquals(9, h.getOldLineCount());
+			assertEquals(236, h.getOldImage().getStartLine());
+			assertEquals(9, h.getOldImage().getLineCount());
 			assertEquals(236, h.getNewStartLine());
 			assertEquals(9, h.getNewLineCount());
 
 			assertEquals(7, h.getLinesContext());
-			assertEquals(2, h.getLinesAdded());
-			assertEquals(2, h.getLinesDeleted());
+			assertEquals(2, h.getOldImage().getLinesAdded());
+			assertEquals(2, h.getOldImage().getLinesDeleted());
 
 			assertEquals(2434, h.endOffset);
 		}
 		{
 			final HunkHeader h = fRepositoryConfig.getHunks().get(1);
 			assertEquals(2434, h.startOffset);
-			assertEquals(300, h.getOldStartLine());
-			assertEquals(7, h.getOldLineCount());
+			assertEquals(300, h.getOldImage().getStartLine());
+			assertEquals(7, h.getOldImage().getLineCount());
 			assertEquals(300, h.getNewStartLine());
 			assertEquals(7, h.getNewLineCount());
 
 			assertEquals(6, h.getLinesContext());
-			assertEquals(1, h.getLinesAdded());
-			assertEquals(1, h.getLinesDeleted());
+			assertEquals(1, h.getOldImage().getLinesAdded());
+			assertEquals(1, h.getOldImage().getLinesDeleted());
 
 			assertEquals(2816, h.endOffset);
 		}
 		{
 			final HunkHeader h = fRepositoryConfig.getHunks().get(2);
 			assertEquals(2816, h.startOffset);
-			assertEquals(954, h.getOldStartLine());
-			assertEquals(7, h.getOldLineCount());
+			assertEquals(954, h.getOldImage().getStartLine());
+			assertEquals(7, h.getOldImage().getLineCount());
 			assertEquals(954, h.getNewStartLine());
 			assertEquals(7, h.getNewLineCount());
 
 			assertEquals(6, h.getLinesContext());
-			assertEquals(1, h.getLinesAdded());
-			assertEquals(1, h.getLinesDeleted());
+			assertEquals(1, h.getOldImage().getLinesAdded());
+			assertEquals(1, h.getOldImage().getLinesDeleted());
 
 			assertEquals(3035, h.endOffset);
 		}
@@ -177,7 +177,7 @@ assertTrue(fh.getNewName().startsWith(
 		assertNull(fh.getForwardBinaryHunk());
 		assertNull(fh.getReverseBinaryHunk());
 		assertEquals(1, fh.getHunks().size());
-		assertEquals(272, fh.getHunks().get(0).getOldStartLine());
+		assertEquals(272, fh.getHunks().get(0).getOldImage().getStartLine());
 	}
 
 	public void testParse_GitBinary() throws IOException {
@@ -222,7 +222,7 @@ assertTrue(fh.getNewName().startsWith(
 		assertNull(fh.getForwardBinaryHunk());
 		assertNull(fh.getReverseBinaryHunk());
 		assertEquals(1, fh.getHunks().size());
-		assertEquals(272, fh.getHunks().get(0).getOldStartLine());
+		assertEquals(272, fh.getHunks().get(0).getOldImage().getStartLine());
 	}
 
 	private Patch parseTestPatchFile() throws IOException {
diff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java
index c3bd642..842519e 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java
@@ -41,10 +41,49 @@
 import static org.spearce.jgit.util.RawParseUtils.nextLF;
 import static org.spearce.jgit.util.RawParseUtils.parseBase10;
 
+import org.spearce.jgit.lib.AbbreviatedObjectId;
 import org.spearce.jgit.util.MutableInteger;
 
 /** Hunk header describing the layout of a single block of lines */
 public class HunkHeader {
+	/** Details about an old image of the file. */
+	public abstract static class OldImage {
+		/** First line number the hunk starts on in this file. */
+		int startLine;
+
+		/** Total number of lines this hunk covers in this file. */
+		int lineCount;
+
+		/** Number of lines deleted by the post-image from this file. */
+		int nDeleted;
+
+		/** Number of lines added by the post-image not in this file. */
+		int nAdded;
+
+		/** @return first line number the hunk starts on in this file. */
+		public int getStartLine() {
+			return startLine;
+		}
+
+		/** @return total number of lines this hunk covers in this file. */
+		public int getLineCount() {
+			return lineCount;
+		}
+
+		/** @return number of lines deleted by the post-image from this file. */
+		public int getLinesDeleted() {
+			return nDeleted;
+		}
+
+		/** @return number of lines added by the post-image not in this file. */
+		public int getLinesAdded() {
+			return nAdded;
+		}
+
+		/** @return object id of the pre-image file. */
+		public abstract AbbreviatedObjectId getId();
+	}
+
 	private final FileHeader file;
 
 	/** Offset within {@link #file}.buf to the "@@ -" line. */
@@ -53,11 +92,7 @@
 	/** Position 1 past the end of this hunk within {@link #file}'s buf. */
 	int endOffset;
 
-	/** First line number in the pre-image file where the hunk starts */
-	int oldStartLine;
-
-	/** Total number of pre-image lines this hunk covers (context + deleted) */
-	int oldLineCount;
+	private final OldImage old;
 
 	/** First line number in the post-image file where the hunk starts */
 	int newStartLine;
@@ -68,15 +103,19 @@
 	/** Total number of lines of context appearing in this hunk */
 	int nContext;
 
-	/** Number of lines removed by this hunk */
-	int nDeleted;
-
-	/** Number of lines added by this hunk */
-	int nAdded;
-
 	HunkHeader(final FileHeader fh, final int offset) {
+		this(fh, offset, new OldImage() {
+			@Override
+			public AbbreviatedObjectId getId() {
+				return fh.getOldId();
+			}
+		});
+	}
+
+	HunkHeader(final FileHeader fh, final int offset, final OldImage oi) {
 		file = fh;
 		startOffset = offset;
+		old = oi;
 	}
 
 	/** @return header for the file this hunk applies to */
@@ -84,14 +123,9 @@ public FileHeader getFileHeader() {
 		return file;
 	}
 
-	/** @return first line number in the pre-image file where the hunk starts */
-	public int getOldStartLine() {
-		return oldStartLine;
-	}
-
-	/** @return total number of pre-image lines this hunk covers */
-	public int getOldLineCount() {
-		return oldLineCount;
+	/** @return information about the old image mentioned in this hunk. */
+	public OldImage getOldImage() {
+		return old;
 	}
 
 	/** @return first line number in the post-image file where the hunk starts */
@@ -109,28 +143,18 @@ public int getLinesContext() {
 		return nContext;
 	}
 
-	/** @return number of lines removed by this hunk */
-	public int getLinesDeleted() {
-		return nDeleted;
-	}
-
-	/** @return number of lines added by this hunk */
-	public int getLinesAdded() {
-		return nAdded;
-	}
-
 	void parseHeader(final int end) {
 		// Parse "@@ -236,9 +236,9 @@ protected boolean"
 		//
 		final byte[] buf = file.buf;
 		final MutableInteger ptr = new MutableInteger();
 		ptr.value = nextLF(buf, startOffset, ' ');
-		oldStartLine = -parseBase10(buf, ptr.value, ptr);
+		old.startLine = -parseBase10(buf, ptr.value, ptr);
 		if (buf[ptr.value] == ',')
-			oldLineCount = parseBase10(buf, ptr.value + 1, ptr);
+			old.lineCount = parseBase10(buf, ptr.value + 1, ptr);
 		else {
-			oldLineCount = oldStartLine;
-			oldStartLine = 0;
+			old.lineCount = old.startLine;
+			old.startLine = 0;
 		}
 
 		newStartLine = parseBase10(buf, ptr.value + 1, ptr);
@@ -146,8 +170,8 @@ int parseBody(final Patch script, final int end) {
 		final byte[] buf = file.buf;
 		int c = nextLF(buf, startOffset), last = c;
 
-		nDeleted = 0;
-		nAdded = 0;
+		old.nDeleted = 0;
+		old.nAdded = 0;
 
 		SCAN: for (; c < end; last = c, c = nextLF(buf, c)) {
 			switch (buf[c]) {
@@ -157,11 +181,11 @@ int parseBody(final Patch script, final int end) {
 				continue;
 
 			case '-':
-				nDeleted++;
+				old.nDeleted++;
 				continue;
 
 			case '+':
-				nAdded++;
+				old.nAdded++;
 				continue;
 
 			case '\\': // Matches "\ No newline at end of file"
@@ -172,33 +196,35 @@ int parseBody(final Patch script, final int end) {
 			}
 		}
 
-		if (last < end && nContext + nDeleted - 1 == oldLineCount
-				&& nContext + nAdded == newLineCount
+		if (last < end && nContext + old.nDeleted - 1 == old.lineCount
+				&& nContext + old.nAdded == newLineCount
 				&& match(buf, last, Patch.SIG_FOOTER) >= 0) {
 			// This is an extremely common occurrence of "corruption".
 			// Users add footers with their signatures after this mark,
 			// and git diff adds the git executable version number.
 			// Let it slide; the hunk otherwise looked sound.
 			//
-			nDeleted--;
+			old.nDeleted--;
 			return last;
 		}
 
-		if (nContext + nDeleted < oldLineCount) {
-			final int missingCount = oldLineCount - (nContext + nDeleted);
+		if (nContext + old.nDeleted < old.lineCount) {
+			final int missingCount = old.lineCount - (nContext + old.nDeleted);
 			script.error(buf, startOffset, "Truncated hunk, at least "
 					+ missingCount + " old lines is missing");
 
-		} else if (nContext + nAdded < newLineCount) {
-			final int missingCount = newLineCount - (nContext + nAdded);
+		} else if (nContext + old.nAdded < newLineCount) {
+			final int missingCount = newLineCount - (nContext + old.nAdded);
 			script.error(buf, startOffset, "Truncated hunk, at least "
 					+ missingCount + " new lines is missing");
 
-		} else if (nContext + nDeleted > oldLineCount
-				|| nContext + nAdded > newLineCount) {
-			script.warn(buf, startOffset, "Hunk header " + oldLineCount + ":"
-					+ newLineCount + " does not match body line count of "
-					+ (nContext + nDeleted) + ":" + (nContext + nAdded));
+		} else if (nContext + old.nDeleted > old.lineCount
+				|| nContext + old.nAdded > newLineCount) {
+			final String oldcnt = old.lineCount + ":" + newLineCount;
+			final String newcnt = (nContext + old.nDeleted) + ":"
+					+ (nContext + old.nAdded);
+			script.warn(buf, startOffset, "Hunk header " + oldcnt
+					+ " does not match body line count of " + newcnt);
 		}
 
 		return c;
-- 
1.6.1.rc2.306.ge5d5e

^ permalink raw reply related

* [JGIT PATCH 09/12] Add test cases for parsing "\ No newline at end of file" style patches
From: Shawn O. Pearce @ 2008-12-12 22:05 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git
In-Reply-To: <1229119558-1293-9-git-send-email-spearce@spearce.org>

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../tst/org/spearce/jgit/patch/PatchTest.java      |   68 ++++++++++++++++++++
 .../jgit/patch/testParse_AddNoNewline.patch        |   20 ++++++
 .../jgit/patch/testParse_FixNoNewline.patch        |   20 ++++++
 3 files changed, 108 insertions(+), 0 deletions(-)
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_AddNoNewline.patch
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_FixNoNewline.patch

diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
index c81356b..13eab5f 100644
--- a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
@@ -228,6 +228,74 @@ assertTrue(fh.getNewName().startsWith(
 		assertEquals(272, fh.getHunks().get(0).getOldImage().getStartLine());
 	}
 
+	public void testParse_FixNoNewline() throws IOException {
+		final Patch p = parseTestPatchFile();
+		assertEquals(1, p.getFiles().size());
+		assertTrue(p.getErrors().isEmpty());
+
+		final FileHeader f = p.getFiles().get(0);
+
+		assertEquals("a", f.getNewName());
+		assertEquals(252, f.startOffset);
+
+		assertEquals("2e65efe", f.getOldId().name());
+		assertEquals("f2ad6c7", f.getNewId().name());
+		assertSame(FileHeader.PatchType.UNIFIED, f.getPatchType());
+		assertSame(FileMode.REGULAR_FILE, f.getOldMode());
+		assertSame(FileMode.REGULAR_FILE, f.getNewMode());
+		assertEquals(1, f.getHunks().size());
+		{
+			final HunkHeader h = f.getHunks().get(0);
+			assertSame(f, h.getFileHeader());
+			assertEquals(317, h.startOffset);
+			assertEquals(1, h.getOldImage().getStartLine());
+			assertEquals(1, h.getOldImage().getLineCount());
+			assertEquals(1, h.getNewStartLine());
+			assertEquals(1, h.getNewLineCount());
+
+			assertEquals(0, h.getLinesContext());
+			assertEquals(1, h.getOldImage().getLinesAdded());
+			assertEquals(1, h.getOldImage().getLinesDeleted());
+			assertSame(f.getOldId(), h.getOldImage().getId());
+
+			assertEquals(363, h.endOffset);
+		}
+	}
+
+	public void testParse_AddNoNewline() throws IOException {
+		final Patch p = parseTestPatchFile();
+		assertEquals(1, p.getFiles().size());
+		assertTrue(p.getErrors().isEmpty());
+
+		final FileHeader f = p.getFiles().get(0);
+
+		assertEquals("a", f.getNewName());
+		assertEquals(256, f.startOffset);
+
+		assertEquals("f2ad6c7", f.getOldId().name());
+		assertEquals("c59d9b6", f.getNewId().name());
+		assertSame(FileHeader.PatchType.UNIFIED, f.getPatchType());
+		assertSame(FileMode.REGULAR_FILE, f.getOldMode());
+		assertSame(FileMode.REGULAR_FILE, f.getNewMode());
+		assertEquals(1, f.getHunks().size());
+		{
+			final HunkHeader h = f.getHunks().get(0);
+			assertSame(f, h.getFileHeader());
+			assertEquals(321, h.startOffset);
+			assertEquals(1, h.getOldImage().getStartLine());
+			assertEquals(1, h.getOldImage().getLineCount());
+			assertEquals(1, h.getNewStartLine());
+			assertEquals(1, h.getNewLineCount());
+
+			assertEquals(0, h.getLinesContext());
+			assertEquals(1, h.getOldImage().getLinesAdded());
+			assertEquals(1, h.getOldImage().getLinesDeleted());
+			assertSame(f.getOldId(), h.getOldImage().getId());
+
+			assertEquals(367, h.endOffset);
+		}
+	}
+
 	private Patch parseTestPatchFile() throws IOException {
 		final String patchFile = getName() + ".patch";
 		final InputStream in = getClass().getResourceAsStream(patchFile);
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_AddNoNewline.patch b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_AddNoNewline.patch
new file mode 100644
index 0000000..3060952
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_AddNoNewline.patch
@@ -0,0 +1,20 @@
+From ca4719a4b2d93a469f61d1ddfb3e39ecbabfcd69 Mon Sep 17 00:00:00 2001
+From: Shawn O. Pearce <sop@google.com>
+Date: Fri, 12 Dec 2008 12:35:14 -0800
+Subject: [PATCH] introduce no lf again
+
+---
+ a |    2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/a b/a
+index f2ad6c7..c59d9b6 100644
+--- a/a
++++ b/a
+@@ -1 +1 @@
+-c
++d
+\ No newline at end of file
+-- 
+1.6.1.rc2.306.ge5d5e
+
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_FixNoNewline.patch b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_FixNoNewline.patch
new file mode 100644
index 0000000..e8af2e7
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_FixNoNewline.patch
@@ -0,0 +1,20 @@
+From 1beb3ec1fe68ff18b0287396096442e12c34787a Mon Sep 17 00:00:00 2001
+From: Shawn O. Pearce <sop@google.com>
+Date: Fri, 12 Dec 2008 12:29:45 -0800
+Subject: [PATCH] make c and add lf
+
+---
+ a |    2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/a b/a
+index 2e65efe..f2ad6c7 100644
+--- a/a
++++ b/a
+@@ -1 +1 @@
+-a
+\ No newline at end of file
++c
+-- 
+1.6.1.rc2.306.ge5d5e
+
-- 
1.6.1.rc2.306.ge5d5e

^ permalink raw reply related

* [JGIT PATCH 07/12] Allow a stray LF at the end of a hunk
From: Shawn O. Pearce @ 2008-12-12 22:05 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git
In-Reply-To: <1229119558-1293-7-git-send-email-spearce@spearce.org>

If a hunk ends and is followed by a stray LF its not worth creating
a warning for.  A single extra blank line isn't all that interesting
relative to the other sorts of data we might find at the end of a
patch hunk.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../src/org/spearce/jgit/patch/Patch.java          |   13 ++++++++++---
 1 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java
index 05d034d..51f1fe5 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java
@@ -287,9 +287,16 @@ private int parseHunks(final FileHeader fh, int c, final int end) {
 				c = h.parseBody(this, end);
 				h.endOffset = c;
 				fh.addHunk(h);
-				if (c < end && buf[c] != '@' && buf[c] != 'd'
-						&& match(buf, c, SIG_FOOTER) < 0) {
-					warn(buf, c, "Unexpected hunk trailer");
+				if (c < end) {
+					switch (buf[c]) {
+					case '@':
+					case 'd':
+					case '\n':
+						break;
+					default:
+						if (match(buf, c, SIG_FOOTER) < 0)
+							warn(buf, c, "Unexpected hunk trailer");
+					}
 				}
 				continue;
 			}
-- 
1.6.1.rc2.306.ge5d5e

^ permalink raw reply related

* [JGIT PATCH 10/12] Use FileMode.MISSING when a file is added or deleted rather than null
From: Shawn O. Pearce @ 2008-12-12 22:05 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git
In-Reply-To: <1229119558-1293-10-git-send-email-spearce@spearce.org>

Null is better used to indicate "no mode information at all" in the
patch, while FileMode.MISSING is already commonly used within the
TreeWalk code to mean "this path doesn't exist in this tree".  In
the context of a patch to create or delete a file, MISSING makes a
lot more sense for the application to work with.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../tst/org/spearce/jgit/patch/FileHeaderTest.java |    4 ++--
 .../tst/org/spearce/jgit/patch/PatchTest.java      |    1 +
 .../src/org/spearce/jgit/patch/FileHeader.java     |    2 ++
 3 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/FileHeaderTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/FileHeaderTest.java
index 36c528e..69e06ab 100644
--- a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/FileHeaderTest.java
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/FileHeaderTest.java
@@ -153,7 +153,7 @@ public void testParseUnicodeName_NewFile() {
 		assertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType());
 		assertTrue(fh.hasMetaDataChanges());
 
-		assertNull(fh.getOldMode());
+		assertSame(FileMode.MISSING, fh.getOldMode());
 		assertSame(FileMode.REGULAR_FILE, fh.getNewMode());
 
 		assertEquals("0000000", fh.getOldId().name());
@@ -179,7 +179,7 @@ public void testParseUnicodeName_DeleteFile() {
 		assertTrue(fh.hasMetaDataChanges());
 
 		assertSame(FileMode.REGULAR_FILE, fh.getOldMode());
-		assertNull(fh.getNewMode());
+		assertSame(FileMode.MISSING, fh.getNewMode());
 
 		assertEquals("7898192", fh.getOldId().name());
 		assertEquals("0000000", fh.getNewId().name());
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
index 13eab5f..2c617d3 100644
--- a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
@@ -160,6 +160,7 @@ public void testParse_NoBinary() throws IOException {
 			assertNotNull(fh.getOldId());
 			assertNotNull(fh.getNewId());
 			assertEquals("0000000", fh.getOldId().name());
+			assertSame(FileMode.MISSING, fh.getOldMode());
 			assertSame(FileMode.REGULAR_FILE, fh.getNewMode());
 			assertTrue(fh.getNewName().startsWith(
 					"org.spearce.egit.ui/icons/toolbar/"));
diff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java
index f93129d..48d7623 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java
@@ -386,9 +386,11 @@ int parseGitHeaders(int ptr, final int end) {
 
 			} else if (match(buf, ptr, DELETED_FILE_MODE) >= 0) {
 				oldMode = parseFileMode(ptr + DELETED_FILE_MODE.length, eol);
+				newMode = FileMode.MISSING;
 				changeType = ChangeType.DELETE;
 
 			} else if (match(buf, ptr, NEW_FILE_MODE) >= 0) {
+				oldMode = FileMode.MISSING;
 				newMode = parseFileMode(ptr + NEW_FILE_MODE.length, eol);
 				changeType = ChangeType.ADD;
 
-- 
1.6.1.rc2.306.ge5d5e

^ permalink raw reply related

* [JGIT PATCH 12/12] Add support for parsing "diff --cc" style patches
From: Shawn O. Pearce @ 2008-12-12 22:05 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git
In-Reply-To: <1229119558-1293-12-git-send-email-spearce@spearce.org>

Even though the diff --cc format used by Git is only meant to be
read by humans, JGit needs to be able to parse these to get the
patch metadata so it can be shown in a user interface to facilitate
code review processes.

Patches are parsed into the specialized CombinedFileHeader and
CombinedHunkHeader classes, where the old image information is
augmented with additional fields for the arbitrary number of parents
that can appear in such patches.  These cost more in terms of memory,
but "diff --cc" style patches tend to occur very infrequently as
they only occur during a merge conflict resolution.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../org/spearce/jgit/patch/PatchCcErrorTest.java   |   97 +++++++++
 .../tst/org/spearce/jgit/patch/PatchCcTest.java    |  200 ++++++++++++++++++
 .../jgit/patch/testError_CcTruncatedOld.patch      |   24 +++
 .../jgit/patch/testParse_CcDeleteFile.patch        |   12 +
 .../spearce/jgit/patch/testParse_CcNewFile.patch   |   14 ++
 .../spearce/jgit/patch/testParse_OneFileCc.patch   |   27 +++
 .../org/spearce/jgit/patch/CombinedFileHeader.java |  213 ++++++++++++++++++++
 .../org/spearce/jgit/patch/CombinedHunkHeader.java |  191 ++++++++++++++++++
 .../src/org/spearce/jgit/patch/FileHeader.java     |   56 +++---
 .../src/org/spearce/jgit/patch/HunkHeader.java     |    2 +-
 .../src/org/spearce/jgit/patch/Patch.java          |    7 +-
 11 files changed, 814 insertions(+), 29 deletions(-)
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchCcErrorTest.java
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchCcTest.java
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_CcTruncatedOld.patch
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_CcDeleteFile.patch
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_CcNewFile.patch
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_OneFileCc.patch
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/patch/CombinedFileHeader.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/patch/CombinedHunkHeader.java

diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchCcErrorTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchCcErrorTest.java
new file mode 100644
index 0000000..a2f3a19
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchCcErrorTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2008, Google Inc.
+ *
+ * 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 Git Development Community 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.spearce.jgit.patch;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import junit.framework.TestCase;
+
+public class PatchCcErrorTest extends TestCase {
+	public void testError_CcTruncatedOld() throws IOException {
+		final Patch p = parseTestPatchFile();
+		assertEquals(1, p.getFiles().size());
+		assertEquals(3, p.getErrors().size());
+		{
+			final FormatError e = p.getErrors().get(0);
+			assertSame(FormatError.Severity.ERROR, e.getSeverity());
+			assertEquals(
+					"Truncated hunk, at least 1 lines is missing for ancestor 1",
+					e.getMessage());
+			assertEquals(346, e.getOffset());
+			assertTrue(e.getLineText().startsWith(
+					"@@@ -55,12 -163,13 +163,15 @@@ public "));
+		}
+		{
+			final FormatError e = p.getErrors().get(1);
+			assertSame(FormatError.Severity.ERROR, e.getSeverity());
+			assertEquals(
+					"Truncated hunk, at least 2 lines is missing for ancestor 2",
+					e.getMessage());
+			assertEquals(346, e.getOffset());
+			assertTrue(e.getLineText().startsWith(
+					"@@@ -55,12 -163,13 +163,15 @@@ public "));
+		}
+		{
+			final FormatError e = p.getErrors().get(2);
+			assertSame(FormatError.Severity.ERROR, e.getSeverity());
+			assertEquals("Truncated hunk, at least 3 new lines is missing", e
+					.getMessage());
+			assertEquals(346, e.getOffset());
+			assertTrue(e.getLineText().startsWith(
+					"@@@ -55,12 -163,13 +163,15 @@@ public "));
+		}
+	}
+
+	private Patch parseTestPatchFile() throws IOException {
+		final String patchFile = getName() + ".patch";
+		final InputStream in = getClass().getResourceAsStream(patchFile);
+		if (in == null) {
+			fail("No " + patchFile + " test vector");
+			return null; // Never happens
+		}
+		try {
+			final Patch p = new Patch();
+			p.parse(in);
+			return p;
+		} finally {
+			in.close();
+		}
+	}
+
+}
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchCcTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchCcTest.java
new file mode 100644
index 0000000..9e8650b
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchCcTest.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2008, Google Inc.
+ *
+ * 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 Git Development Community 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.spearce.jgit.patch;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.spearce.jgit.lib.FileMode;
+
+import junit.framework.TestCase;
+
+public class PatchCcTest extends TestCase {
+	public void testParse_OneFileCc() throws IOException {
+		final Patch p = parseTestPatchFile();
+		assertEquals(1, p.getFiles().size());
+		assertTrue(p.getErrors().isEmpty());
+
+		final CombinedFileHeader cfh = (CombinedFileHeader) p.getFiles().get(0);
+
+		assertEquals("org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java",
+				cfh.getNewName());
+		assertEquals(cfh.getNewName(), cfh.getOldName());
+
+		assertEquals(98, cfh.startOffset);
+
+		assertEquals(2, cfh.getParentCount());
+		assertSame(cfh.getOldId(0), cfh.getOldId());
+		assertEquals("169356b", cfh.getOldId(0).name());
+		assertEquals("dd8c317", cfh.getOldId(1).name());
+		assertEquals("fd85931", cfh.getNewId().name());
+
+		assertSame(cfh.getOldMode(0), cfh.getOldMode());
+		assertSame(FileMode.REGULAR_FILE, cfh.getOldMode(0));
+		assertSame(FileMode.REGULAR_FILE, cfh.getOldMode(1));
+		assertSame(FileMode.EXECUTABLE_FILE, cfh.getNewMode());
+		assertSame(FileHeader.ChangeType.MODIFY, cfh.getChangeType());
+		assertSame(FileHeader.PatchType.UNIFIED, cfh.getPatchType());
+
+		assertEquals(1, cfh.getHunks().size());
+		{
+			final CombinedHunkHeader h = cfh.getHunks().get(0);
+
+			assertSame(cfh, h.getFileHeader());
+			assertEquals(346, h.startOffset);
+			assertEquals(764, h.endOffset);
+
+			assertSame(h.getOldImage(0), h.getOldImage());
+			assertSame(cfh.getOldId(0), h.getOldImage(0).getId());
+			assertSame(cfh.getOldId(1), h.getOldImage(1).getId());
+
+			assertEquals(55, h.getOldImage(0).getStartLine());
+			assertEquals(12, h.getOldImage(0).getLineCount());
+			assertEquals(3, h.getOldImage(0).getLinesAdded());
+			assertEquals(0, h.getOldImage(0).getLinesDeleted());
+
+			assertEquals(163, h.getOldImage(1).getStartLine());
+			assertEquals(13, h.getOldImage(1).getLineCount());
+			assertEquals(2, h.getOldImage(1).getLinesAdded());
+			assertEquals(0, h.getOldImage(1).getLinesDeleted());
+
+			assertEquals(163, h.getNewStartLine());
+			assertEquals(15, h.getNewLineCount());
+
+			assertEquals(10, h.getLinesContext());
+		}
+	}
+
+	public void testParse_CcNewFile() throws IOException {
+		final Patch p = parseTestPatchFile();
+		assertEquals(1, p.getFiles().size());
+		assertTrue(p.getErrors().isEmpty());
+
+		final CombinedFileHeader cfh = (CombinedFileHeader) p.getFiles().get(0);
+
+		assertSame(FileHeader.DEV_NULL, cfh.getOldName());
+		assertEquals("d", cfh.getNewName());
+
+		assertEquals(187, cfh.startOffset);
+
+		assertEquals(2, cfh.getParentCount());
+		assertSame(cfh.getOldId(0), cfh.getOldId());
+		assertEquals("0000000", cfh.getOldId(0).name());
+		assertEquals("0000000", cfh.getOldId(1).name());
+		assertEquals("4bcfe98", cfh.getNewId().name());
+
+		assertSame(cfh.getOldMode(0), cfh.getOldMode());
+		assertSame(FileMode.MISSING, cfh.getOldMode(0));
+		assertSame(FileMode.MISSING, cfh.getOldMode(1));
+		assertSame(FileMode.REGULAR_FILE, cfh.getNewMode());
+		assertSame(FileHeader.ChangeType.ADD, cfh.getChangeType());
+		assertSame(FileHeader.PatchType.UNIFIED, cfh.getPatchType());
+
+		assertEquals(1, cfh.getHunks().size());
+		{
+			final CombinedHunkHeader h = cfh.getHunks().get(0);
+
+			assertSame(cfh, h.getFileHeader());
+			assertEquals(273, h.startOffset);
+			assertEquals(300, h.endOffset);
+
+			assertSame(h.getOldImage(0), h.getOldImage());
+			assertSame(cfh.getOldId(0), h.getOldImage(0).getId());
+			assertSame(cfh.getOldId(1), h.getOldImage(1).getId());
+
+			assertEquals(1, h.getOldImage(0).getStartLine());
+			assertEquals(0, h.getOldImage(0).getLineCount());
+			assertEquals(1, h.getOldImage(0).getLinesAdded());
+			assertEquals(0, h.getOldImage(0).getLinesDeleted());
+
+			assertEquals(1, h.getOldImage(1).getStartLine());
+			assertEquals(0, h.getOldImage(1).getLineCount());
+			assertEquals(1, h.getOldImage(1).getLinesAdded());
+			assertEquals(0, h.getOldImage(1).getLinesDeleted());
+
+			assertEquals(1, h.getNewStartLine());
+			assertEquals(1, h.getNewLineCount());
+
+			assertEquals(0, h.getLinesContext());
+		}
+	}
+
+	public void testParse_CcDeleteFile() throws IOException {
+		final Patch p = parseTestPatchFile();
+		assertEquals(1, p.getFiles().size());
+		assertTrue(p.getErrors().isEmpty());
+
+		final CombinedFileHeader cfh = (CombinedFileHeader) p.getFiles().get(0);
+
+		assertEquals("a", cfh.getOldName());
+		assertSame(FileHeader.DEV_NULL, cfh.getNewName());
+
+		assertEquals(187, cfh.startOffset);
+
+		assertEquals(2, cfh.getParentCount());
+		assertSame(cfh.getOldId(0), cfh.getOldId());
+		assertEquals("7898192", cfh.getOldId(0).name());
+		assertEquals("2e65efe", cfh.getOldId(1).name());
+		assertEquals("0000000", cfh.getNewId().name());
+
+		assertSame(cfh.getOldMode(0), cfh.getOldMode());
+		assertSame(FileMode.REGULAR_FILE, cfh.getOldMode(0));
+		assertSame(FileMode.REGULAR_FILE, cfh.getOldMode(1));
+		assertSame(FileMode.MISSING, cfh.getNewMode());
+		assertSame(FileHeader.ChangeType.DELETE, cfh.getChangeType());
+		assertSame(FileHeader.PatchType.UNIFIED, cfh.getPatchType());
+
+		assertTrue(cfh.getHunks().isEmpty());
+	}
+
+	private Patch parseTestPatchFile() throws IOException {
+		final String patchFile = getName() + ".patch";
+		final InputStream in = getClass().getResourceAsStream(patchFile);
+		if (in == null) {
+			fail("No " + patchFile + " test vector");
+			return null; // Never happens
+		}
+		try {
+			final Patch p = new Patch();
+			p.parse(in);
+			return p;
+		} finally {
+			in.close();
+		}
+	}
+}
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_CcTruncatedOld.patch b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_CcTruncatedOld.patch
new file mode 100644
index 0000000..1bbcfb5
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_CcTruncatedOld.patch
@@ -0,0 +1,24 @@
+commit 1a56639bbea8e8cbfbe5da87746de97f9217ce9b
+Date:   Tue May 13 00:43:56 2008 +0200
+      ...
+
+diff --cc org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java
+index 169356b,dd8c317..fd85931
+mode 100644,100644..100755
+--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java
++++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java
+@@@ -55,12 -163,13 +163,15 @@@ public class UIText extends NLS 
+  
+  	/** */
+  	public static String ResourceHistory_toggleCommentWrap;
++ 
+  	/** */
+ +	/** */
+  	public static String ResourceHistory_toggleRevDetail;
+  	/** */
+  	public static String ResourceHistory_toggleRevComment;
+  	/** */
+  	public static String ResourceHistory_toggleTooltips;
+  
+
+commit 1a56639bbea8e8cbfbe5da87746de97f9217ce9b
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_CcDeleteFile.patch b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_CcDeleteFile.patch
new file mode 100644
index 0000000..2654e09
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_CcDeleteFile.patch
@@ -0,0 +1,12 @@
+commit 740709ece2412856c0c3eabd4dc4a4cf115b0de6
+Merge: 5c19b43... 13a2c0d...
+Author: Shawn O. Pearce <sop@google.com>
+Date:   Fri Dec 12 13:26:52 2008 -0800
+
+    Merge branch 'b' into d
+
+diff --cc a
+index 7898192,2e65efe..0000000
+deleted file mode 100644,100644
+--- a/a
++++ /dev/null
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_CcNewFile.patch b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_CcNewFile.patch
new file mode 100644
index 0000000..1a9b7b0
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_CcNewFile.patch
@@ -0,0 +1,14 @@
+commit 6cb8160a4717d51fd3cc0baf721946daa60cf921
+Merge: 5c19b43... 13a2c0d...
+Author: Shawn O. Pearce <sop@google.com>
+Date:   Fri Dec 12 13:26:52 2008 -0800
+
+    Merge branch 'b' into d
+
+diff --cc d
+index 0000000,0000000..4bcfe98
+new file mode 100644
+--- /dev/null
++++ b/d
+@@@ -1,0 -1,0 +1,1 @@@
+++d
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_OneFileCc.patch b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_OneFileCc.patch
new file mode 100644
index 0000000..c096b33
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_OneFileCc.patch
@@ -0,0 +1,27 @@
+commit 1a56639bbea8e8cbfbe5da87746de97f9217ce9b
+Date:   Tue May 13 00:43:56 2008 +0200
+      ...
+
+diff --cc org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java
+index 169356b,dd8c317..fd85931
+mode 100644,100644..100755
+--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java
++++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java
+@@@ -55,12 -163,13 +163,15 @@@ public class UIText extends NLS 
+  
+  	/** */
+  	public static String ResourceHistory_toggleCommentWrap;
++ 
+  	/** */
+ +	public static String ResourceHistory_toggleCommentFill;
+ +	/** */
+  	public static String ResourceHistory_toggleRevDetail;
++ 
+  	/** */
+  	public static String ResourceHistory_toggleRevComment;
++ 
+  	/** */
+  	public static String ResourceHistory_toggleTooltips;
+  
+
+commit 1a56639bbea8e8cbfbe5da87746de97f9217ce9b
diff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/CombinedFileHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/CombinedFileHeader.java
new file mode 100644
index 0000000..7171600
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/CombinedFileHeader.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2008, Google Inc.
+ *
+ * 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 Git Development Community 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.spearce.jgit.patch;
+
+import static org.spearce.jgit.lib.Constants.encodeASCII;
+import static org.spearce.jgit.util.RawParseUtils.match;
+import static org.spearce.jgit.util.RawParseUtils.nextLF;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.spearce.jgit.lib.AbbreviatedObjectId;
+import org.spearce.jgit.lib.FileMode;
+
+/**
+ * A file in the Git "diff --cc" or "diff --combined" format.
+ * <p>
+ * A combined diff shows an n-way comparison between two or more ancestors and
+ * the final revision. Its primary function is to perform code reviews on a
+ * merge which introduces changes not in any ancestor.
+ */
+public class CombinedFileHeader extends FileHeader {
+	private static final byte[] MODE = encodeASCII("mode ");
+
+	private AbbreviatedObjectId[] oldIds;
+
+	private FileMode[] oldModes;
+
+	CombinedFileHeader(final byte[] b, final int offset) {
+		super(b, offset);
+	}
+
+	@Override
+	@SuppressWarnings("unchecked")
+	public List<? extends CombinedHunkHeader> getHunks() {
+		return (List<CombinedHunkHeader>) super.getHunks();
+	}
+
+	/** @return number of ancestor revisions mentioned in this diff. */
+	@Override
+	public int getParentCount() {
+		return oldIds.length;
+	}
+
+	/** @return get the file mode of the first parent. */
+	@Override
+	public FileMode getOldMode() {
+		return getOldMode(0);
+	}
+
+	/**
+	 * Get the file mode of the nth ancestor
+	 * 
+	 * @param nthParent
+	 *            the ancestor to get the mode of
+	 * @return the mode of the requested ancestor.
+	 */
+	public FileMode getOldMode(final int nthParent) {
+		return oldModes[nthParent];
+	}
+
+	/** @return get the object id of the first parent. */
+	@Override
+	public AbbreviatedObjectId getOldId() {
+		return getOldId(0);
+	}
+
+	/**
+	 * Get the ObjectId of the nth ancestor
+	 * 
+	 * @param nthParent
+	 *            the ancestor to get the object id of
+	 * @return the id of the requested ancestor.
+	 */
+	public AbbreviatedObjectId getOldId(final int nthParent) {
+		return oldIds[nthParent];
+	}
+
+	int parseGitHeaders(int ptr, final int end) {
+		while (ptr < end) {
+			final int eol = nextLF(buf, ptr);
+			if (isHunkHdr(buf, ptr, end) >= 1) {
+				// First hunk header; break out and parse them later.
+				break;
+
+			} else if (match(buf, ptr, OLD_NAME) >= 0) {
+				parseOldName(ptr, eol);
+
+			} else if (match(buf, ptr, NEW_NAME) >= 0) {
+				parseNewName(ptr, eol);
+
+			} else if (match(buf, ptr, INDEX) >= 0) {
+				parseIndexLine(ptr + INDEX.length, eol);
+
+			} else if (match(buf, ptr, MODE) >= 0) {
+				parseModeLine(ptr + MODE.length, eol);
+
+			} else if (match(buf, ptr, NEW_FILE_MODE) >= 0) {
+				parseNewFileMode(ptr, eol);
+
+			} else if (match(buf, ptr, DELETED_FILE_MODE) >= 0) {
+				parseDeletedFileMode(ptr + DELETED_FILE_MODE.length, eol);
+
+			} else {
+				// Probably an empty patch (stat dirty).
+				break;
+			}
+
+			ptr = eol;
+		}
+		return ptr;
+	}
+
+	@Override
+	protected void parseIndexLine(int ptr, final int eol) {
+		// "index $asha1,$bsha1..$csha1"
+		//
+		final List<AbbreviatedObjectId> ids = new ArrayList<AbbreviatedObjectId>();
+		while (ptr < eol) {
+			final int comma = nextLF(buf, ptr, ',');
+			if (eol <= comma)
+				break;
+			ids.add(AbbreviatedObjectId.fromString(buf, ptr, comma - 1));
+			ptr = comma;
+		}
+
+		oldIds = new AbbreviatedObjectId[ids.size() + 1];
+		ids.toArray(oldIds);
+		final int dot2 = nextLF(buf, ptr, '.');
+		oldIds[ids.size()] = AbbreviatedObjectId.fromString(buf, ptr, dot2 - 1);
+		newId = AbbreviatedObjectId.fromString(buf, dot2 + 1, eol - 1);
+		oldModes = new FileMode[oldIds.length];
+	}
+
+	@Override
+	protected void parseNewFileMode(final int ptr, final int eol) {
+		for (int i = 0; i < oldModes.length; i++)
+			oldModes[i] = FileMode.MISSING;
+		super.parseNewFileMode(ptr, eol);
+	}
+
+	@Override
+	HunkHeader newHunkHeader(final int offset) {
+		return new CombinedHunkHeader(this, offset);
+	}
+
+	private void parseModeLine(int ptr, final int eol) {
+		// "mode $amode,$bmode..$cmode"
+		//
+		int n = 0;
+		while (ptr < eol) {
+			final int comma = nextLF(buf, ptr, ',');
+			if (eol <= comma)
+				break;
+			oldModes[n++] = parseFileMode(ptr, comma);
+			ptr = comma;
+		}
+		final int dot2 = nextLF(buf, ptr, '.');
+		oldModes[n] = parseFileMode(ptr, dot2);
+		newMode = parseFileMode(dot2 + 1, eol);
+	}
+
+	private void parseDeletedFileMode(int ptr, final int eol) {
+		// "deleted file mode $amode,$bmode"
+		//
+		changeType = ChangeType.DELETE;
+		int n = 0;
+		while (ptr < eol) {
+			final int comma = nextLF(buf, ptr, ',');
+			if (eol <= comma)
+				break;
+			oldModes[n++] = parseFileMode(ptr, comma);
+			ptr = comma;
+		}
+		oldModes[n] = parseFileMode(ptr, eol);
+		newMode = FileMode.MISSING;
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/CombinedHunkHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/CombinedHunkHeader.java
new file mode 100644
index 0000000..bebeafa
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/CombinedHunkHeader.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2008, Google Inc.
+ *
+ * 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 Git Development Community 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.spearce.jgit.patch;
+
+import static org.spearce.jgit.util.RawParseUtils.nextLF;
+import static org.spearce.jgit.util.RawParseUtils.parseBase10;
+
+import org.spearce.jgit.lib.AbbreviatedObjectId;
+import org.spearce.jgit.util.MutableInteger;
+
+/** Hunk header for a hunk appearing in a "diff --cc" style patch. */
+public class CombinedHunkHeader extends HunkHeader {
+	private static abstract class CombinedOldImage extends OldImage {
+		int nContext;
+	}
+
+	private CombinedOldImage[] old;
+
+	CombinedHunkHeader(final CombinedFileHeader fh, final int offset) {
+		super(fh, offset, null);
+		old = new CombinedOldImage[fh.getParentCount()];
+		for (int i = 0; i < old.length; i++) {
+			final int imagePos = i;
+			old[i] = new CombinedOldImage() {
+				@Override
+				public AbbreviatedObjectId getId() {
+					return fh.getOldId(imagePos);
+				}
+			};
+		}
+	}
+
+	@Override
+	public CombinedFileHeader getFileHeader() {
+		return (CombinedFileHeader) super.getFileHeader();
+	}
+
+	@Override
+	public OldImage getOldImage() {
+		return getOldImage(0);
+	}
+
+	/**
+	 * Get the OldImage data related to the nth ancestor
+	 * 
+	 * @param nthParent
+	 *            the ancestor to get the old image data of
+	 * @return image data of the requested ancestor.
+	 */
+	public OldImage getOldImage(final int nthParent) {
+		return old[nthParent];
+	}
+
+	@Override
+	void parseHeader(final int end) {
+		// Parse "@@@ -55,12 -163,13 +163,15 @@@ protected boolean"
+		//
+		final byte[] buf = file.buf;
+		final MutableInteger ptr = new MutableInteger();
+		ptr.value = nextLF(buf, startOffset, ' ');
+
+		for (int n = 0; n < old.length; n++) {
+			old[n].startLine = -parseBase10(buf, ptr.value, ptr);
+			if (buf[ptr.value] == ',')
+				old[n].lineCount = parseBase10(buf, ptr.value + 1, ptr);
+			else
+				old[n].lineCount = 1;
+		}
+
+		newStartLine = parseBase10(buf, ptr.value + 1, ptr);
+		if (buf[ptr.value] == ',')
+			newLineCount = parseBase10(buf, ptr.value + 1, ptr);
+		else
+			newLineCount = 1;
+	}
+
+	@Override
+	int parseBody(final Patch script, final int end) {
+		final byte[] buf = file.buf;
+		int c = nextLF(buf, startOffset);
+
+		for (final CombinedOldImage o : old) {
+			o.nDeleted = 0;
+			o.nAdded = 0;
+			o.nContext = 0;
+		}
+		nContext = 0;
+		int nAdded = 0;
+
+		SCAN: for (int eol; c < end; c = eol) {
+			eol = nextLF(buf, c);
+
+			if (eol - c < old.length + 1) {
+				// Line isn't long enough to mention the state of each
+				// ancestor. It must be the end of the hunk.
+				break SCAN;
+			}
+
+			switch (buf[c]) {
+			case ' ':
+			case '-':
+			case '+':
+				break;
+
+			default:
+				// Line can't possibly be part of this hunk; the first
+				// ancestor information isn't recognizable.
+				//
+				break SCAN;
+			}
+
+			int localcontext = 0;
+			for (int ancestor = 0; ancestor < old.length; ancestor++) {
+				switch (buf[c + ancestor]) {
+				case ' ':
+					localcontext++;
+					old[ancestor].nContext++;
+					continue;
+
+				case '-':
+					old[ancestor].nDeleted++;
+					continue;
+
+				case '+':
+					old[ancestor].nAdded++;
+					nAdded++;
+					continue;
+
+				default:
+					break SCAN;
+				}
+			}
+			if (localcontext == old.length)
+				nContext++;
+		}
+
+		for (int ancestor = 0; ancestor < old.length; ancestor++) {
+			final CombinedOldImage o = old[ancestor];
+			final int cmp = o.nContext + o.nDeleted;
+			if (cmp < o.lineCount) {
+				final int missingCnt = o.lineCount - cmp;
+				script.error(buf, startOffset, "Truncated hunk, at least "
+						+ missingCnt + " lines is missing for ancestor "
+						+ (ancestor + 1));
+			}
+		}
+
+		if (nContext + nAdded < newLineCount) {
+			final int missingCount = newLineCount - (nContext + nAdded);
+			script.error(buf, startOffset, "Truncated hunk, at least "
+					+ missingCount + " new lines is missing");
+		}
+
+		return c;
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java
index 48d7623..79e4b0a 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java
@@ -61,9 +61,9 @@
 
 	private static final byte[] NEW_MODE = encodeASCII("new mode ");
 
-	private static final byte[] DELETED_FILE_MODE = encodeASCII("deleted file mode ");
+	protected static final byte[] DELETED_FILE_MODE = encodeASCII("deleted file mode ");
 
-	private static final byte[] NEW_FILE_MODE = encodeASCII("new file mode ");
+	protected static final byte[] NEW_FILE_MODE = encodeASCII("new file mode ");
 
 	private static final byte[] COPY_FROM = encodeASCII("copy from ");
 
@@ -81,7 +81,7 @@
 
 	private static final byte[] DISSIMILARITY_INDEX = encodeASCII("dissimilarity index ");
 
-	private static final byte[] INDEX = encodeASCII("index ");
+	protected static final byte[] INDEX = encodeASCII("index ");
 
 	static final byte[] OLD_NAME = encodeASCII("--- ");
 
@@ -136,10 +136,10 @@
 	private FileMode oldMode;
 
 	/** New mode of the file, if described by the patch, else null. */
-	private FileMode newMode;
+	protected FileMode newMode;
 
 	/** General type of change indicated by the patch. */
-	private ChangeType changeType;
+	protected ChangeType changeType;
 
 	/** Similarity score if {@link #changeType} is a copy or rename. */
 	private int score;
@@ -148,7 +148,7 @@
 	private AbbreviatedObjectId oldId;
 
 	/** ObjectId listed on the index line for the new (post-image) */
-	private AbbreviatedObjectId newId;
+	protected AbbreviatedObjectId newId;
 
 	/** Type of patch used to modify this file */
 	PatchType patchType;
@@ -264,7 +264,7 @@ public boolean hasMetaDataChanges() {
 	}
 
 	/** @return hunks altering this file; in order of appearance in patch */
-	public List<HunkHeader> getHunks() {
+	public List<? extends HunkHeader> getHunks() {
 		if (hunks == null)
 			return Collections.emptyList();
 		return hunks;
@@ -369,14 +369,10 @@ int parseGitHeaders(int ptr, final int end) {
 				break;
 
 			} else if (match(buf, ptr, OLD_NAME) >= 0) {
-				oldName = p1(parseName(oldName, ptr + OLD_NAME.length, eol));
-				if (oldName == DEV_NULL)
-					changeType = ChangeType.ADD;
+				parseOldName(ptr, eol);
 
 			} else if (match(buf, ptr, NEW_NAME) >= 0) {
-				newName = p1(parseName(newName, ptr + NEW_NAME.length, eol));
-				if (newName == DEV_NULL)
-					changeType = ChangeType.DELETE;
+				parseNewName(ptr, eol);
 
 			} else if (match(buf, ptr, OLD_MODE) >= 0) {
 				oldMode = parseFileMode(ptr + OLD_MODE.length, eol);
@@ -390,9 +386,7 @@ int parseGitHeaders(int ptr, final int end) {
 				changeType = ChangeType.DELETE;
 
 			} else if (match(buf, ptr, NEW_FILE_MODE) >= 0) {
-				oldMode = FileMode.MISSING;
-				newMode = parseFileMode(ptr + NEW_FILE_MODE.length, eol);
-				changeType = ChangeType.ADD;
+				parseNewFileMode(ptr, eol);
 
 			} else if (match(buf, ptr, COPY_FROM) >= 0) {
 				oldName = parseName(oldName, ptr + COPY_FROM.length, eol);
@@ -437,6 +431,24 @@ int parseGitHeaders(int ptr, final int end) {
 		return ptr;
 	}
 
+	protected void parseOldName(int ptr, final int eol) {
+		oldName = p1(parseName(oldName, ptr + OLD_NAME.length, eol));
+		if (oldName == DEV_NULL)
+			changeType = ChangeType.ADD;
+	}
+
+	protected void parseNewName(int ptr, final int eol) {
+		newName = p1(parseName(newName, ptr + NEW_NAME.length, eol));
+		if (newName == DEV_NULL)
+			changeType = ChangeType.DELETE;
+	}
+
+	protected void parseNewFileMode(int ptr, final int eol) {
+		oldMode = FileMode.MISSING;
+		newMode = parseFileMode(ptr + NEW_FILE_MODE.length, eol);
+		changeType = ChangeType.ADD;
+	}
+
 	int parseTraditionalHeaders(int ptr, final int end) {
 		while (ptr < end) {
 			final int eol = nextLF(buf, ptr);
@@ -445,14 +457,10 @@ int parseTraditionalHeaders(int ptr, final int end) {
 				break;
 
 			} else if (match(buf, ptr, OLD_NAME) >= 0) {
-				oldName = p1(parseName(oldName, ptr + OLD_NAME.length, eol));
-				if (oldName == DEV_NULL)
-					changeType = ChangeType.ADD;
+				parseOldName(ptr, eol);
 
 			} else if (match(buf, ptr, NEW_NAME) >= 0) {
-				newName = p1(parseName(newName, ptr + NEW_NAME.length, eol));
-				if (newName == DEV_NULL)
-					changeType = ChangeType.DELETE;
+				parseNewName(ptr, eol);
 
 			} else {
 				// Possibly an empty patch.
@@ -494,7 +502,7 @@ private static String p1(final String r) {
 		return s > 0 ? r.substring(s + 1) : r;
 	}
 
-	private FileMode parseFileMode(int ptr, final int end) {
+	protected FileMode parseFileMode(int ptr, final int end) {
 		int tmp = 0;
 		while (ptr < end - 1) {
 			tmp <<= 3;
@@ -503,7 +511,7 @@ private FileMode parseFileMode(int ptr, final int end) {
 		return FileMode.fromBits(tmp);
 	}
 
-	private void parseIndexLine(int ptr, final int end) {
+	protected void parseIndexLine(int ptr, final int end) {
 		// "index $asha1..$bsha1[ $mode]" where $asha1 and $bsha1
 		// can be unique abbreviations
 		//
diff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java
index f543aed..fc149ac 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java
@@ -84,7 +84,7 @@ public int getLinesAdded() {
 		public abstract AbbreviatedObjectId getId();
 	}
 
-	private final FileHeader file;
+	final FileHeader file;
 
 	/** Offset within {@link #file}.buf to the "@@ -" line. */
 	final int startOffset;
diff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java
index 51f1fe5..f23ba69 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java
@@ -94,7 +94,7 @@ public void addFile(final FileHeader fh) {
 	}
 
 	/** @return list of files described in the patch, in occurrence order. */
-	public List<FileHeader> getFiles() {
+	public List<? extends FileHeader> getFiles() {
 		return files;
 	}
 
@@ -238,9 +238,8 @@ private int parseDiffCombined(final byte[] hdr, final byte[] buf,
 		if (ptr < 0)
 			return skipFile(buf, start, end);
 
-		// TODO Support parsing diff --cc headers
-		// TODO parse diff --cc hunks
-		warn(buf, start, "diff --cc format not supported");
+		ptr = fh.parseGitHeaders(ptr, end);
+		ptr = parseHunks(fh, ptr, end);
 		fh.endOffset = ptr;
 		addFile(fh);
 		return ptr;
-- 
1.6.1.rc2.306.ge5d5e

^ permalink raw reply related

* [JGIT PATCH 11/12] Add a test for delta binary patch parsing and fix a bug in it
From: Shawn O. Pearce @ 2008-12-12 22:05 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git
In-Reply-To: <1229119558-1293-11-git-send-email-spearce@spearce.org>

We had the wrong header code in this case, so we didn't parse
the length correctly for delta style binary hunks.  Without a
test case for it we never noticed the problem.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../tst/org/spearce/jgit/patch/PatchTest.java      |   39 +++++++++++++++++++-
 .../jgit/patch/testParse_GitBinaryDelta.patch      |   21 +++++++++++
 ...nary.patch => testParse_GitBinaryLiteral.patch} |    0
 .../src/org/spearce/jgit/patch/BinaryHunk.java     |    2 +-
 4 files changed, 60 insertions(+), 2 deletions(-)
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_GitBinaryDelta.patch
 rename org.spearce.jgit.test/tst/org/spearce/jgit/patch/{testParse_GitBinary.patch => testParse_GitBinaryLiteral.patch} (100%)

diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
index 2c617d3..8309951 100644
--- a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
@@ -184,7 +184,7 @@ assertTrue(fh.getNewName().startsWith(
 		assertEquals(272, fh.getHunks().get(0).getOldImage().getStartLine());
 	}
 
-	public void testParse_GitBinary() throws IOException {
+	public void testParse_GitBinaryLiteral() throws IOException {
 		final Patch p = parseTestPatchFile();
 		final int[] binsizes = { 359, 393, 372, 404 };
 		assertEquals(5, p.getFiles().size());
@@ -229,6 +229,43 @@ assertTrue(fh.getNewName().startsWith(
 		assertEquals(272, fh.getHunks().get(0).getOldImage().getStartLine());
 	}
 
+	public void testParse_GitBinaryDelta() throws IOException {
+		final Patch p = parseTestPatchFile();
+		assertEquals(1, p.getFiles().size());
+		assertTrue(p.getErrors().isEmpty());
+
+		final FileHeader fh = p.getFiles().get(0);
+		assertTrue(fh.getNewName().startsWith("zero.bin"));
+		assertSame(FileHeader.ChangeType.MODIFY, fh.getChangeType());
+		assertSame(FileHeader.PatchType.GIT_BINARY, fh.getPatchType());
+		assertSame(FileMode.REGULAR_FILE, fh.getNewMode());
+
+		assertNotNull(fh.getOldId());
+		assertNotNull(fh.getNewId());
+		assertEquals("08e7df176454f3ee5eeda13efa0adaa54828dfd8", fh.getOldId()
+				.name());
+		assertEquals("d70d8710b6d32ff844af0ee7c247e4b4b051867f", fh.getNewId()
+				.name());
+
+		assertTrue(fh.getHunks().isEmpty());
+		assertFalse(fh.hasMetaDataChanges());
+
+		final BinaryHunk fwd = fh.getForwardBinaryHunk();
+		final BinaryHunk rev = fh.getReverseBinaryHunk();
+		assertNotNull(fwd);
+		assertNotNull(rev);
+		assertEquals(12, fwd.getSize());
+		assertEquals(11, rev.getSize());
+
+		assertSame(fh, fwd.getFileHeader());
+		assertSame(fh, rev.getFileHeader());
+
+		assertSame(BinaryHunk.Type.DELTA_DEFLATED, fwd.getType());
+		assertSame(BinaryHunk.Type.DELTA_DEFLATED, rev.getType());
+
+		assertEquals(496, fh.endOffset);
+	}
+
 	public void testParse_FixNoNewline() throws IOException {
 		final Patch p = parseTestPatchFile();
 		assertEquals(1, p.getFiles().size());
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_GitBinaryDelta.patch b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_GitBinaryDelta.patch
new file mode 100644
index 0000000..5b2c9c6
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_GitBinaryDelta.patch
@@ -0,0 +1,21 @@
+From 7e49721ad0efdec3a81e20bc58e385ea5d2b87b7 Mon Sep 17 00:00:00 2001
+From: Shawn O. Pearce <sop@google.com>
+Date: Fri, 12 Dec 2008 12:45:17 -0800
+Subject: [PATCH] make zero have a 3
+
+---
+ zero.bin |  Bin 4096 -> 4096 bytes
+ 1 files changed, 0 insertions(+), 0 deletions(-)
+
+diff --git a/zero.bin b/zero.bin
+index 08e7df176454f3ee5eeda13efa0adaa54828dfd8..d70d8710b6d32ff844af0ee7c247e4b4b051867f 100644
+GIT binary patch
+delta 12
+TcmZorXi%6C%4ociaTPxR8IA+R
+
+delta 11
+ScmZorXi(Uguz-JJK>`37u>@iO
+
+-- 
+1.6.1.rc2.306.ge5d5e
+
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_GitBinary.patch b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_GitBinaryLiteral.patch
similarity index 100%
rename from org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_GitBinary.patch
rename to org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_GitBinaryLiteral.patch
diff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/BinaryHunk.java b/org.spearce.jgit/src/org/spearce/jgit/patch/BinaryHunk.java
index 3e07ec4..92eab86 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/patch/BinaryHunk.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/BinaryHunk.java
@@ -100,7 +100,7 @@ int parseHunk(int ptr, final int end) {
 
 		} else if (match(buf, ptr, DELTA) >= 0) {
 			type = Type.DELTA_DEFLATED;
-			length = parseBase10(buf, ptr + LITERAL.length, null);
+			length = parseBase10(buf, ptr + DELTA.length, null);
 
 		} else {
 			// Not a valid binary hunk. Signal to the caller that
-- 
1.6.1.rc2.306.ge5d5e

^ permalink raw reply related

* [JGIT PATCH 08/12] Fix HunkHeader start line when parsing "@@ -1 +1 @@" style headers
From: Shawn O. Pearce @ 2008-12-12 22:05 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git
In-Reply-To: <1229119558-1293-8-git-send-email-spearce@spearce.org>

In this case we are listing a delta of 1 line but also the
position start is line 1.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../src/org/spearce/jgit/patch/HunkHeader.java     |   12 ++++--------
 1 files changed, 4 insertions(+), 8 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java
index 842519e..f543aed 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java
@@ -152,18 +152,14 @@ void parseHeader(final int end) {
 		old.startLine = -parseBase10(buf, ptr.value, ptr);
 		if (buf[ptr.value] == ',')
 			old.lineCount = parseBase10(buf, ptr.value + 1, ptr);
-		else {
-			old.lineCount = old.startLine;
-			old.startLine = 0;
-		}
+		else
+			old.lineCount = 1;
 
 		newStartLine = parseBase10(buf, ptr.value + 1, ptr);
 		if (buf[ptr.value] == ',')
 			newLineCount = parseBase10(buf, ptr.value + 1, ptr);
-		else {
-			newLineCount = newStartLine;
-			newStartLine = 0;
-		}
+		else
+			newLineCount = 1;
 	}
 
 	int parseBody(final Patch script, final int end) {
-- 
1.6.1.rc2.306.ge5d5e

^ permalink raw reply related

* [JGIT PATCH 06/12] Assert the ChunkHeader.OldImage.getId uses FileHeader.getOldImage
From: Shawn O. Pearce @ 2008-12-12 22:05 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git
In-Reply-To: <1229119558-1293-6-git-send-email-spearce@spearce.org>

These should always produce the same AbbreviatedObjectId.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../tst/org/spearce/jgit/patch/PatchTest.java      |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
index 4eceeb5..c81356b 100644
--- a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
@@ -90,6 +90,8 @@ assertSame(FileHeader.PatchType.UNIFIED, fRepositoryConfigTest
 			assertEquals(4, h.getLinesContext());
 			assertEquals(7, h.getOldImage().getLinesAdded());
 			assertEquals(0, h.getOldImage().getLinesDeleted());
+			assertSame(fRepositoryConfigTest.getOldId(), h.getOldImage()
+					.getId());
 
 			assertEquals(1490, h.endOffset);
 		}
@@ -113,6 +115,7 @@ assertSame(FileHeader.PatchType.UNIFIED, fRepositoryConfig
 			assertEquals(7, h.getLinesContext());
 			assertEquals(2, h.getOldImage().getLinesAdded());
 			assertEquals(2, h.getOldImage().getLinesDeleted());
+			assertSame(fRepositoryConfig.getOldId(), h.getOldImage().getId());
 
 			assertEquals(2434, h.endOffset);
 		}
-- 
1.6.1.rc2.306.ge5d5e

^ permalink raw reply related

* Re: gitweb and unicode special characters
From: Jakub Narebski @ 2008-12-12 22:09 UTC (permalink / raw)
  To: Praveen A; +Cc: git, Santhosh Thottingal
In-Reply-To: <m37i65gp6b.fsf@localhost.localdomain>

Jakub Narebski <jnareb@gmail.com> writes:
> "Praveen A" <pravi.a@gmail.com> writes:
> 
> > Git currently does not handle unicode special characters ZWJ and ZWNJ,
> > both are heavily used in Malayalam and common in other languages
> > needing complex text layout like Sinhala and Arabic.
> > 
> > An example of this is shown in the commit message here
> > http://git.savannah.gnu.org/gitweb/?p=smc.git;a=commit;h=c3f368c60aabdc380c77608c614d91b0a628590a
> > 
> > \20014 and \20015 should have been ZWNJ and ZWJ respectively. You just
> > need to handle them as any other unicode character - especially it is
> > a commit message and expectation is normal pain text display.
> > 
> > I hope some one will fix this.
> 
> Well, I am bit stumped.  git_commit calls format_log_line_html, which
> in turn calls esc_html.  esc_html looks like this:
> 
>   sub esc_html ($;%) {
>   	my $str = shift;
>   	my %opts = @_;
>   
>   **	$str = to_utf8($str);
>   	$str = $cgi->escapeHTML($str);
>   	if ($opts{'-nbsp'}) {
>   		$str =~ s/ /&nbsp;/g;
>   	}
>   **	$str =~ s|([[:cntrl:]])|(($1 ne "\t") ? quot_cec($1) : $1)|eg;
>   	return $str;
>   }
> 
> The two important lines are marked with '**'.
[...]

> So it looks like Perl treats \20014 and \20015 (ZWNJ and ZWJ) as
> belonging to '[:cntrl:]' class. I don't know if it is correct from the
> point of view of Unicode character classes, therefore if it is a bug
> in Perl, or just in gitweb.

I checked this, via this simple Perl script:

  #!/usr/bin/perl

  use charnames ":full";

  my $c = ord("\N{ZWNJ}");
  printf "oct=%o dec=%d hex=%x\n", $c, $c, $c;

  "\N{ZWNJ}" =~ /[[:cntrl:]]/ and print "is [:cntrl:]";

And the answer was:

  oct=20014 dex=8204 hex=200c
  is [:cntrl:]

'ZERO WIDTH NON-JOINER' _is_ control character... We probably should
use [^[:print:][:space:]] instead of [[:cntrl:]] here.

[...]
> P.S. Even that might not help much, as Savannah uses git and gitwev
> version 1.5.6.5, which is probably version released with some major
> distribution.  As of now we are at 1.6.0.5...

Which can be seen from the fact that gitweb uses octal escapes,
instead of hex escapes...

-- 
Jakub Narebski
Poland
ShadeHawk on #git

^ permalink raw reply

* [PATCH 0/41] JGIT patch api series
From: Shawn O. Pearce @ 2008-12-12 22:09 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git

I think I've finished my work on the basic patch API for JGit.
A full summary of all of the commits appears below, along with the
final diffstat relative to the current master branch. 41 commits
and 6,151 lines of code in 3 days.  Yikes.

  git://repo.or.cz/egit/spearce.git patchapi

Shawn O. Pearce (41):
      Define an abstraction for handling abbreviated SHA-1 strings
      Add ObjectId.startsWith(AbbreviatedObjectId)
      Change AnyObjectId.abbreviate() to return AbbreviatedObjectId
      Simplify RawParseUtils.nextLF invocations
      Simplify RawParseUtils next and nextLF loops
      Correct Javadoc of RawParseUtils next and nextLF methods
      Add QuotedString class to handle Git path style quoting rules
      Add Bourne style quoting for TransportGitSsh
      Add ~user friendly Bourne style quoting for TransportGitSsh
      Add toByteArray() to TemporaryBuffer
      Add copy(InputStream) to TemporaryBuffer
      Define FileHeader to parse the header block of a git diff
      Define Patch to parse a sequence of patch FileHeaders
      Add HunkHeader to represent a single hunk of a file within a patch
      Correct use of TemporaryBuffer in Patch
      Add tests for TemporaryBuffer
      Add IntList as a more efficient representation of List<Integer>
      Add lineMap computer to RawParseUtils to index locations of line starts
      Define FileHeader.PatchType to report the style of patch used
      Test for non-git binary files and mark them as PatchType.BINARY
      Set empty patches with no Git metadata to PatchType.BINARY
      Always use the FileHeader buffer during Patch.parseHunks
      Parse "GIT binary patch" style patch metadata
      Record patch parsing errors for later inspection by applications
      Fix Patch.parse to honor the end point passed in
      Correctly handle hunk headers such as "@@ -0,0 +1 @@"
      Patch parse test comparing "git log -p" output to "git log --numstat"
      Abstract the hunk header testing into a method
      Treat "diff --combined" the same as "diff --cc"
      Assert the HunkHeader.getFileHeader returns the right file
      Add tests to cover more methods of BinaryHunk
      Add a simple toString to FormatError to facilitate debugging
      Allow FileHeader to create its HunkHeader children
      Refactor the old/pre-image data in HunkHeader to support >1 ancestor
      Assert the ChunkHeader.OldImage.getId uses FileHeader.getOldImage
      Allow a stray LF at the end of a hunk
      Fix HunkHeader start line when parsing "@@ -1 +1 @@" style headers
      Add test cases for parsing "\ No newline at end of file" style patches
      Use FileMode.MISSING when a file is added or deleted rather than null
      Add a test for delta binary patch parsing and fix a bug in it
      Add support for parsing "diff --cc" style patches

 .../ui/internal/components/RefContentProposal.java |    2 +-
 .../egit/ui/internal/fetch/FetchResultTable.java   |   12 +-
 .../egit/ui/internal/push/PushResultTable.java     |   21 +-
 .../src/org/spearce/jgit/pgm/Branch.java           |    2 +-
 .../src/org/spearce/jgit/pgm/Fetch.java            |    8 +-
 .../src/org/spearce/jgit/pgm/Push.java             |    5 +-
 .../spearce/jgit/patch/EGitPatchHistoryTest.java   |  221 ++++++++
 .../spearce/jgit/lib/AbbreviatedObjectIdTest.java  |  285 ++++++++++
 .../tst/org/spearce/jgit/patch/FileHeaderTest.java |  427 +++++++++++++++
 .../org/spearce/jgit/patch/PatchCcErrorTest.java   |   97 ++++
 .../tst/org/spearce/jgit/patch/PatchCcTest.java    |  200 +++++++
 .../tst/org/spearce/jgit/patch/PatchErrorTest.java |  174 ++++++
 .../tst/org/spearce/jgit/patch/PatchTest.java      |  352 ++++++++++++
 .../spearce/jgit/patch/testError_BodyTooLong.patch |   17 +
 .../jgit/patch/testError_CcTruncatedOld.patch      |   24 +
 .../jgit/patch/testError_DisconnectedHunk.patch    |   30 +
 .../jgit/patch/testError_GarbageBetweenFiles.patch |   33 ++
 .../patch/testError_GitBinaryNoForwardHunk.patch   |   10 +
 .../jgit/patch/testError_TruncatedNew.patch        |   15 +
 .../jgit/patch/testError_TruncatedOld.patch        |   15 +
 .../jgit/patch/testParse_AddNoNewline.patch        |   20 +
 .../jgit/patch/testParse_CcDeleteFile.patch        |   12 +
 .../spearce/jgit/patch/testParse_CcNewFile.patch   |   14 +
 .../patch/testParse_ConfigCaseInsensitive.patch    |   67 +++
 .../jgit/patch/testParse_FixNoNewline.patch        |   20 +
 .../jgit/patch/testParse_GitBinaryDelta.patch      |   21 +
 .../jgit/patch/testParse_GitBinaryLiteral.patch    |  135 +++++
 .../spearce/jgit/patch/testParse_NoBinary.patch    |   83 +++
 .../spearce/jgit/patch/testParse_OneFileCc.patch   |   27 +
 .../tst/org/spearce/jgit/util/IntListTest.java     |  156 ++++++
 .../jgit/util/QuotedStringBourneStyleTest.java     |  111 ++++
 .../util/QuotedStringBourneUserPathStyleTest.java  |  130 +++++
 .../jgit/util/QuotedStringGitPathStyleTest.java    |  172 ++++++
 .../jgit/util/RawParseUtils_LineMapTest.java       |   88 +++
 .../org/spearce/jgit/util/TemporaryBufferTest.java |  374 +++++++++++++
 .../tst/org/spearce/jgit/util/TestRng.java         |   61 +++
 .../org/spearce/jgit/lib/AbbreviatedObjectId.java  |  262 +++++++++
 .../src/org/spearce/jgit/lib/AnyObjectId.java      |   39 ++-
 .../src/org/spearce/jgit/lib/ObjectChecker.java    |    4 +-
 .../src/org/spearce/jgit/patch/BinaryHunk.java     |  127 +++++
 .../org/spearce/jgit/patch/CombinedFileHeader.java |  213 ++++++++
 .../org/spearce/jgit/patch/CombinedHunkHeader.java |  191 +++++++
 .../src/org/spearce/jgit/patch/FileHeader.java     |  568 ++++++++++++++++++++
 .../src/org/spearce/jgit/patch/FormatError.java    |  109 ++++
 .../src/org/spearce/jgit/patch/HunkHeader.java     |  228 ++++++++
 .../src/org/spearce/jgit/patch/Patch.java          |  376 +++++++++++++
 .../src/org/spearce/jgit/revwalk/RevTag.java       |    2 +-
 .../spearce/jgit/transport/TransportGitSsh.java    |   38 +--
 .../src/org/spearce/jgit/util/IntList.java         |  128 +++++
 .../src/org/spearce/jgit/util/QuotedString.java    |  362 +++++++++++++
 .../src/org/spearce/jgit/util/RawParseUtils.java   |   76 ++-
 .../src/org/spearce/jgit/util/TemporaryBuffer.java |   72 +++-
 52 files changed, 6151 insertions(+), 85 deletions(-)
 create mode 100644 org.spearce.jgit.test/exttst/org/spearce/jgit/patch/EGitPatchHistoryTest.java
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/lib/AbbreviatedObjectIdTest.java
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/FileHeaderTest.java
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchCcErrorTest.java
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchCcTest.java
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchErrorTest.java
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_BodyTooLong.patch
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_CcTruncatedOld.patch
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_DisconnectedHunk.patch
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_GarbageBetweenFiles.patch
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_GitBinaryNoForwardHunk.patch
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_TruncatedNew.patch
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_TruncatedOld.patch
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_AddNoNewline.patch
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_CcDeleteFile.patch
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_CcNewFile.patch
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_ConfigCaseInsensitive.patch
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_FixNoNewline.patch
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_GitBinaryDelta.patch
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_GitBinaryLiteral.patch
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_NoBinary.patch
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_OneFileCc.patch
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/util/IntListTest.java
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/util/QuotedStringBourneStyleTest.java
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/util/QuotedStringBourneUserPathStyleTest.java
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/util/QuotedStringGitPathStyleTest.java
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/util/RawParseUtils_LineMapTest.java
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/util/TemporaryBufferTest.java
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/util/TestRng.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/AbbreviatedObjectId.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/patch/BinaryHunk.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/patch/CombinedFileHeader.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/patch/CombinedHunkHeader.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/patch/FormatError.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/util/IntList.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/util/QuotedString.java

-- 
Shawn.

^ permalink raw reply

* Re: [PATCH 2/2] mergetool: Don't keep temporary merge files unless told to
From: Jakub Narebski @ 2008-12-12 22:11 UTC (permalink / raw)
  To: git
In-Reply-To: <1229118521-22923-3-git-send-email-charles@hashpling.org>

Charles Bailey wrote:

> +mergetool.keepTemporaries::

mergetool.keepTemporaryFiles ?

-- 
Jakub Narebski
Warsaw, Poland
ShadeHawk on #git

^ permalink raw reply

* [PATCH] "git svn clone" fails when tags have illegal url characters in them.
From: Jerry Seutter @ 2008-12-12 22:46 UTC (permalink / raw)
  To: git

Found a repository with a tag that had a hash (#) mark in the tag name.
The svn repository was available via WebDAV. "git svn clone <url>" would
generate an invalid url when trying to fetch the tag, causing a server
400 result on the other end of the connection.

The fix is to escape the url correctly.  This is a quick and dirty
hack that copies two functions from earlier in the file - only the last
line modified in the commit is important.

Signed-off-by: Jerry Seutter <jseutter@gmail.com>
---
 git-svn.perl |   21 ++++++++++++++++++++-
 1 files changed, 20 insertions(+), 1 deletions(-)

diff --git a/git-svn.perl b/git-svn.perl
index 2c206e9..6b9e010 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -2213,9 +2213,28 @@ sub metadata_url {
 	   (length $self->{path} ? '/' . $self->{path} : '');
 }

+sub junk_escape_uri_only {
+	my ($uri) = @_;
+	my @tmp;
+	foreach (split m{/}, $uri) {
+		s/([^~\w.%+-]|%(?![a-fA-F0-9]{2}))/sprintf("%%%02X",ord($1))/eg;
+		push @tmp, $_;
+	}
+	join('/', @tmp);
+}
+
+sub junk_escape_url {
+	my ($url) = @_;
+	if ($url =~ m#^([^:]+)://([^/]*)(.*)$#) {
+		my ($scheme, $domain, $uri) = ($1, $2, junk_escape_uri_only($3));
+		$url = "$scheme://$domain$uri";
+	}
+	$url;
+}
+
 sub full_url {
 	my ($self) = @_;
-	$self->{url} . (length $self->{path} ? '/' . $self->{path} : '');
+	junk_escape_url($self->{url} . (length $self->{path} ? '/' .
$self->{path} : ''));
 }


-- 
1.6.1.rc2.20.gde0d.dirty

^ permalink raw reply related

* Re: [JGIT PATCH 15/15] Treat "diff --combined" the same as "diff --cc"
From: Robin Rosenberg @ 2008-12-12 23:11 UTC (permalink / raw)
  To: Shawn O. Pearce; +Cc: git
In-Reply-To: <1229049981-14152-16-git-send-email-spearce@spearce.org>


The source for CombinedFileHeader would be nice. 

Btw, I moved the patches to the test-rsrc directory to make it possible
to run the unit tests throuh maven.

-- robin

^ permalink raw reply

* Re: [PATCH] git-branch: display sha1 on branch deletion
From: Brandon Casey @ 2008-12-12 23:17 UTC (permalink / raw)
  To: Jeff King; +Cc: gitster, git
In-Reply-To: <20081212194349.GA5486@sigill.intra.peff.net>

Jeff King wrote:
> On Fri, Dec 12, 2008 at 01:29:01PM -0600, Brandon Casey wrote:
> 
>> Make it easier to recover from a mistaken branch deletion by displaying the
>> sha1 of the branch's tip commit.
> 
> I think this is reasonable behavior, but I have two comments:
> 
>> -			printf("Deleted %sbranch %s.\n", remote, argv[i]);
>> +			printf("Deleted %sbranch %s (%s).\n", remote, argv[i],
>> +                                sha1_to_hex(sha1));
> 
> 1. Any reason not to use find_unique_abbrev(sha1, DEFAULT_ABBREV) here?

I didn't consider using find_unique_abbrev(). I used the same format
that 'git stash drop' uses. There is enough room for a 20-char branch
name without overflowing an 80-char line (unless you're deleting a
remote branch). I kind of like the exactness of the full sha1 in this
case.

>    The full 40-character sha1 kind of dominates the line, especially if
>    you have short branch name. And this is not really for long-term
>    usage, but rather "oops, I didn't mean to have just deleted that".

Actually, I'm more worried about line wrapping with a long branch name
than a short branch name being dominated by the 40-char sha1. I'd even
consider dropping the the word "branch " from the "Deleted branch ..."
message since the user already knows that a branch is being deleted.
So, this word-wrapping argument holds weight with me.

We read left to right and the sha1 is delimited by parenthesis, so I'm
not sure if the sha1 is dominating the line to the extent that it would
cause confusion.

For comparison:

  $ git stash drop
  Dropped refs/stash@{0} (a8381dd3d3ea4e7bc83c2bfe7d1c27fa180632ee)

compared to:

  $ git branch -d my_branch_name
  Deleted branch my_branch_name (ca26e31d5442f3d1ef8ae1cb69970fb31ed4b841).

or

  $ git branch -d my_branch_name
  Deleted branch my_branch_name (ca26e31d).

or

  $ git branch -d my_branch_name
  Deleted my_branch_name (ca26e31d5442f3d1ef8ae1cb69970fb31ed4b841).

But since the longest branch that has been merged in the git history
was 40 characters, maybe it does make sense to abbreviate the sha1.

> 2. I wonder if it is confusing to new users to simply say "Delete branch
>    $branch ($sha1)". We haven't deleted $sha1, just the branch pointer.
>    $sha1 is probably still in the HEAD reflog, if not in another branch.
>    Maybe something like "(was $sha1)" would be appropriate.
>
> I don't know if '2' is a big deal. I haven't been a new user for a long
> time, so I didn't personally find it confusing (especially with '1' so
> that you actually notice the branch name rather than the gigantic sha1).

  Deleted branch my_branch_name (was ca26e31d).

I don't find it confusing (obviously) without the addition of "was ", but
I too haven't been a new user in a while.

-brandon

^ permalink raw reply

* [JGIT PATCH 15/15 v2] Treat "diff --combined" the same as "diff --cc"
From: Shawn O. Pearce @ 2008-12-12 23:18 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git
In-Reply-To: <200812130011.37854.robin.rosenberg.lists@dewire.com>

According to the git diff manual page these two formats
share the same file structure, so we can parse them with
the same function.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
  Robin Rosenberg <robin.rosenberg.lists@dewire.com> wrote:
  > 
  > The source for CombinedFileHeader would be nice. 

  Dammit, that wasn't added until 12/12 of the final series.
  A new 12/12 v2 will be sent in a minute.
   
  > Btw, I moved the patches to the test-rsrc directory to make it possible
  > to run the unit tests throuh maven.
  
  Oh.

 .../src/org/spearce/jgit/patch/Patch.java          |   13 ++++++++++---
 1 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java
index e1e79b7..9ae2635 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java
@@ -57,6 +57,8 @@
 
 	private static final byte[] DIFF_CC = encodeASCII("diff --cc ");
 
+	private static final byte[] DIFF_COMBINED = encodeASCII("diff --combined ");
+
 	private static final byte[][] BIN_HEADERS = new byte[][] {
 			encodeASCII("Binary files "), encodeASCII("Files "), };
 
@@ -177,7 +179,9 @@ private int parseFile(final byte[] buf, int c, final int end) {
 			if (match(buf, c, DIFF_GIT) >= 0)
 				return parseDiffGit(buf, c, end);
 			if (match(buf, c, DIFF_CC) >= 0)
-				return parseDiffCC(buf, c, end);
+				return parseDiffCombined(DIFF_CC, buf, c, end);
+			if (match(buf, c, DIFF_COMBINED) >= 0)
+				return parseDiffCombined(DIFF_COMBINED, buf, c, end);
 
 			// Junk between files? Leading junk? Traditional
 			// (non-git generated) patch?
@@ -227,9 +231,10 @@ private int parseDiffGit(final byte[] buf, final int start, final int end) {
 		return ptr;
 	}
 
-	private int parseDiffCC(final byte[] buf, final int start, final int end) {
+	private int parseDiffCombined(final byte[] hdr, final byte[] buf,
+			final int start, final int end) {
 		final FileHeader fh = new FileHeader(buf, start);
-		int ptr = fh.parseGitFileName(start + DIFF_CC.length, end);
+		int ptr = fh.parseGitFileName(start + hdr.length, end);
 		if (ptr < 0)
 			return skipFile(buf, start, end);
 
@@ -269,6 +274,8 @@ private int parseHunks(final FileHeader fh, int c, final int end) {
 				break;
 			if (match(buf, c, DIFF_CC) >= 0)
 				break;
+			if (match(buf, c, DIFF_COMBINED) >= 0)
+				break;
 			if (match(buf, c, OLD_NAME) >= 0)
 				break;
 			if (match(buf, c, NEW_NAME) >= 0)
-- 
1.6.1.rc2.306.ge5d5e


-- 
Shawn.

^ permalink raw reply related

* [JGIT PATCH 12/12 v2] Add support for parsing "diff --cc" style patches
From: Shawn O. Pearce @ 2008-12-12 23:19 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git
In-Reply-To: <1229119558-1293-13-git-send-email-spearce@spearce.org>

Even though the diff --cc format used by Git is only meant to be
read by humans, JGit needs to be able to parse these to get the
patch metadata so it can be shown in a user interface to facilitate
code review processes.

Patches are parsed into the specialized CombinedFileHeader and
CombinedHunkHeader classes, where the old image information is
augmented with additional fields for the arbitrary number of parents
that can appear in such patches.  These cost more in terms of memory,
but "diff --cc" style patches tend to occur very infrequently as
they only occur during a merge conflict resolution.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---

 v2 is necessary because of the CombinedFileHeader glitch
 in an earlier series.

 .../org/spearce/jgit/patch/PatchCcErrorTest.java   |   97 +++++++++
 .../tst/org/spearce/jgit/patch/PatchCcTest.java    |  200 ++++++++++++++++++
 .../jgit/patch/testError_CcTruncatedOld.patch      |   24 +++
 .../jgit/patch/testParse_CcDeleteFile.patch        |   12 +
 .../spearce/jgit/patch/testParse_CcNewFile.patch   |   14 ++
 .../spearce/jgit/patch/testParse_OneFileCc.patch   |   27 +++
 .../org/spearce/jgit/patch/CombinedFileHeader.java |  213 ++++++++++++++++++++
 .../org/spearce/jgit/patch/CombinedHunkHeader.java |  191 ++++++++++++++++++
 .../src/org/spearce/jgit/patch/FileHeader.java     |   56 +++---
 .../src/org/spearce/jgit/patch/HunkHeader.java     |    2 +-
 .../src/org/spearce/jgit/patch/Patch.java          |    9 +-
 11 files changed, 815 insertions(+), 30 deletions(-)
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchCcErrorTest.java
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchCcTest.java
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_CcTruncatedOld.patch
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_CcDeleteFile.patch
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_CcNewFile.patch
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_OneFileCc.patch
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/patch/CombinedFileHeader.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/patch/CombinedHunkHeader.java

diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchCcErrorTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchCcErrorTest.java
new file mode 100644
index 0000000..a2f3a19
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchCcErrorTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2008, Google Inc.
+ *
+ * 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 Git Development Community 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.spearce.jgit.patch;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import junit.framework.TestCase;
+
+public class PatchCcErrorTest extends TestCase {
+	public void testError_CcTruncatedOld() throws IOException {
+		final Patch p = parseTestPatchFile();
+		assertEquals(1, p.getFiles().size());
+		assertEquals(3, p.getErrors().size());
+		{
+			final FormatError e = p.getErrors().get(0);
+			assertSame(FormatError.Severity.ERROR, e.getSeverity());
+			assertEquals(
+					"Truncated hunk, at least 1 lines is missing for ancestor 1",
+					e.getMessage());
+			assertEquals(346, e.getOffset());
+			assertTrue(e.getLineText().startsWith(
+					"@@@ -55,12 -163,13 +163,15 @@@ public "));
+		}
+		{
+			final FormatError e = p.getErrors().get(1);
+			assertSame(FormatError.Severity.ERROR, e.getSeverity());
+			assertEquals(
+					"Truncated hunk, at least 2 lines is missing for ancestor 2",
+					e.getMessage());
+			assertEquals(346, e.getOffset());
+			assertTrue(e.getLineText().startsWith(
+					"@@@ -55,12 -163,13 +163,15 @@@ public "));
+		}
+		{
+			final FormatError e = p.getErrors().get(2);
+			assertSame(FormatError.Severity.ERROR, e.getSeverity());
+			assertEquals("Truncated hunk, at least 3 new lines is missing", e
+					.getMessage());
+			assertEquals(346, e.getOffset());
+			assertTrue(e.getLineText().startsWith(
+					"@@@ -55,12 -163,13 +163,15 @@@ public "));
+		}
+	}
+
+	private Patch parseTestPatchFile() throws IOException {
+		final String patchFile = getName() + ".patch";
+		final InputStream in = getClass().getResourceAsStream(patchFile);
+		if (in == null) {
+			fail("No " + patchFile + " test vector");
+			return null; // Never happens
+		}
+		try {
+			final Patch p = new Patch();
+			p.parse(in);
+			return p;
+		} finally {
+			in.close();
+		}
+	}
+
+}
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchCcTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchCcTest.java
new file mode 100644
index 0000000..9e8650b
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchCcTest.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2008, Google Inc.
+ *
+ * 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 Git Development Community 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.spearce.jgit.patch;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.spearce.jgit.lib.FileMode;
+
+import junit.framework.TestCase;
+
+public class PatchCcTest extends TestCase {
+	public void testParse_OneFileCc() throws IOException {
+		final Patch p = parseTestPatchFile();
+		assertEquals(1, p.getFiles().size());
+		assertTrue(p.getErrors().isEmpty());
+
+		final CombinedFileHeader cfh = (CombinedFileHeader) p.getFiles().get(0);
+
+		assertEquals("org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java",
+				cfh.getNewName());
+		assertEquals(cfh.getNewName(), cfh.getOldName());
+
+		assertEquals(98, cfh.startOffset);
+
+		assertEquals(2, cfh.getParentCount());
+		assertSame(cfh.getOldId(0), cfh.getOldId());
+		assertEquals("169356b", cfh.getOldId(0).name());
+		assertEquals("dd8c317", cfh.getOldId(1).name());
+		assertEquals("fd85931", cfh.getNewId().name());
+
+		assertSame(cfh.getOldMode(0), cfh.getOldMode());
+		assertSame(FileMode.REGULAR_FILE, cfh.getOldMode(0));
+		assertSame(FileMode.REGULAR_FILE, cfh.getOldMode(1));
+		assertSame(FileMode.EXECUTABLE_FILE, cfh.getNewMode());
+		assertSame(FileHeader.ChangeType.MODIFY, cfh.getChangeType());
+		assertSame(FileHeader.PatchType.UNIFIED, cfh.getPatchType());
+
+		assertEquals(1, cfh.getHunks().size());
+		{
+			final CombinedHunkHeader h = cfh.getHunks().get(0);
+
+			assertSame(cfh, h.getFileHeader());
+			assertEquals(346, h.startOffset);
+			assertEquals(764, h.endOffset);
+
+			assertSame(h.getOldImage(0), h.getOldImage());
+			assertSame(cfh.getOldId(0), h.getOldImage(0).getId());
+			assertSame(cfh.getOldId(1), h.getOldImage(1).getId());
+
+			assertEquals(55, h.getOldImage(0).getStartLine());
+			assertEquals(12, h.getOldImage(0).getLineCount());
+			assertEquals(3, h.getOldImage(0).getLinesAdded());
+			assertEquals(0, h.getOldImage(0).getLinesDeleted());
+
+			assertEquals(163, h.getOldImage(1).getStartLine());
+			assertEquals(13, h.getOldImage(1).getLineCount());
+			assertEquals(2, h.getOldImage(1).getLinesAdded());
+			assertEquals(0, h.getOldImage(1).getLinesDeleted());
+
+			assertEquals(163, h.getNewStartLine());
+			assertEquals(15, h.getNewLineCount());
+
+			assertEquals(10, h.getLinesContext());
+		}
+	}
+
+	public void testParse_CcNewFile() throws IOException {
+		final Patch p = parseTestPatchFile();
+		assertEquals(1, p.getFiles().size());
+		assertTrue(p.getErrors().isEmpty());
+
+		final CombinedFileHeader cfh = (CombinedFileHeader) p.getFiles().get(0);
+
+		assertSame(FileHeader.DEV_NULL, cfh.getOldName());
+		assertEquals("d", cfh.getNewName());
+
+		assertEquals(187, cfh.startOffset);
+
+		assertEquals(2, cfh.getParentCount());
+		assertSame(cfh.getOldId(0), cfh.getOldId());
+		assertEquals("0000000", cfh.getOldId(0).name());
+		assertEquals("0000000", cfh.getOldId(1).name());
+		assertEquals("4bcfe98", cfh.getNewId().name());
+
+		assertSame(cfh.getOldMode(0), cfh.getOldMode());
+		assertSame(FileMode.MISSING, cfh.getOldMode(0));
+		assertSame(FileMode.MISSING, cfh.getOldMode(1));
+		assertSame(FileMode.REGULAR_FILE, cfh.getNewMode());
+		assertSame(FileHeader.ChangeType.ADD, cfh.getChangeType());
+		assertSame(FileHeader.PatchType.UNIFIED, cfh.getPatchType());
+
+		assertEquals(1, cfh.getHunks().size());
+		{
+			final CombinedHunkHeader h = cfh.getHunks().get(0);
+
+			assertSame(cfh, h.getFileHeader());
+			assertEquals(273, h.startOffset);
+			assertEquals(300, h.endOffset);
+
+			assertSame(h.getOldImage(0), h.getOldImage());
+			assertSame(cfh.getOldId(0), h.getOldImage(0).getId());
+			assertSame(cfh.getOldId(1), h.getOldImage(1).getId());
+
+			assertEquals(1, h.getOldImage(0).getStartLine());
+			assertEquals(0, h.getOldImage(0).getLineCount());
+			assertEquals(1, h.getOldImage(0).getLinesAdded());
+			assertEquals(0, h.getOldImage(0).getLinesDeleted());
+
+			assertEquals(1, h.getOldImage(1).getStartLine());
+			assertEquals(0, h.getOldImage(1).getLineCount());
+			assertEquals(1, h.getOldImage(1).getLinesAdded());
+			assertEquals(0, h.getOldImage(1).getLinesDeleted());
+
+			assertEquals(1, h.getNewStartLine());
+			assertEquals(1, h.getNewLineCount());
+
+			assertEquals(0, h.getLinesContext());
+		}
+	}
+
+	public void testParse_CcDeleteFile() throws IOException {
+		final Patch p = parseTestPatchFile();
+		assertEquals(1, p.getFiles().size());
+		assertTrue(p.getErrors().isEmpty());
+
+		final CombinedFileHeader cfh = (CombinedFileHeader) p.getFiles().get(0);
+
+		assertEquals("a", cfh.getOldName());
+		assertSame(FileHeader.DEV_NULL, cfh.getNewName());
+
+		assertEquals(187, cfh.startOffset);
+
+		assertEquals(2, cfh.getParentCount());
+		assertSame(cfh.getOldId(0), cfh.getOldId());
+		assertEquals("7898192", cfh.getOldId(0).name());
+		assertEquals("2e65efe", cfh.getOldId(1).name());
+		assertEquals("0000000", cfh.getNewId().name());
+
+		assertSame(cfh.getOldMode(0), cfh.getOldMode());
+		assertSame(FileMode.REGULAR_FILE, cfh.getOldMode(0));
+		assertSame(FileMode.REGULAR_FILE, cfh.getOldMode(1));
+		assertSame(FileMode.MISSING, cfh.getNewMode());
+		assertSame(FileHeader.ChangeType.DELETE, cfh.getChangeType());
+		assertSame(FileHeader.PatchType.UNIFIED, cfh.getPatchType());
+
+		assertTrue(cfh.getHunks().isEmpty());
+	}
+
+	private Patch parseTestPatchFile() throws IOException {
+		final String patchFile = getName() + ".patch";
+		final InputStream in = getClass().getResourceAsStream(patchFile);
+		if (in == null) {
+			fail("No " + patchFile + " test vector");
+			return null; // Never happens
+		}
+		try {
+			final Patch p = new Patch();
+			p.parse(in);
+			return p;
+		} finally {
+			in.close();
+		}
+	}
+}
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_CcTruncatedOld.patch b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_CcTruncatedOld.patch
new file mode 100644
index 0000000..1bbcfb5
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_CcTruncatedOld.patch
@@ -0,0 +1,24 @@
+commit 1a56639bbea8e8cbfbe5da87746de97f9217ce9b
+Date:   Tue May 13 00:43:56 2008 +0200
+      ...
+
+diff --cc org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java
+index 169356b,dd8c317..fd85931
+mode 100644,100644..100755
+--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java
++++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java
+@@@ -55,12 -163,13 +163,15 @@@ public class UIText extends NLS 
+  
+  	/** */
+  	public static String ResourceHistory_toggleCommentWrap;
++ 
+  	/** */
+ +	/** */
+  	public static String ResourceHistory_toggleRevDetail;
+  	/** */
+  	public static String ResourceHistory_toggleRevComment;
+  	/** */
+  	public static String ResourceHistory_toggleTooltips;
+  
+
+commit 1a56639bbea8e8cbfbe5da87746de97f9217ce9b
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_CcDeleteFile.patch b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_CcDeleteFile.patch
new file mode 100644
index 0000000..2654e09
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_CcDeleteFile.patch
@@ -0,0 +1,12 @@
+commit 740709ece2412856c0c3eabd4dc4a4cf115b0de6
+Merge: 5c19b43... 13a2c0d...
+Author: Shawn O. Pearce <sop@google.com>
+Date:   Fri Dec 12 13:26:52 2008 -0800
+
+    Merge branch 'b' into d
+
+diff --cc a
+index 7898192,2e65efe..0000000
+deleted file mode 100644,100644
+--- a/a
++++ /dev/null
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_CcNewFile.patch b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_CcNewFile.patch
new file mode 100644
index 0000000..1a9b7b0
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_CcNewFile.patch
@@ -0,0 +1,14 @@
+commit 6cb8160a4717d51fd3cc0baf721946daa60cf921
+Merge: 5c19b43... 13a2c0d...
+Author: Shawn O. Pearce <sop@google.com>
+Date:   Fri Dec 12 13:26:52 2008 -0800
+
+    Merge branch 'b' into d
+
+diff --cc d
+index 0000000,0000000..4bcfe98
+new file mode 100644
+--- /dev/null
++++ b/d
+@@@ -1,0 -1,0 +1,1 @@@
+++d
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_OneFileCc.patch b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_OneFileCc.patch
new file mode 100644
index 0000000..c096b33
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_OneFileCc.patch
@@ -0,0 +1,27 @@
+commit 1a56639bbea8e8cbfbe5da87746de97f9217ce9b
+Date:   Tue May 13 00:43:56 2008 +0200
+      ...
+
+diff --cc org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java
+index 169356b,dd8c317..fd85931
+mode 100644,100644..100755
+--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java
++++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java
+@@@ -55,12 -163,13 +163,15 @@@ public class UIText extends NLS 
+  
+  	/** */
+  	public static String ResourceHistory_toggleCommentWrap;
++ 
+  	/** */
+ +	public static String ResourceHistory_toggleCommentFill;
+ +	/** */
+  	public static String ResourceHistory_toggleRevDetail;
++ 
+  	/** */
+  	public static String ResourceHistory_toggleRevComment;
++ 
+  	/** */
+  	public static String ResourceHistory_toggleTooltips;
+  
+
+commit 1a56639bbea8e8cbfbe5da87746de97f9217ce9b
diff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/CombinedFileHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/CombinedFileHeader.java
new file mode 100644
index 0000000..7171600
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/CombinedFileHeader.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2008, Google Inc.
+ *
+ * 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 Git Development Community 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.spearce.jgit.patch;
+
+import static org.spearce.jgit.lib.Constants.encodeASCII;
+import static org.spearce.jgit.util.RawParseUtils.match;
+import static org.spearce.jgit.util.RawParseUtils.nextLF;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.spearce.jgit.lib.AbbreviatedObjectId;
+import org.spearce.jgit.lib.FileMode;
+
+/**
+ * A file in the Git "diff --cc" or "diff --combined" format.
+ * <p>
+ * A combined diff shows an n-way comparison between two or more ancestors and
+ * the final revision. Its primary function is to perform code reviews on a
+ * merge which introduces changes not in any ancestor.
+ */
+public class CombinedFileHeader extends FileHeader {
+	private static final byte[] MODE = encodeASCII("mode ");
+
+	private AbbreviatedObjectId[] oldIds;
+
+	private FileMode[] oldModes;
+
+	CombinedFileHeader(final byte[] b, final int offset) {
+		super(b, offset);
+	}
+
+	@Override
+	@SuppressWarnings("unchecked")
+	public List<? extends CombinedHunkHeader> getHunks() {
+		return (List<CombinedHunkHeader>) super.getHunks();
+	}
+
+	/** @return number of ancestor revisions mentioned in this diff. */
+	@Override
+	public int getParentCount() {
+		return oldIds.length;
+	}
+
+	/** @return get the file mode of the first parent. */
+	@Override
+	public FileMode getOldMode() {
+		return getOldMode(0);
+	}
+
+	/**
+	 * Get the file mode of the nth ancestor
+	 * 
+	 * @param nthParent
+	 *            the ancestor to get the mode of
+	 * @return the mode of the requested ancestor.
+	 */
+	public FileMode getOldMode(final int nthParent) {
+		return oldModes[nthParent];
+	}
+
+	/** @return get the object id of the first parent. */
+	@Override
+	public AbbreviatedObjectId getOldId() {
+		return getOldId(0);
+	}
+
+	/**
+	 * Get the ObjectId of the nth ancestor
+	 * 
+	 * @param nthParent
+	 *            the ancestor to get the object id of
+	 * @return the id of the requested ancestor.
+	 */
+	public AbbreviatedObjectId getOldId(final int nthParent) {
+		return oldIds[nthParent];
+	}
+
+	int parseGitHeaders(int ptr, final int end) {
+		while (ptr < end) {
+			final int eol = nextLF(buf, ptr);
+			if (isHunkHdr(buf, ptr, end) >= 1) {
+				// First hunk header; break out and parse them later.
+				break;
+
+			} else if (match(buf, ptr, OLD_NAME) >= 0) {
+				parseOldName(ptr, eol);
+
+			} else if (match(buf, ptr, NEW_NAME) >= 0) {
+				parseNewName(ptr, eol);
+
+			} else if (match(buf, ptr, INDEX) >= 0) {
+				parseIndexLine(ptr + INDEX.length, eol);
+
+			} else if (match(buf, ptr, MODE) >= 0) {
+				parseModeLine(ptr + MODE.length, eol);
+
+			} else if (match(buf, ptr, NEW_FILE_MODE) >= 0) {
+				parseNewFileMode(ptr, eol);
+
+			} else if (match(buf, ptr, DELETED_FILE_MODE) >= 0) {
+				parseDeletedFileMode(ptr + DELETED_FILE_MODE.length, eol);
+
+			} else {
+				// Probably an empty patch (stat dirty).
+				break;
+			}
+
+			ptr = eol;
+		}
+		return ptr;
+	}
+
+	@Override
+	protected void parseIndexLine(int ptr, final int eol) {
+		// "index $asha1,$bsha1..$csha1"
+		//
+		final List<AbbreviatedObjectId> ids = new ArrayList<AbbreviatedObjectId>();
+		while (ptr < eol) {
+			final int comma = nextLF(buf, ptr, ',');
+			if (eol <= comma)
+				break;
+			ids.add(AbbreviatedObjectId.fromString(buf, ptr, comma - 1));
+			ptr = comma;
+		}
+
+		oldIds = new AbbreviatedObjectId[ids.size() + 1];
+		ids.toArray(oldIds);
+		final int dot2 = nextLF(buf, ptr, '.');
+		oldIds[ids.size()] = AbbreviatedObjectId.fromString(buf, ptr, dot2 - 1);
+		newId = AbbreviatedObjectId.fromString(buf, dot2 + 1, eol - 1);
+		oldModes = new FileMode[oldIds.length];
+	}
+
+	@Override
+	protected void parseNewFileMode(final int ptr, final int eol) {
+		for (int i = 0; i < oldModes.length; i++)
+			oldModes[i] = FileMode.MISSING;
+		super.parseNewFileMode(ptr, eol);
+	}
+
+	@Override
+	HunkHeader newHunkHeader(final int offset) {
+		return new CombinedHunkHeader(this, offset);
+	}
+
+	private void parseModeLine(int ptr, final int eol) {
+		// "mode $amode,$bmode..$cmode"
+		//
+		int n = 0;
+		while (ptr < eol) {
+			final int comma = nextLF(buf, ptr, ',');
+			if (eol <= comma)
+				break;
+			oldModes[n++] = parseFileMode(ptr, comma);
+			ptr = comma;
+		}
+		final int dot2 = nextLF(buf, ptr, '.');
+		oldModes[n] = parseFileMode(ptr, dot2);
+		newMode = parseFileMode(dot2 + 1, eol);
+	}
+
+	private void parseDeletedFileMode(int ptr, final int eol) {
+		// "deleted file mode $amode,$bmode"
+		//
+		changeType = ChangeType.DELETE;
+		int n = 0;
+		while (ptr < eol) {
+			final int comma = nextLF(buf, ptr, ',');
+			if (eol <= comma)
+				break;
+			oldModes[n++] = parseFileMode(ptr, comma);
+			ptr = comma;
+		}
+		oldModes[n] = parseFileMode(ptr, eol);
+		newMode = FileMode.MISSING;
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/CombinedHunkHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/CombinedHunkHeader.java
new file mode 100644
index 0000000..bebeafa
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/CombinedHunkHeader.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2008, Google Inc.
+ *
+ * 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 Git Development Community 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.spearce.jgit.patch;
+
+import static org.spearce.jgit.util.RawParseUtils.nextLF;
+import static org.spearce.jgit.util.RawParseUtils.parseBase10;
+
+import org.spearce.jgit.lib.AbbreviatedObjectId;
+import org.spearce.jgit.util.MutableInteger;
+
+/** Hunk header for a hunk appearing in a "diff --cc" style patch. */
+public class CombinedHunkHeader extends HunkHeader {
+	private static abstract class CombinedOldImage extends OldImage {
+		int nContext;
+	}
+
+	private CombinedOldImage[] old;
+
+	CombinedHunkHeader(final CombinedFileHeader fh, final int offset) {
+		super(fh, offset, null);
+		old = new CombinedOldImage[fh.getParentCount()];
+		for (int i = 0; i < old.length; i++) {
+			final int imagePos = i;
+			old[i] = new CombinedOldImage() {
+				@Override
+				public AbbreviatedObjectId getId() {
+					return fh.getOldId(imagePos);
+				}
+			};
+		}
+	}
+
+	@Override
+	public CombinedFileHeader getFileHeader() {
+		return (CombinedFileHeader) super.getFileHeader();
+	}
+
+	@Override
+	public OldImage getOldImage() {
+		return getOldImage(0);
+	}
+
+	/**
+	 * Get the OldImage data related to the nth ancestor
+	 * 
+	 * @param nthParent
+	 *            the ancestor to get the old image data of
+	 * @return image data of the requested ancestor.
+	 */
+	public OldImage getOldImage(final int nthParent) {
+		return old[nthParent];
+	}
+
+	@Override
+	void parseHeader(final int end) {
+		// Parse "@@@ -55,12 -163,13 +163,15 @@@ protected boolean"
+		//
+		final byte[] buf = file.buf;
+		final MutableInteger ptr = new MutableInteger();
+		ptr.value = nextLF(buf, startOffset, ' ');
+
+		for (int n = 0; n < old.length; n++) {
+			old[n].startLine = -parseBase10(buf, ptr.value, ptr);
+			if (buf[ptr.value] == ',')
+				old[n].lineCount = parseBase10(buf, ptr.value + 1, ptr);
+			else
+				old[n].lineCount = 1;
+		}
+
+		newStartLine = parseBase10(buf, ptr.value + 1, ptr);
+		if (buf[ptr.value] == ',')
+			newLineCount = parseBase10(buf, ptr.value + 1, ptr);
+		else
+			newLineCount = 1;
+	}
+
+	@Override
+	int parseBody(final Patch script, final int end) {
+		final byte[] buf = file.buf;
+		int c = nextLF(buf, startOffset);
+
+		for (final CombinedOldImage o : old) {
+			o.nDeleted = 0;
+			o.nAdded = 0;
+			o.nContext = 0;
+		}
+		nContext = 0;
+		int nAdded = 0;
+
+		SCAN: for (int eol; c < end; c = eol) {
+			eol = nextLF(buf, c);
+
+			if (eol - c < old.length + 1) {
+				// Line isn't long enough to mention the state of each
+				// ancestor. It must be the end of the hunk.
+				break SCAN;
+			}
+
+			switch (buf[c]) {
+			case ' ':
+			case '-':
+			case '+':
+				break;
+
+			default:
+				// Line can't possibly be part of this hunk; the first
+				// ancestor information isn't recognizable.
+				//
+				break SCAN;
+			}
+
+			int localcontext = 0;
+			for (int ancestor = 0; ancestor < old.length; ancestor++) {
+				switch (buf[c + ancestor]) {
+				case ' ':
+					localcontext++;
+					old[ancestor].nContext++;
+					continue;
+
+				case '-':
+					old[ancestor].nDeleted++;
+					continue;
+
+				case '+':
+					old[ancestor].nAdded++;
+					nAdded++;
+					continue;
+
+				default:
+					break SCAN;
+				}
+			}
+			if (localcontext == old.length)
+				nContext++;
+		}
+
+		for (int ancestor = 0; ancestor < old.length; ancestor++) {
+			final CombinedOldImage o = old[ancestor];
+			final int cmp = o.nContext + o.nDeleted;
+			if (cmp < o.lineCount) {
+				final int missingCnt = o.lineCount - cmp;
+				script.error(buf, startOffset, "Truncated hunk, at least "
+						+ missingCnt + " lines is missing for ancestor "
+						+ (ancestor + 1));
+			}
+		}
+
+		if (nContext + nAdded < newLineCount) {
+			final int missingCount = newLineCount - (nContext + nAdded);
+			script.error(buf, startOffset, "Truncated hunk, at least "
+					+ missingCount + " new lines is missing");
+		}
+
+		return c;
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java
index 48d7623..79e4b0a 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java
@@ -61,9 +61,9 @@
 
 	private static final byte[] NEW_MODE = encodeASCII("new mode ");
 
-	private static final byte[] DELETED_FILE_MODE = encodeASCII("deleted file mode ");
+	protected static final byte[] DELETED_FILE_MODE = encodeASCII("deleted file mode ");
 
-	private static final byte[] NEW_FILE_MODE = encodeASCII("new file mode ");
+	protected static final byte[] NEW_FILE_MODE = encodeASCII("new file mode ");
 
 	private static final byte[] COPY_FROM = encodeASCII("copy from ");
 
@@ -81,7 +81,7 @@
 
 	private static final byte[] DISSIMILARITY_INDEX = encodeASCII("dissimilarity index ");
 
-	private static final byte[] INDEX = encodeASCII("index ");
+	protected static final byte[] INDEX = encodeASCII("index ");
 
 	static final byte[] OLD_NAME = encodeASCII("--- ");
 
@@ -136,10 +136,10 @@
 	private FileMode oldMode;
 
 	/** New mode of the file, if described by the patch, else null. */
-	private FileMode newMode;
+	protected FileMode newMode;
 
 	/** General type of change indicated by the patch. */
-	private ChangeType changeType;
+	protected ChangeType changeType;
 
 	/** Similarity score if {@link #changeType} is a copy or rename. */
 	private int score;
@@ -148,7 +148,7 @@
 	private AbbreviatedObjectId oldId;
 
 	/** ObjectId listed on the index line for the new (post-image) */
-	private AbbreviatedObjectId newId;
+	protected AbbreviatedObjectId newId;
 
 	/** Type of patch used to modify this file */
 	PatchType patchType;
@@ -264,7 +264,7 @@ public boolean hasMetaDataChanges() {
 	}
 
 	/** @return hunks altering this file; in order of appearance in patch */
-	public List<HunkHeader> getHunks() {
+	public List<? extends HunkHeader> getHunks() {
 		if (hunks == null)
 			return Collections.emptyList();
 		return hunks;
@@ -369,14 +369,10 @@ int parseGitHeaders(int ptr, final int end) {
 				break;
 
 			} else if (match(buf, ptr, OLD_NAME) >= 0) {
-				oldName = p1(parseName(oldName, ptr + OLD_NAME.length, eol));
-				if (oldName == DEV_NULL)
-					changeType = ChangeType.ADD;
+				parseOldName(ptr, eol);
 
 			} else if (match(buf, ptr, NEW_NAME) >= 0) {
-				newName = p1(parseName(newName, ptr + NEW_NAME.length, eol));
-				if (newName == DEV_NULL)
-					changeType = ChangeType.DELETE;
+				parseNewName(ptr, eol);
 
 			} else if (match(buf, ptr, OLD_MODE) >= 0) {
 				oldMode = parseFileMode(ptr + OLD_MODE.length, eol);
@@ -390,9 +386,7 @@ int parseGitHeaders(int ptr, final int end) {
 				changeType = ChangeType.DELETE;
 
 			} else if (match(buf, ptr, NEW_FILE_MODE) >= 0) {
-				oldMode = FileMode.MISSING;
-				newMode = parseFileMode(ptr + NEW_FILE_MODE.length, eol);
-				changeType = ChangeType.ADD;
+				parseNewFileMode(ptr, eol);
 
 			} else if (match(buf, ptr, COPY_FROM) >= 0) {
 				oldName = parseName(oldName, ptr + COPY_FROM.length, eol);
@@ -437,6 +431,24 @@ int parseGitHeaders(int ptr, final int end) {
 		return ptr;
 	}
 
+	protected void parseOldName(int ptr, final int eol) {
+		oldName = p1(parseName(oldName, ptr + OLD_NAME.length, eol));
+		if (oldName == DEV_NULL)
+			changeType = ChangeType.ADD;
+	}
+
+	protected void parseNewName(int ptr, final int eol) {
+		newName = p1(parseName(newName, ptr + NEW_NAME.length, eol));
+		if (newName == DEV_NULL)
+			changeType = ChangeType.DELETE;
+	}
+
+	protected void parseNewFileMode(int ptr, final int eol) {
+		oldMode = FileMode.MISSING;
+		newMode = parseFileMode(ptr + NEW_FILE_MODE.length, eol);
+		changeType = ChangeType.ADD;
+	}
+
 	int parseTraditionalHeaders(int ptr, final int end) {
 		while (ptr < end) {
 			final int eol = nextLF(buf, ptr);
@@ -445,14 +457,10 @@ int parseTraditionalHeaders(int ptr, final int end) {
 				break;
 
 			} else if (match(buf, ptr, OLD_NAME) >= 0) {
-				oldName = p1(parseName(oldName, ptr + OLD_NAME.length, eol));
-				if (oldName == DEV_NULL)
-					changeType = ChangeType.ADD;
+				parseOldName(ptr, eol);
 
 			} else if (match(buf, ptr, NEW_NAME) >= 0) {
-				newName = p1(parseName(newName, ptr + NEW_NAME.length, eol));
-				if (newName == DEV_NULL)
-					changeType = ChangeType.DELETE;
+				parseNewName(ptr, eol);
 
 			} else {
 				// Possibly an empty patch.
@@ -494,7 +502,7 @@ private static String p1(final String r) {
 		return s > 0 ? r.substring(s + 1) : r;
 	}
 
-	private FileMode parseFileMode(int ptr, final int end) {
+	protected FileMode parseFileMode(int ptr, final int end) {
 		int tmp = 0;
 		while (ptr < end - 1) {
 			tmp <<= 3;
@@ -503,7 +511,7 @@ private FileMode parseFileMode(int ptr, final int end) {
 		return FileMode.fromBits(tmp);
 	}
 
-	private void parseIndexLine(int ptr, final int end) {
+	protected void parseIndexLine(int ptr, final int end) {
 		// "index $asha1..$bsha1[ $mode]" where $asha1 and $bsha1
 		// can be unique abbreviations
 		//
diff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java
index f543aed..fc149ac 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java
@@ -84,7 +84,7 @@ public int getLinesAdded() {
 		public abstract AbbreviatedObjectId getId();
 	}
 
-	private final FileHeader file;
+	final FileHeader file;
 
 	/** Offset within {@link #file}.buf to the "@@ -" line. */
 	final int startOffset;
diff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java
index 2886e4c..f23ba69 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java
@@ -94,7 +94,7 @@ public void addFile(final FileHeader fh) {
 	}
 
 	/** @return list of files described in the patch, in occurrence order. */
-	public List<FileHeader> getFiles() {
+	public List<? extends FileHeader> getFiles() {
 		return files;
 	}
 
@@ -233,14 +233,13 @@ private int parseDiffGit(final byte[] buf, final int start, final int end) {
 
 	private int parseDiffCombined(final byte[] hdr, final byte[] buf,
 			final int start, final int end) {
-		final FileHeader fh = new FileHeader(buf, start);
+		final CombinedFileHeader fh = new CombinedFileHeader(buf, start);
 		int ptr = fh.parseGitFileName(start + hdr.length, end);
 		if (ptr < 0)
 			return skipFile(buf, start, end);
 
-		// TODO Support parsing diff --cc headers
-		// TODO parse diff --cc hunks
-		warn(buf, start, "diff --cc format not supported");
+		ptr = fh.parseGitHeaders(ptr, end);
+		ptr = parseHunks(fh, ptr, end);
 		fh.endOffset = ptr;
 		addFile(fh);
 		return ptr;
-- 
1.6.1.rc2.306.ge5d5e

^ permalink raw reply related

* [PATCH v2] git-branch: display sha1 on branch deletion
From: Brandon Casey @ 2008-12-12 23:20 UTC (permalink / raw)
  To: peff; +Cc: gitster, git
In-Reply-To: <AeIdfC6dcUaLaDHDaAdSq8Mxz6QoUdeypSUI24erUEyAHVz4HeTSCw@cipher.nrlssc.navy.mil>

Make it easier to recover from a mistaken branch deletion by displaying the
sha1 of the branch's tip commit.

Update t3200.

Signed-off-by: Brandon Casey <casey@nrlssc.navy.mil>
---
 builtin-branch.c  |    3 ++-
 t/t3200-branch.sh |    3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/builtin-branch.c b/builtin-branch.c
index 494cbac..02fa38f 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -165,7 +165,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
 			ret = 1;
 		} else {
 			struct strbuf buf = STRBUF_INIT;
-			printf("Deleted %sbranch %s.\n", remote, argv[i]);
+			printf("Deleted %sbranch %s (%s).\n", remote, argv[i],
+				find_unique_abbrev(sha1, DEFAULT_ABBREV));
 			strbuf_addf(&buf, "branch.%s", argv[i]);
 			if (git_config_rename_section(buf.buf, NULL) < 0)
 				warning("Update of config-file failed");
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index 25e9971..61a2010 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -194,7 +194,8 @@ test_expect_success 'test deleting branch deletes branch config' \
 
 test_expect_success 'test deleting branch without config' \
     'git branch my7 s &&
-     test "$(git branch -d my7 2>&1)" = "Deleted branch my7."'
+     sha1=$(git rev-parse my7 | cut -c 1-7) &&
+     test "$(git branch -d my7 2>&1)" = "Deleted branch my7 ($sha1)."'
 
 test_expect_success 'test --track without .fetch entries' \
     'git branch --track my8 &&
-- 
1.6.0.4.794.g35fad

^ permalink raw reply related

* Re: [PATCH] Simplified GIT usage guide
From: David Howells @ 2008-12-13  0:30 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: dhowells, torvalds, git, linux-kernel
In-Reply-To: <4942C2D1.4090309@garzik.org>

Jeff Garzik <jeff@garzik.org> wrote:

> What do you feel is missing from the Kernel Hackers' Guide to Git?  :)

Quite a lot.  Most notably the section I have on publishing changes by GIT
tree.  It's taken a lot of experimentation to work out how to do it, and I'm
sure it can be done better.  I hadn't managed to find anywhere on the web
describing how to do it that I could follow, and no-one that I asked was really
willing to help me set it up.

I have a number of crib sheets that I've cobbled together to note how to do
things that I can cut and paste from, so I turned them into a document.

David

^ permalink raw reply

* Re: gitweb and unicode special characters
From: Praveen A @ 2008-12-13  0:55 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: git, Santhosh Thottingal
In-Reply-To: <m3y6ylf3mq.fsf@localhost.localdomain>

2008/12/12 Jakub Narebski <jnareb@gmail.com>:
> Jakub Narebski <jnareb@gmail.com> writes:
>> "Praveen A" <pravi.a@gmail.com> writes:
>>
>> > Git currently does not handle unicode special characters ZWJ and ZWNJ,
>> > both are heavily used in Malayalam and common in other languages
>> > needing complex text layout like Sinhala and Arabic.
>> >
>> > An example of this is shown in the commit message here
>> > http://git.savannah.gnu.org/gitweb/?p=smc.git;a=commit;h=c3f368c60aabdc380c77608c614d91b0a628590a
>> >
>> > \20014 and \20015 should have been ZWNJ and ZWJ respectively. You just
>> > need to handle them as any other unicode character - especially it is
>> > a commit message and expectation is normal pain text display.
>> >
>> > I hope some one will fix this.
>>
>> Well, I am bit stumped.  git_commit calls format_log_line_html, which
>> in turn calls esc_html.  esc_html looks like this:
>>
>>   sub esc_html ($;%) {
>>       my $str = shift;
>>       my %opts = @_;
>>
>>   **  $str = to_utf8($str);
>>       $str = $cgi->escapeHTML($str);
>>       if ($opts{'-nbsp'}) {
>>               $str =~ s/ /&nbsp;/g;
>>       }
>>   **  $str =~ s|([[:cntrl:]])|(($1 ne "\t") ? quot_cec($1) : $1)|eg;
>>       return $str;
>>   }
>>
>> The two important lines are marked with '**'.
> [...]
>
>> So it looks like Perl treats \20014 and \20015 (ZWNJ and ZWJ) as
>> belonging to '[:cntrl:]' class. I don't know if it is correct from the
>> point of view of Unicode character classes, therefore if it is a bug
>> in Perl, or just in gitweb.
>
> I checked this, via this simple Perl script:
>
>  #!/usr/bin/perl
>
>  use charnames ":full";
>
>  my $c = ord("\N{ZWNJ}");
>  printf "oct=%o dec=%d hex=%x\n", $c, $c, $c;
>
>  "\N{ZWNJ}" =~ /[[:cntrl:]]/ and print "is [:cntrl:]";
>
> And the answer was:
>
>  oct=20014 dex=8204 hex=200c
>  is [:cntrl:]
>
> 'ZERO WIDTH NON-JOINER' _is_ control character... We probably should
> use [^[:print:][:space:]] instead of [[:cntrl:]] here.

That looks good. But I'm wondering why do we need to filter at all?
Is it a security concern? It is just description.

>
> [...]
>> P.S. Even that might not help much, as Savannah uses git and gitwev
>> version 1.5.6.5, which is probably version released with some major
>> distribution.  As of now we are at 1.6.0.5...
>
> Which can be seen from the fact that gitweb uses octal escapes,
> instead of hex escapes...

But we can expect it to work someday when savannah updates their git
version, or we can bug them to upgrade if the fix is in official git
release.

- Praveen
j4v4m4n
>
> --
> Jakub Narebski
> Poland
> ShadeHawk on #git
>



-- 
പ്രവീണ്‍ അരിമ്പ്രത്തൊടിയില്‍
<GPLv2> I know my rights; I want my phone call!
<DRM> What use is a phone call, if you are unable to speak?
(as seen on /.)
Join The DRM Elimination Crew Now!
http://fci.wikia.com/wiki/Anti-DRM-Campaign

^ permalink raw reply

* Re: [PATCH] Simplified GIT usage guide
From: J. Bruce Fields @ 2008-12-13  1:03 UTC (permalink / raw)
  To: David Howells; +Cc: torvalds, git, linux-kernel
In-Reply-To: <20081212194703.GA17573@fieldses.org>

On Fri, Dec 12, 2008 at 02:47:03PM -0500, bfields wrote:
> On Fri, Dec 12, 2008 at 06:28:27PM +0000, David Howells wrote:
> > Add a guide to using GIT's simpler features.
> > 
> > Signed-off-by: David Howells <dhowells@redhat.com>
> 
> Just a couple random thoughts:

(Also: this patch applies to either the git or linux trees, and you sent
it to both mailing lists.  Looks like you meant it for linux, but you
might want to clarify....)

--b.

> 
> 	- The advantage of adding this to the kernel tree is that you
> 	  can tailor it for a more specific audience (kernel developers
> 	  and testers).  A lot of this (e.g. the object-database
> 	  discussion) seems to be generic introduction-to-git stuff.
> 	  Is there some canonical external documentation you could refer
> 	  to for that stuff, that would allow you to get more quickly to
> 	  the more tailored information?  If not, is there something you
> 	  could improve to the point where you *would* be comfortable
> 	  referring to it?
> 	- How much overlap is there with
> 	  Documentation/development-process/7.AdvancedTopics?  Should
> 	  there be cross-references between the two?
> 
> There's an awful lot of introductions to git out there now (and I've got
> my own share of the blame).

^ permalink raw reply

* Re: [PATCH] Simplified GIT usage guide
From: David Howells @ 2008-12-13  1:04 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: dhowells, torvalds, git, linux-kernel
In-Reply-To: <m33agtgp2v.fsf@localhost.localdomain>

Jakub Narebski <jnareb@gmail.com> wrote:

> Wouldn't it be better to update either "Git User's Manual",

This is better in some ways than book.git-scm.com.  It has pictures for one
thing.  It does, however, go into too much detail too quickly.

> or http://book.git-scm.com?

I didn't see that when googling for stuff.  I have four issues with what's on
this website, though I could perhaps, as you point out, fix three of them
myself:

 (1) It really needs a pictorial representation of the virtual GIT tree.  How
     GIT actually stores its data is irrelevant to people trying to use GIT
     rather than trying to modify GIT.  It would help those trying to use GIT
     if you can help them visualise what they're dealing with.  It doesn't
     have to be complex.

     See the attached FIG file for examples.  Try running it through:

	fig2dev -L pdf <in.fig >out.pdf

 (2) In my document I've tried to thread a worked example through, so that if
     you go through all the steps, they'll work in order.  I'm not sure I've
     been entirely successful, though.

 (3) You put some non-basic stuff in the basic section (branching - this isn't
     ordinarily useful, IMHO), but you miss other stuff out ('git rm' for
     example).

 (4) It needs to be installed with GIT in a form that can easily be cut and
     pasted from (maybe this is the case).

I like the videos, though not all of them work, and a number of sections say
things like:

	github
	repoorcz

whatever that means.

David

#FIG 3.2
Landscape
Center
Inches
A4
100.00
Single
-2
1200 2
0 32 #efefef
0 33 #7f7f7f
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 1485 15420 1485 15660 2025 15660 2025 15420 1485 15420
4 1 0 49 -1 0 10 0.000 4 2 540 1755 15600 C0\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 4365 14820 4365 15060 4905 15060 4905 14820 4365 14820
4 1 0 49 -1 0 10 0.000 4 2 540 4635 15000 B1\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 1485 14820 1485 15060 2025 15060 2025 14820 1485 14820
4 1 0 49 -1 0 10 0.000 4 2 540 1755 15000 C1\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 4365 14220 4365 14460 4905 14460 4905 14220 4365 14220
4 1 0 49 -1 0 10 0.000 4 2 540 4635 14400 B2\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 1485 14220 1485 14460 2025 14460 2025 14220 1485 14220
4 1 0 49 -1 0 10 0.000 4 2 540 1755 14400 C2\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 4365 13620 4365 13860 4905 13860 4905 13620 4365 13620
4 1 0 49 -1 0 10 0.000 4 2 540 4635 13800 B3\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 1485 13620 1485 13860 2025 13860 2025 13620 1485 13620
4 1 0 49 -1 0 10 0.000 4 2 540 1755 13800 C3\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 1485 13020 1485 13260 2025 13260 2025 13020 1485 13020
4 1 0 49 -1 0 10 0.000 4 2 540 1755 13200 C4\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 1485 12300 1485 12540 2025 12540 2025 12300 1485 12300
4 1 0 49 -1 0 10 0.000 4 2 540 1755 12480 C0\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 4365 11700 4365 11940 4905 11940 4905 11700 4365 11700
4 1 0 49 -1 0 10 0.000 4 2 540 4635 11880 B1\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 1485 11700 1485 11940 2025 11940 2025 11700 1485 11700
4 1 0 49 -1 0 10 0.000 4 2 540 1755 11880 C1\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 4365 11100 4365 11340 4905 11340 4905 11100 4365 11100
4 1 0 49 -1 0 10 0.000 4 2 540 4635 11280 B2\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 1485 11100 1485 11340 2025 11340 2025 11100 1485 11100
4 1 0 49 -1 0 10 0.000 4 2 540 1755 11280 C2\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 4365 10500 4365 10740 4905 10740 4905 10500 4365 10500
4 1 0 49 -1 0 10 0.000 4 2 540 4635 10680 B3\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 1485 10500 1485 10740 2025 10740 2025 10500 1485 10500
4 1 0 49 -1 0 10 0.000 4 2 540 1755 10680 C3\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 4365 9780 4365 10020 4905 10020 4905 9780 4365 9780
4 1 0 49 -1 0 10 0.000 4 2 540 4635 9960 F1A\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 4365 9300 4365 9540 4905 9540 4905 9300 4365 9300
4 1 0 49 -1 0 10 0.000 4 2 540 4635 9480 F2A\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 2835 9300 2835 9540 3375 9540 3375 9300 2835 9300
4 1 0 49 -1 0 10 0.000 4 2 540 3105 9480 D0\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 1485 9300 1485 9540 2025 9540 2025 9300 1485 9300
4 1 0 49 -1 0 10 0.000 4 2 540 1755 9480 C0\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 4365 8820 4365 9060 4905 9060 4905 8820 4365 8820
4 1 0 49 -1 0 10 0.000 4 2 540 4635 9000 F3A\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 2835 8340 2835 8580 3375 8580 3375 8340 2835 8340
4 1 0 49 -1 0 10 0.000 4 2 540 3105 8520 D1\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 1485 8340 1485 8580 2025 8580 2025 8340 1485 8340
4 1 0 49 -1 0 10 0.000 4 2 540 1755 8520 C1\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 4365 8100 4365 8340 4905 8340 4905 8100 4365 8100
4 1 0 49 -1 0 10 0.000 4 2 540 4635 8280 F2B\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 1485 7620 1485 7860 2025 7860 2025 7620 1485 7620
4 1 0 49 -1 0 10 0.000 4 2 540 1755 7800 C3\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 4365 6900 4365 7140 4905 7140 4905 6900 4365 6900
4 1 0 49 -1 0 10 0.000 4 2 540 4635 7080 F1A\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 4365 6420 4365 6660 4905 6660 4905 6420 4365 6420
4 1 0 49 -1 0 10 0.000 4 2 540 4635 6600 F2A\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 2835 6420 2835 6660 3375 6660 3375 6420 2835 6420
4 1 0 49 -1 0 10 0.000 4 2 540 3105 6600 D0\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 1485 6420 1485 6660 2025 6660 2025 6420 1485 6420
4 1 0 49 -1 0 10 0.000 4 2 540 1755 6600 C0\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 4365 5940 4365 6180 4905 6180 4905 5940 4365 5940
4 1 0 49 -1 0 10 0.000 4 2 540 4635 6120 F3A\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 2835 5460 2835 5700 3375 5700 3375 5460 2835 5460
4 1 0 49 -1 0 10 0.000 4 2 540 3105 5640 D1\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 1485 5460 1485 5700 2025 5700 2025 5460 1485 5460
4 1 0 49 -1 0 10 0.000 4 2 540 1755 5640 C1\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 4365 5220 4365 5460 4905 5460 4905 5220 4365 5220
4 1 0 49 -1 0 10 0.000 4 2 540 4635 5400 F2B\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 2835 4740 2835 4980 3375 4980 3375 4740 2835 4740
4 1 0 49 -1 0 10 0.000 4 2 540 3105 4920 D2\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 1485 4740 1485 4980 2025 4980 2025 4740 1485 4740
4 1 0 49 -1 0 10 0.000 4 2 540 1755 4920 C2\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 4365 4500 4365 4740 4905 4740 4905 4500 4365 4500
4 1 0 49 -1 0 10 0.000 4 2 540 4635 4680 F1B\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 4365 3780 4365 4020 4905 4020 4905 3780 4365 3780
4 1 0 49 -1 0 10 0.000 4 2 540 4635 3960 F1A\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 4365 3300 4365 3540 4905 3540 4905 3300 4365 3300
4 1 0 49 -1 0 10 0.000 4 2 540 4635 3480 F2A\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 2835 3300 2835 3540 3375 3540 3375 3300 2835 3300
4 1 0 49 -1 0 10 0.000 4 2 540 3105 3480 D0\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 1485 3300 1485 3540 2025 3540 2025 3300 1485 3300
4 1 0 49 -1 0 10 0.000 4 2 540 1755 3480 C0\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 4365 2820 4365 3060 4905 3060 4905 2820 4365 2820
4 1 0 49 -1 0 10 0.000 4 2 540 4635 3000 F3A\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 2835 2340 2835 2580 3375 2580 3375 2340 2835 2340
4 1 0 49 -1 0 10 0.000 4 2 540 3105 2520 D1\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 1485 2340 1485 2580 2025 2580 2025 2340 1485 2340
4 1 0 49 -1 0 10 0.000 4 2 540 1755 2520 C1\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 4365 2100 4365 2340 4905 2340 4905 2100 4365 2100
4 1 0 49 -1 0 10 0.000 4 2 540 4635 2280 F2B\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 4365 1260 4365 1500 4905 1500 4905 1260 4365 1260
4 1 0 49 -1 0 10 0.000 4 2 540 4635 1440 F1A\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 4365 780 4365 1020 4905 1020 4905 780 4365 780
4 1 0 49 -1 0 10 0.000 4 2 540 4635 960 F2A\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 2835 780 2835 1020 3375 1020 3375 780 2835 780
4 1 0 49 -1 0 10 0.000 4 2 540 3105 960 D0\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 1485 780 1485 1020 2025 1020 2025 780 1485 780
4 1 0 49 -1 0 10 0.000 4 2 540 1755 960 C0\001
2 2 0 1 0 7 50 -1 19 0.000 0 0 -1 0 0 5
	 4365 300 4365 540 4905 540 4905 300 4365 300
4 1 0 49 -1 0 10 0.000 4 2 540 4635 480 F3A\001
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 1755 15420 1755 15060
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 2025 14940 4365 14940
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 4635 14820 4635 14460
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 1755 14820 1755 14460
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 4635 14220 4635 13860
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 1755 14220 1755 13860
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 4365 13740 3555 13740
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 1755 13620 1755 13260
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 3
	1 1 0.00 60.000 120.000
	 4635 13620 4635 13140 2025 13140
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 1485 13140 675 13140
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 1755 12300 1755 11940
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 2025 11820 4365 11820
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 4635 11700 4635 11340
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 1755 11700 1755 11340
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 4635 11100 4635 10740
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 1755 11100 1755 10740
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 4365 10620 3555 10620
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 1485 10620 675 10620
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 2835 9420 2025 9420
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 4
	1 1 0.00 60.000 120.000
	 4365 9900 4005 9900 4005 9420 3375 9420
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 3
	1 1 0.00 60.000 120.000
	 4005 9420 4005 9060 4365 9060
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
	1 1 0.00 60.000 120.000
	 4005 9420 4365 9420
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 1755 9300 1755 8580
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 2835 8460 2025 8460
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 1 4
	1 1 0.00 60.000 120.000
	1 1 0.00 60.000 120.000
	 4365 9780 3825 9780 3825 8940 4365 8940
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 3
	1 1 0.00 60.000 120.000
	 3825 8460 3825 8220 4365 8220
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
	 3825 8940 3825 8460 3375 8460
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 1755 8340 1755 7860
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 4
	1 1 0.00 60.000 120.000
	 2835 9300 2385 9300 2385 7740 2025 7740
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 1485 7740 675 7740
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 2835 6540 2025 6540
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 4
	1 1 0.00 60.000 120.000
	 4365 7020 4005 7020 4005 6540 3375 6540
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 3
	1 1 0.00 60.000 120.000
	 4005 6540 4005 6180 4365 6180
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
	1 1 0.00 60.000 120.000
	 4005 6540 4365 6540
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 1755 6420 1755 5700
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 2835 5580 2025 5580
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 1 4
	1 1 0.00 60.000 120.000
	1 1 0.00 60.000 120.000
	 4365 6900 3825 6900 3825 6060 4365 6060
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 3
	1 1 0.00 60.000 120.000
	 3825 5580 3825 5340 4365 5340
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
	 3825 6060 3825 5580 3375 5580
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 1755 5460 1755 4980
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 2835 4860 2025 4860
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 1485 4860 675 4860
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 1 4
	1 1 0.00 60.000 120.000
	1 1 0.00 60.000 120.000
	 4365 5940 3645 5940 3645 5220 4365 5220
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 3
	1 1 0.00 60.000 120.000
	 3645 4860 3645 4620 4365 4620
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
	 3645 5220 3645 4860 3375 4860
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 2835 3420 2025 3420
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 4
	1 1 0.00 60.000 120.000
	 4365 3900 4005 3900 4005 3420 3375 3420
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 3
	1 1 0.00 60.000 120.000
	 4005 3420 4005 3060 4365 3060
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
	1 1 0.00 60.000 120.000
	 4005 3420 4365 3420
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 1755 3300 1755 2580
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 2835 2460 2025 2460
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 1485 2460 675 2460
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 1 4
	1 1 0.00 60.000 120.000
	1 1 0.00 60.000 120.000
	 4365 3780 3825 3780 3825 2940 4365 2940
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 3
	1 1 0.00 60.000 120.000
	 3825 2460 3825 2220 4365 2220
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
	 3825 2940 3825 2460 3375 2460
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 2835 900 2025 900
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
	1 1 0.00 60.000 120.000
	 1485 900 675 900
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 4
	1 1 0.00 60.000 120.000
	 4365 1380 4005 1380 4005 900 3375 900
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 3
	1 1 0.00 60.000 120.000
	 4005 900 4005 420 4365 420
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
	1 1 0.00 60.000 120.000
	 4005 900 4365 900
4 0 0 49 -1 0 10 0.000 4 120 540 3600 13680 Branch\001
4 0 0 49 -1 0 10 0.000 4 120 360 720 13080 HEAD\001
4 0 0 49 -1 0 10 0.000 4 120 990 0 12840 POST-MERGE:\001
4 0 0 49 -1 0 10 0.000 4 120 540 3600 10560 Branch\001
4 0 0 49 -1 0 10 0.000 4 120 360 720 10560 HEAD\001
4 0 0 49 -1 0 10 0.000 4 120 900 0 10320 PRE-MERGE:\001
4 0 0 49 -1 0 10 0.000 4 120 360 720 7680 HEAD\001
4 0 0 49 -1 0 10 0.000 4 120 1350 0 7440 REVERT FILE F2:\001
4 0 0 49 -1 0 10 0.000 4 120 360 720 4800 HEAD\001
4 0 0 49 -1 0 10 0.000 4 120 1350 0 4320 CHANGE FILE F1:\001
4 0 0 49 -1 0 10 0.000 4 120 360 720 2400 HEAD\001
4 0 0 49 -1 0 10 0.000 4 120 1350 0 1920 CHANGE FILE F2:\001
4 0 0 49 -1 0 10 0.000 4 120 360 720 840 HEAD\001
4 0 0 49 -1 0 10 0.000 4 120 1170 0 120 INITIAL TREE:\001

^ permalink raw reply

* Git weekly news: 2008-50
From: Felipe Contreras @ 2008-12-13  1:10 UTC (permalink / raw)
  To: git list

Hi all,

This week I'm trying to address some of the issues mentioned here. I
still would like people to request user accounts to this blog if they
wish to make some git related posts.

http://gitlog.wordpress.com/2008/12/13/git-weekly-links-2008-50/

== Articles ==

Why Subversion does not suck
http://blog.assembla.com/assemblablog/tabid/12618/bid/7437/Why-Subversion-does-not-suck.aspx

Articles on Git, a distributed version control system
http://www.gnome.org/~federico/index.html#git

Pushing and pulling with Git, part 2
http://www.gnome.org/~federico/news-2008-12.html#pushing-and-pulling-with-git-2

Setting Up Ruby on Rails Projects with Git and Github
A straight forward guide to setup git and github in Windows
http://beans.seartipy.com/2008/12/09/setting-up-ruby-on-rails-projects-with-git-and-github/

Insider Guide to GitHub
Comprehensive screencasts to get familiar with git by Scott Chacon
http://github.com/blog/261-insider-guide-to-github-series
http://www.pragprog.com/screencasts/v-scgithub/insider-guide-to-github

YUI on GitHub
Yahoo's User Interface Library is now hosted publicly on Github
http://github.com/blog/262-yui-on-github

Las ventajas de Git
Discussion about Git's advantages in barrapunto.com (Spanish's slashdot)
http://preguntas.barrapunto.com/article.pl?sid=08/12/08/019203

== In Japaneese (I've no idea) ==

coderepos の commit 数ナンバー1の人が github を練習したメモ
http://d.hatena.ne.jp/tokuhirom/20081212/1229080990

gitでドットファイルをバージョン管理する
http://d.hatena.ne.jp/javascripter/20081208/1228730967

gistコマンドよりちょっと便利なgisty
http://d.hatena.ne.jp/swdyh/20081207/1228655198

== Still popular ==

Why Git is Better than X
Brief explanation about why you should switch from X to Git
http://whygitisbetterthanx.com/

Por qué Git es mejor que X
Also very popular is the Spanish version
http://es.whygitisbetterthanx.com/

== My picks ==

What are your favorite git features or tricks?
A bunch of git tricks
http://stackoverflow.com/questions/347901/what-are-your-favorite-git-features-or-tricks

-- 
Felipe Contreras

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox