git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [JGIT PATCH 0/6] Diff processing utilities
@ 2009-05-02  2:08 Shawn O. Pearce
  2009-05-02  2:08 ` [JGIT PATCH 1/6] Add set to IntList Shawn O. Pearce
  2009-05-03 14:24 ` [JGIT PATCH 0/6] Diff processing utilities Johannes Schindelin
  0 siblings, 2 replies; 15+ messages in thread
From: Shawn O. Pearce @ 2009-05-02  2:08 UTC (permalink / raw)
  To: Robin Rosenberg, Johannes Schindelin; +Cc: git

Back in late 2008 Dscho started writing some basic diff routines
for JGit.  That work got partially shelved when he had to move onto
more important day-job topics.  This series is starting to revive
some portions of his work, and bring it into JGit.

In particular I want at least this much merged, so I can use it
in Gerrit Code Review to massage an existing patch file before I
display it.  Currently most of that logic is within Gerrit, and
I'd much rather push it down into JGit where anyone can reuse it.

To be clear, this series *does not* contain a way to create a diff.
Dscho wrote much more code which is not yet ready for inclusion.

After applying this series we have an EditList describing what
happened in an existing patch we parsed with the existing Patch
class, and a DiffFormatter which can be used to reflow a patch
script if the repository has both the pre and post image objects.

The formatter part is really only useful if the repository created
the patch script, or received the objects over a git native object
transport, like Gerrit Code Review does with SSH.

Dscho, can you give us yea-or-nay for SBO lines for the patches
I've attributed to you?


Johannes E. Schindelin (3):
  Add diff.Edit to describe an edit region within a file
  Add diff.RawText to index a file content for later compares
  Add diff.DiffFormatter to create Git style unified patch scripts

Shawn O. Pearce (3):
  Add set to IntList
  Add diff.EditList to provide for a list of Edit instances
  Teach FileHeader, HunkHeader how to create an EditList

 .../spearce/jgit/patch/testEditList_Types.patch    |   24 +++
 .../tst/org/spearce/jgit/diff/EditListTest.java    |  121 +++++++++++++
 .../tst/org/spearce/jgit/diff/EditTest.java        |  139 ++++++++++++++
 .../tst/org/spearce/jgit/diff/RawTextTest.java     |   93 ++++++++++
 .../tst/org/spearce/jgit/patch/EditListTest.java   |   95 ++++++++++
 .../tst/org/spearce/jgit/util/IntListTest.java     |   21 +++
 .../src/org/spearce/jgit/diff/DiffFormatter.java   |  187 +++++++++++++++++++
 .../src/org/spearce/jgit/diff/Edit.java            |  189 ++++++++++++++++++++
 .../src/org/spearce/jgit/diff/EditList.java        |   93 ++++++++++
 .../src/org/spearce/jgit/diff/RawText.java         |  172 ++++++++++++++++++
 .../src/org/spearce/jgit/diff/Sequence.java        |   78 ++++++++
 .../src/org/spearce/jgit/patch/FileHeader.java     |    9 +
 .../src/org/spearce/jgit/patch/HunkHeader.java     |   48 +++++
 .../src/org/spearce/jgit/util/IntList.java         |   17 ++
 14 files changed, 1286 insertions(+), 0 deletions(-)
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/patch/testEditList_Types.patch
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/diff/EditListTest.java
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/diff/EditTest.java
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/diff/RawTextTest.java
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/EditListTest.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/diff/DiffFormatter.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/diff/Edit.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/diff/EditList.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/diff/RawText.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/diff/Sequence.java

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [JGIT PATCH 1/6] Add set to IntList
  2009-05-02  2:08 [JGIT PATCH 0/6] Diff processing utilities Shawn O. Pearce
@ 2009-05-02  2:08 ` Shawn O. Pearce
  2009-05-02  2:08   ` [JGIT PATCH 2/6] Add diff.Edit to describe an edit region within a file Shawn O. Pearce
  2009-05-03  7:07   ` [JGIT PATCH 1/6] Add set to IntList Robin Rosenberg
  2009-05-03 14:24 ` [JGIT PATCH 0/6] Diff processing utilities Johannes Schindelin
  1 sibling, 2 replies; 15+ messages in thread
From: Shawn O. Pearce @ 2009-05-02  2:08 UTC (permalink / raw)
  To: Robin Rosenberg, Johannes Schindelin; +Cc: git

Some applications may wish to modify an int list.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../tst/org/spearce/jgit/util/IntListTest.java     |   21 ++++++++++++++++++++
 .../src/org/spearce/jgit/util/IntList.java         |   17 ++++++++++++++++
 2 files changed, 38 insertions(+), 0 deletions(-)

diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/util/IntListTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/util/IntListTest.java
index c470d55..ce0d7af 100644
--- a/org.spearce.jgit.test/tst/org/spearce/jgit/util/IntListTest.java
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/util/IntListTest.java
@@ -144,6 +144,27 @@ public void testClear() {
 		}
 	}
 
+	public void testSet() {
+		final IntList i = new IntList();
+		i.add(1);
+		assertEquals(1, i.size());
+		assertEquals(1, i.get(0));
+		
+		i.set(0, 5);
+		assertEquals(5, i.get(0));
+		
+		try {
+			i.set(5, 5);
+			fail("accepted set of 5 beyond end of list");
+		} catch (ArrayIndexOutOfBoundsException e){
+			assertTrue(true);
+		}
+
+		i.set(1, 2);
+		assertEquals(2, i.size());
+		assertEquals(2, i.get(1));
+	}
+
 	public void testToString() {
 		final IntList i = new IntList();
 		i.add(1);
diff --git a/org.spearce.jgit/src/org/spearce/jgit/util/IntList.java b/org.spearce.jgit/src/org/spearce/jgit/util/IntList.java
index 0a84793..9d86a5c 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/util/IntList.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/util/IntList.java
@@ -94,6 +94,23 @@ public void add(final int n) {
 	}
 
 	/**
+	 * Assign an entry in the list.
+	 * 
+	 * @param index
+	 *            index to set, must be in the range [0, {@link #size()}).
+	 * @param n
+	 *            value to store at the position.
+	 */
+	public void set(final int index, final int n) {
+		if (count < index)
+			throw new ArrayIndexOutOfBoundsException(index);
+		else if (count == index)
+			add(n);
+		else
+			entries[index] = n;
+	}
+	
+	/**
 	 * Pad the list with entries.
 	 *
 	 * @param toIndex
-- 
1.6.3.rc3.212.g8c698

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [JGIT PATCH 2/6] Add diff.Edit to describe an edit region within a file
  2009-05-02  2:08 ` [JGIT PATCH 1/6] Add set to IntList Shawn O. Pearce
@ 2009-05-02  2:08   ` Shawn O. Pearce
  2009-05-02  2:08     ` [JGIT PATCH 3/6] Add diff.EditList to provide for a list of Edit instances Shawn O. Pearce
  2009-05-03  7:07   ` [JGIT PATCH 1/6] Add set to IntList Robin Rosenberg
  1 sibling, 1 reply; 15+ messages in thread
From: Shawn O. Pearce @ 2009-05-02  2:08 UTC (permalink / raw)
  To: Robin Rosenberg, Johannes Schindelin; +Cc: git, Johannes E. Schindelin

From: Johannes E. Schindelin <johannes.schindelin@gmx.de>

This type represents a difference region detected between two files.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../tst/org/spearce/jgit/diff/EditTest.java        |  139 ++++++++++++++
 .../src/org/spearce/jgit/diff/Edit.java            |  189 ++++++++++++++++++++
 2 files changed, 328 insertions(+), 0 deletions(-)
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/diff/EditTest.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/diff/Edit.java

diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/diff/EditTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/diff/EditTest.java
new file mode 100644
index 0000000..8f9b0e3
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/diff/EditTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2009, 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.diff;
+
+import junit.framework.TestCase;
+
+public class EditTest extends TestCase {
+	public void testCreate() {
+		final Edit e = new Edit(1, 2, 3, 4);
+		assertEquals(1, e.getBeginA());
+		assertEquals(2, e.getEndA());
+		assertEquals(3, e.getBeginB());
+		assertEquals(4, e.getEndB());
+	}
+
+	public void testCreateEmpty() {
+		final Edit e = new Edit(1, 3);
+		assertEquals(1, e.getBeginA());
+		assertEquals(1, e.getEndA());
+		assertEquals(3, e.getBeginB());
+		assertEquals(3, e.getEndB());
+	}
+
+	public void testSwap() {
+		final Edit e = new Edit(1, 2, 3, 4);
+		e.swap();
+		assertEquals(3, e.getBeginA());
+		assertEquals(4, e.getEndA());
+		assertEquals(1, e.getBeginB());
+		assertEquals(2, e.getEndB());
+	}
+
+	public void testType_Insert() {
+		final Edit e = new Edit(1, 1, 1, 2);
+		assertSame(Edit.Type.INSERT, e.getType());
+	}
+
+	public void testType_Delete() {
+		final Edit e = new Edit(1, 2, 1, 1);
+		assertSame(Edit.Type.DELETE, e.getType());
+	}
+
+	public void testType_Replace() {
+		final Edit e = new Edit(1, 2, 1, 4);
+		assertSame(Edit.Type.REPLACE, e.getType());
+	}
+
+	public void testType_Empty() {
+		assertSame(Edit.Type.EMPTY, new Edit(1, 1, 2, 2).getType());
+		assertSame(Edit.Type.EMPTY, new Edit(1, 2).getType());
+	}
+
+	public void testToString() {
+		final Edit e = new Edit(1, 2, 1, 4);
+		assertEquals("REPLACE(1-2,1-4)", e.toString());
+	}
+
+	public void testEquals1() {
+		final Edit e1 = new Edit(1, 2, 3, 4);
+		final Edit e2 = new Edit(1, 2, 3, 4);
+
+		assertTrue(e1.equals(e1));
+		assertTrue(e1.equals(e2));
+		assertTrue(e2.equals(e1));
+		assertEquals(e1.hashCode(), e2.hashCode());
+		assertFalse(e1.equals(""));
+	}
+
+	public void testNotEquals1() {
+		assertFalse(new Edit(1, 2, 3, 4).equals(new Edit(0, 2, 3, 4)));
+	}
+
+	public void testNotEquals2() {
+		assertFalse(new Edit(1, 2, 3, 4).equals(new Edit(1, 0, 3, 4)));
+	}
+
+	public void testNotEquals3() {
+		assertFalse(new Edit(1, 2, 3, 4).equals(new Edit(1, 2, 0, 4)));
+	}
+
+	public void testNotEquals4() {
+		assertFalse(new Edit(1, 2, 3, 4).equals(new Edit(1, 2, 3, 0)));
+	}
+
+	public void testExtendA() {
+		final Edit e = new Edit(1, 2, 1, 1);
+
+		e.extendA();
+		assertEquals(new Edit(1, 3, 1, 1), e);
+
+		e.extendA();
+		assertEquals(new Edit(1, 4, 1, 1), e);
+	}
+
+	public void testExtendB() {
+		final Edit e = new Edit(1, 2, 1, 1);
+
+		e.extendB();
+		assertEquals(new Edit(1, 2, 1, 2), e);
+
+		e.extendB();
+		assertEquals(new Edit(1, 2, 1, 3), e);
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/diff/Edit.java b/org.spearce.jgit/src/org/spearce/jgit/diff/Edit.java
new file mode 100644
index 0000000..460e2c4
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/diff/Edit.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2008, Johannes E. Schindelin <johannes.schindelin@gmx.de>
+ *
+ * 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.diff;
+
+/**
+ * A modified region detected between two versions of roughly the same content.
+ * <p>
+ * An edit covers the modified region only. It does not cover a common region.
+ * <p>
+ * Regions should be specified using 0 based notation, so add 1 to the start and
+ * end marks for line numbers in a file.
+ * <p>
+ * An edit where <code>beginA == endA && beginB < endB</code> is an insert edit,
+ * that is sequence B inserted the elements in region
+ * <code>[beginB, endB)</code> at <code>beginA</code>.
+ * <p>
+ * An edit where <code>beginA < endA && beginB == endB</code> is a delete edit,
+ * that is sequence B has removed the elements between
+ * <code>[beginA, endA)</code>.
+ * <p>
+ * An edit where <code>beginA < endA && beginB < endB</code> is a replace edit,
+ * that is sequence B has replaced the range of elements between
+ * <code>[beginA, endA)</code> with those found in <code>[beginB, endB)</code>.
+ */
+public class Edit {
+	/** Type of edit */
+	public static enum Type {
+		/** Sequence B has inserted the region. */
+		INSERT,
+
+		/** Sequence B has removed the region. */
+		DELETE,
+
+		/** Sequence B has replaced the region with different content. */
+		REPLACE,
+
+		/** Sequence A and B have zero length, describing nothing. */
+		EMPTY;
+	}
+
+	int beginA;
+
+	int endA;
+
+	int beginB;
+
+	int endB;
+
+	/**
+	 * Create a new empty edit.
+	 * 
+	 * @param as
+	 *            beginA: start and end of region in sequence A; 0 based.
+	 * @param bs
+	 *            beginB: start and end of region in sequence B; 0 based.
+	 */
+	public Edit(final int as, final int bs) {
+		this(as, as, bs, bs);
+	}
+
+	/**
+	 * Create a new edit.
+	 * 
+	 * @param as
+	 *            beginA: start of region in sequence A; 0 based.
+	 * @param ae
+	 *            endA: end of region in sequence A; must be >= as.
+	 * @param bs
+	 *            beginB: start of region in sequence B; 0 based.
+	 * @param be
+	 *            endB: end of region in sequence B; must be >= bs.
+	 */
+	public Edit(final int as, final int ae, final int bs, final int be) {
+		beginA = as;
+		endA = ae;
+
+		beginB = bs;
+		endB = be;
+	}
+
+	/** @return the type of this region */
+	public final Type getType() {
+		if (beginA == endA && beginB < endB)
+			return Type.INSERT;
+		if (beginA < endA && beginB == endB)
+			return Type.DELETE;
+		if (beginA == endA && beginB == endB)
+			return Type.EMPTY;
+		return Type.REPLACE;
+	}
+
+	/** @return start point in sequence A. */
+	public final int getBeginA() {
+		return beginA;
+	}
+
+	/** @return end point in sequence A. */
+	public final int getEndA() {
+		return endA;
+	}
+
+	/** @return start point in sequence B. */
+	public final int getBeginB() {
+		return beginB;
+	}
+
+	/** @return end point in sequence B. */
+	public final int getEndB() {
+		return endB;
+	}
+
+	/** Increase {@link #getEndA()} by 1. */
+	public void extendA() {
+		endA++;
+	}
+
+	/** Increase {@link #getEndB()} by 1. */
+	public void extendB() {
+		endB++;
+	}
+
+	/** Swap A and B, so the edit goes the other direction. */
+	public void swap() {
+		final int sBegin = beginA;
+		final int sEnd = endA;
+
+		beginA = beginB;
+		endA = endB;
+
+		beginB = sBegin;
+		endB = sEnd;
+	}
+
+	@Override
+	public int hashCode() {
+		return beginA ^ endA;
+	}
+
+	@Override
+	public boolean equals(final Object o) {
+		if (o instanceof Edit) {
+			final Edit e = (Edit) o;
+			return this.beginA == e.beginA && this.endA == e.endA
+					&& this.beginB == e.beginB && this.endB == e.endB;
+		}
+		return false;
+	}
+
+	@Override
+	public String toString() {
+		final Type t = getType();
+		return t + "(" + beginA + "-" + endA + "," + beginB + "-" + endB + ")";
+	}
+}
-- 
1.6.3.rc3.212.g8c698

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [JGIT PATCH 3/6] Add diff.EditList to provide for a list of Edit instances
  2009-05-02  2:08   ` [JGIT PATCH 2/6] Add diff.Edit to describe an edit region within a file Shawn O. Pearce
@ 2009-05-02  2:08     ` Shawn O. Pearce
  2009-05-02  2:08       ` [JGIT PATCH 4/6] Add diff.RawText to index a file content for later compares Shawn O. Pearce
  0 siblings, 1 reply; 15+ messages in thread
From: Shawn O. Pearce @ 2009-05-02  2:08 UTC (permalink / raw)
  To: Robin Rosenberg, Johannes Schindelin; +Cc: git

Often a file difference involves more than one modified region, in
which case we need more than one Edit instance to describe the full
change that was made.

List<Edit> could be used with any generic List implementation, but
we may want to offer additional API functionality that is Edit list
specific, so I'm creating a specialized List implementation for it.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../tst/org/spearce/jgit/diff/EditListTest.java    |  121 ++++++++++++++++++++
 .../src/org/spearce/jgit/diff/EditList.java        |   93 +++++++++++++++
 2 files changed, 214 insertions(+), 0 deletions(-)
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/diff/EditListTest.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/diff/EditList.java

diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/diff/EditListTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/diff/EditListTest.java
new file mode 100644
index 0000000..54d5007
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/diff/EditListTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2009, 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.diff;
+
+import java.util.Iterator;
+
+import junit.framework.TestCase;
+
+public class EditListTest extends TestCase {
+	public void testEmpty() {
+		final EditList l = new EditList();
+		assertEquals(0, l.size());
+		assertTrue(l.isEmpty());
+		assertEquals("EditList[]", l.toString());
+
+		assertTrue(l.equals(l));
+		assertTrue(l.equals(new EditList()));
+		assertFalse(l.equals(""));
+		assertEquals(l.hashCode(), new EditList().hashCode());
+	}
+
+	public void testAddOne() {
+		final Edit e = new Edit(1, 2, 1, 1);
+		final EditList l = new EditList();
+		l.add(e);
+		assertEquals(1, l.size());
+		assertFalse(l.isEmpty());
+		assertSame(e, l.get(0));
+		assertSame(e, l.iterator().next());
+
+		assertTrue(l.equals(l));
+		assertFalse(l.equals(new EditList()));
+
+		final EditList l2 = new EditList();
+		l2.add(e);
+		assertTrue(l.equals(l2));
+		assertTrue(l2.equals(l));
+		assertEquals(l.hashCode(), l2.hashCode());
+	}
+
+	public void testAddTwo() {
+		final Edit e1 = new Edit(1, 2, 1, 1);
+		final Edit e2 = new Edit(8, 8, 8, 12);
+		final EditList l = new EditList();
+		l.add(e1);
+		l.add(e2);
+		assertEquals(2, l.size());
+		assertSame(e1, l.get(0));
+		assertSame(e2, l.get(1));
+
+		final Iterator<Edit> i = l.iterator();
+		assertSame(e1, i.next());
+		assertSame(e2, i.next());
+
+		assertTrue(l.equals(l));
+		assertFalse(l.equals(new EditList()));
+
+		final EditList l2 = new EditList();
+		l2.add(e1);
+		l2.add(e2);
+		assertTrue(l.equals(l2));
+		assertTrue(l2.equals(l));
+		assertEquals(l.hashCode(), l2.hashCode());
+	}
+
+	public void testSet() {
+		final Edit e1 = new Edit(1, 2, 1, 1);
+		final Edit e2 = new Edit(3, 4, 3, 3);
+		final EditList l = new EditList();
+		l.add(e1);
+		assertSame(e1, l.get(0));
+		assertSame(e1, l.set(0, e2));
+		assertSame(e2, l.get(0));
+	}
+
+	public void testRemove() {
+		final Edit e1 = new Edit(1, 2, 1, 1);
+		final Edit e2 = new Edit(8, 8, 8, 12);
+		final EditList l = new EditList();
+		l.add(e1);
+		l.add(e2);
+		l.remove(e1);
+		assertEquals(1, l.size());
+		assertSame(e2, l.get(0));
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/diff/EditList.java b/org.spearce.jgit/src/org/spearce/jgit/diff/EditList.java
new file mode 100644
index 0000000..0ccf366
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/diff/EditList.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2009, 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.diff;
+
+import java.util.AbstractList;
+import java.util.ArrayList;
+
+/** Specialized list of {@link Edit}s in a document. */
+public class EditList extends AbstractList<Edit> {
+	private final ArrayList<Edit> container;
+
+	/** Create a new, empty edit list. */
+	public EditList() {
+		container = new ArrayList<Edit>();
+	}
+
+	@Override
+	public int size() {
+		return container.size();
+	}
+
+	@Override
+	public Edit get(final int index) {
+		return container.get(index);
+	}
+
+	@Override
+	public Edit set(final int index, final Edit element) {
+		return container.set(index, element);
+	}
+
+	@Override
+	public void add(final int index, final Edit element) {
+		container.add(index, element);
+	}
+
+	@Override
+	public Edit remove(final int index) {
+		return container.remove(index);
+	}
+
+	@Override
+	public int hashCode() {
+		return container.hashCode();
+	}
+
+	@Override
+	public boolean equals(final Object o) {
+		if (o instanceof EditList)
+			return container.equals(((EditList) o).container);
+		return false;
+	}
+
+	@Override
+	public String toString() {
+		return "EditList" + container.toString();
+	}
+}
-- 
1.6.3.rc3.212.g8c698

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [JGIT PATCH 4/6] Add diff.RawText to index a file content for later compares
  2009-05-02  2:08     ` [JGIT PATCH 3/6] Add diff.EditList to provide for a list of Edit instances Shawn O. Pearce
@ 2009-05-02  2:08       ` Shawn O. Pearce
  2009-05-02  2:08         ` [JGIT PATCH 5/6] Teach FileHeader, HunkHeader how to create an EditList Shawn O. Pearce
  0 siblings, 1 reply; 15+ messages in thread
From: Shawn O. Pearce @ 2009-05-02  2:08 UTC (permalink / raw)
  To: Robin Rosenberg, Johannes Schindelin; +Cc: git, Johannes E. Schindelin

From: Johannes E. Schindelin <johannes.schindelin@gmx.de>

The RawText class converts a byte[] into an indexed map of file
lines, assuming the source content is a UNIX formatted text file.
This fits in line with Git's usage of libxdiff, where typically
the file content is treated as a UNIX formatted text file, unless
binary content was previously detected in the file stream.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../tst/org/spearce/jgit/diff/RawTextTest.java     |   93 +++++++++++
 .../src/org/spearce/jgit/diff/RawText.java         |  172 ++++++++++++++++++++
 .../src/org/spearce/jgit/diff/Sequence.java        |   78 +++++++++
 3 files changed, 343 insertions(+), 0 deletions(-)
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/diff/RawTextTest.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/diff/RawText.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/diff/Sequence.java

diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/diff/RawTextTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/diff/RawTextTest.java
new file mode 100644
index 0000000..a7c621e
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/diff/RawTextTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2009, 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.diff;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+import org.spearce.jgit.lib.Constants;
+import org.spearce.jgit.util.RawParseUtils;
+
+public class RawTextTest extends TestCase {
+	public void testEmpty() {
+		final RawText r = new RawText(new byte[0]);
+		assertEquals(0, r.size());
+	}
+
+	public void testEquals() {
+		final RawText a = new RawText(Constants.encodeASCII("foo-a\nfoo-b\n"));
+		final RawText b = new RawText(Constants.encodeASCII("foo-b\nfoo-c\n"));
+
+		assertEquals(2, a.size());
+		assertEquals(2, b.size());
+
+		// foo-a != foo-b
+		assertFalse(a.equals(0, b, 0));
+		assertFalse(b.equals(0, a, 0));
+
+		// foo-b == foo-b
+		assertTrue(a.equals(1, b, 0));
+		assertTrue(b.equals(0, a, 1));
+	}
+
+	public void testWriteLine1() throws IOException {
+		final RawText a = new RawText(Constants.encodeASCII("foo-a\nfoo-b\n"));
+		final ByteArrayOutputStream o = new ByteArrayOutputStream();
+		a.writeLine(o, 0);
+		final byte[] r = o.toByteArray();
+		assertEquals("foo-a", RawParseUtils.decode(r));
+	}
+
+	public void testWriteLine2() throws IOException {
+		final RawText a = new RawText(Constants.encodeASCII("foo-a\nfoo-b"));
+		final ByteArrayOutputStream o = new ByteArrayOutputStream();
+		a.writeLine(o, 1);
+		final byte[] r = o.toByteArray();
+		assertEquals("foo-b", RawParseUtils.decode(r));
+	}
+
+	public void testWriteLine3() throws IOException {
+		final RawText a = new RawText(Constants.encodeASCII("a\n\nb\n"));
+		final ByteArrayOutputStream o = new ByteArrayOutputStream();
+		a.writeLine(o, 1);
+		final byte[] r = o.toByteArray();
+		assertEquals("", RawParseUtils.decode(r));
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/diff/RawText.java b/org.spearce.jgit/src/org/spearce/jgit/diff/RawText.java
new file mode 100644
index 0000000..1d10082
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/diff/RawText.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2008, Johannes E. Schindelin <johannes.schindelin@gmx.de>
+ * Copyright (C) 2009, 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.diff;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.spearce.jgit.util.IntList;
+import org.spearce.jgit.util.RawParseUtils;
+
+/**
+ * A Sequence supporting UNIX formatted text in byte[] format.
+ * <p>
+ * Elements of the sequence are the lines of the file, as delimited by the UNIX
+ * newline character ('\n'). The file content is treated as 8 bit binary text,
+ * with no assumptions or requirements on character encoding.
+ * <p>
+ * Note that the first line of the file is element 0, as defined by the Sequence
+ * interface API. Traditionally in a text editor a patch file the first line is
+ * line number 1. Callers may need to subtract 1 prior to invoking methods if
+ * they are converting from "line number" to "element index".
+ */
+public class RawText implements Sequence {
+	/** The file content for this sequence. */
+	protected final byte[] content;
+
+	/** Map of line number to starting position within {@link #content}. */
+	protected final IntList lines;
+
+	/** Hash code for each line, for fast equality elimination. */
+	protected final IntList hashes;
+
+	/**
+	 * Create a new sequence from an existing content byte array.
+	 * <p>
+	 * The entire array (indexes 0 through length-1) is used as the content.
+	 * 
+	 * @param input
+	 *            the content array. The array is never modified, so passing
+	 *            through cached arrays is safe.
+	 */
+	public RawText(final byte[] input) {
+		content = input;
+		lines = RawParseUtils.lineMap(content, 0, content.length);
+		hashes = computeHashes();
+	}
+
+	public int size() {
+		// The line map is always 2 entries larger than the number of lines in
+		// the file. Index 0 is padded out/unused. The last index is the total
+		// length of the buffer, and acts as a sentinel.
+		//
+		return lines.size() - 2;
+	}
+
+	public boolean equals(final int i, final Sequence other, final int j) {
+		return equals(this, i + 1, (RawText) other, j + 1);
+	}
+
+	private static boolean equals(final RawText a, final int ai,
+			final RawText b, final int bi) {
+		if (a.hashes.get(ai) != b.hashes.get(bi))
+			return false;
+
+		int as = a.lines.get(ai);
+		int bs = b.lines.get(bi);
+		final int ae = a.lines.get(ai + 1);
+		final int be = b.lines.get(bi + 1);
+
+		if (ae - as != be - bs)
+			return false;
+
+		while (as < ae) {
+			if (a.content[as++] != b.content[bs++])
+				return false;
+		}
+		return true;
+	}
+
+	/**
+	 * Write a specific line to the output stream, without its trailing LF.
+	 * <p>
+	 * The specified line is copied as-is, with no character encoding
+	 * translation performed.
+	 * <p>
+	 * If the specified line ends with an LF ('\n'), the LF is <b>not</b>
+	 * copied. It is up to the caller to write the LF, if desired, between
+	 * output lines.
+	 * 
+	 * @param out
+	 *            stream to copy the line data onto.
+	 * @param i
+	 *            index of the line to extract. Note this is 0-based, so line
+	 *            number 1 is actually index 0.
+	 * @throws IOException
+	 *             the stream write operation failed.
+	 */
+	public void writeLine(final OutputStream out, final int i)
+			throws IOException {
+		final int start = lines.get(i + 1);
+		int end = lines.get(i + 2);
+		if (content[end - 1] == '\n')
+			end--;
+		out.write(content, start, end - start);
+	}
+
+	private IntList computeHashes() {
+		final IntList r = new IntList(lines.size());
+		r.add(0);
+		for (int lno = 1; lno < lines.size() - 1; lno++) {
+			final int ptr = lines.get(lno);
+			final int end = lines.get(lno + 1);
+			r.add(hashLine(content, ptr, end));
+		}
+		r.add(0);
+		return r;
+	}
+
+	/**
+	 * Compute a hash code for a single line.
+	 * 
+	 * @param raw
+	 *            the raw file content.
+	 * @param ptr
+	 *            first byte of the content line to hash.
+	 * @param end
+	 *            1 past the last byte of the content line.
+	 * @return hash code for the region <code>[ptr, end)</code> of raw.
+	 */
+	protected int hashLine(final byte[] raw, int ptr, final int end) {
+		int hash = 5381;
+		for (; ptr < end; ptr++)
+			hash = (hash << 5) ^ (raw[ptr] & 0xff);
+		return hash;
+	}
+}
\ No newline at end of file
diff --git a/org.spearce.jgit/src/org/spearce/jgit/diff/Sequence.java b/org.spearce.jgit/src/org/spearce/jgit/diff/Sequence.java
new file mode 100644
index 0000000..8e754e0
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/diff/Sequence.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2008, Johannes E. Schindelin <johannes.schindelin@gmx.de>
+ *
+ * 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.diff;
+
+/**
+ * Arbitrary sequence of elements with fast comparison support.
+ * <p>
+ * A sequence of elements is defined to contain elements in the index range
+ * <code>[0, {@link #size()})</code>, like a standard Java List implementation.
+ * Unlike a List, the members of the sequence are not directly obtainable, but
+ * element equality can be tested if two Sequences are the same implementation.
+ * <p>
+ * An implementation may chose to implement the equals semantic as necessary,
+ * including fuzzy matching rules such as ignoring insignificant sub-elements,
+ * e.g. ignoring whitespace differences in text.
+ * <p>
+ * Implementations of Sequence are primarily intended for use in content
+ * difference detection algorithms, to produce an {@link EditList} of
+ * {@link Edit} instances describing how two Sequence instances differ.
+ */
+public interface Sequence {
+	/** @return total number of items in the sequence. */
+	public int size();
+
+	/**
+	 * Determine if the i-th member is equal to the j-th member.
+	 * <p>
+	 * Implementations must ensure <code>equals(thisIdx,other,otherIdx)</code>
+	 * returns the same as <code>other.equals(otherIdx,this,thisIdx)</code>.
+	 * 
+	 * @param thisIdx
+	 *            index within <code>this</code> sequence; must be in the range
+	 *            <code>[ 0, this.size() )</code>.
+	 * @param other
+	 *            another sequence; must be the same implementation class, that
+	 *            is <code>this.getClass() == other.getClass()</code>.
+	 * @param otherIdx
+	 *            index within <code>other</code> sequence; must be in the range
+	 *            <code>[ 0, other.size() )</code>.
+	 * @return true if the elements are equal; false if they are not equal.
+	 */
+	public boolean equals(int thisIdx, Sequence other, int otherIdx);
+}
\ No newline at end of file
-- 
1.6.3.rc3.212.g8c698

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [JGIT PATCH 5/6] Teach FileHeader, HunkHeader how to create an EditList
  2009-05-02  2:08       ` [JGIT PATCH 4/6] Add diff.RawText to index a file content for later compares Shawn O. Pearce
@ 2009-05-02  2:08         ` Shawn O. Pearce
  2009-05-02  2:08           ` [JGIT PATCH 6/6] Add diff.DiffFormatter to create Git style unified patch scripts Shawn O. Pearce
  0 siblings, 1 reply; 15+ messages in thread
From: Shawn O. Pearce @ 2009-05-02  2:08 UTC (permalink / raw)
  To: Robin Rosenberg, Johannes Schindelin; +Cc: git

The EditList type along with its member Edit instances can be very
useful when mining a patch for information about the change it is
trying to represent to the content.  This can be useful to create a
modified version of the patch, such as with larger or smaller number
of context lines.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../spearce/jgit/patch/testEditList_Types.patch    |   24 +++++
 .../tst/org/spearce/jgit/patch/EditListTest.java   |   95 ++++++++++++++++++++
 .../src/org/spearce/jgit/patch/FileHeader.java     |    9 ++
 .../src/org/spearce/jgit/patch/HunkHeader.java     |   48 ++++++++++
 4 files changed, 176 insertions(+), 0 deletions(-)
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/patch/testEditList_Types.patch
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/EditListTest.java

diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/patch/testEditList_Types.patch b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/patch/testEditList_Types.patch
new file mode 100644
index 0000000..e5363eb
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/patch/testEditList_Types.patch
@@ -0,0 +1,24 @@
+diff --git a/X b/X
+index a3648a1..2d44096 100644
+--- a/X
++++ b/X
+@@ -2,2 +2,3 @@ a
+ b
++c
+ d
+@@ -16,4 +17,2 @@ p
+ q
+-r
+-s
+ t
+@@ -22,4 +21,8 @@ v
+ w
+-x
+-y
++0
++1
++2
++3
++4
++5
+ z
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/EditListTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/EditListTest.java
new file mode 100644
index 0000000..452f661
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/EditListTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2009, 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;
+
+import org.spearce.jgit.diff.Edit;
+import org.spearce.jgit.diff.EditList;
+
+public class EditListTest extends TestCase {
+	public void testHunkHeader() throws IOException {
+		final Patch p = parseTestPatchFile("testGetText_BothISO88591.patch");
+		final FileHeader fh = p.getFiles().get(0);
+
+		final EditList list0 = fh.getHunks().get(0).toEditList();
+		assertEquals(1, list0.size());
+		assertEquals(new Edit(4 - 1, 5 - 1, 4 - 1, 5 - 1), list0.get(0));
+
+		final EditList list1 = fh.getHunks().get(1).toEditList();
+		assertEquals(1, list1.size());
+		assertEquals(new Edit(16 - 1, 17 - 1, 16 - 1, 17 - 1), list1.get(0));
+	}
+
+	public void testFileHeader() throws IOException {
+		final Patch p = parseTestPatchFile("testGetText_BothISO88591.patch");
+		final FileHeader fh = p.getFiles().get(0);
+		final EditList e = fh.toEditList();
+		assertEquals(2, e.size());
+		assertEquals(new Edit(4 - 1, 5 - 1, 4 - 1, 5 - 1), e.get(0));
+		assertEquals(new Edit(16 - 1, 17 - 1, 16 - 1, 17 - 1), e.get(1));
+	}
+
+	public void testTypes() throws IOException {
+		final Patch p = parseTestPatchFile("testEditList_Types.patch");
+		final FileHeader fh = p.getFiles().get(0);
+		final EditList e = fh.toEditList();
+		assertEquals(3, e.size());
+		assertEquals(new Edit(3 - 1, 3 - 1, 3 - 1, 4 - 1), e.get(0));
+		assertEquals(new Edit(17 - 1, 19 - 1, 18 - 1, 18 - 1), e.get(1));
+		assertEquals(new Edit(23 - 1, 25 - 1, 22 - 1, 28 - 1), e.get(2));
+	}
+
+	private Patch parseTestPatchFile(final String patchFile) throws IOException {
+		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/src/org/spearce/jgit/patch/FileHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java
index 7d341d8..c64f742 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java
@@ -52,6 +52,7 @@
 import java.util.Collections;
 import java.util.List;
 
+import org.spearce.jgit.diff.EditList;
 import org.spearce.jgit.lib.AbbreviatedObjectId;
 import org.spearce.jgit.lib.Constants;
 import org.spearce.jgit.lib.FileMode;
@@ -423,6 +424,14 @@ public BinaryHunk getReverseBinaryHunk() {
 		return reverseBinaryHunk;
 	}
 
+	/** @return a list describing the content edits performed on this file. */
+	public EditList toEditList() {
+		final EditList r = new EditList();
+		for (final HunkHeader hunk : hunks)
+			r.addAll(hunk.toEditList());
+		return r;
+	}
+
 	/**
 	 * Parse a "diff --git" or "diff --cc" line.
 	 *
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 e9c55e3..af128ab 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java
@@ -44,6 +44,8 @@
 import java.io.IOException;
 import java.io.OutputStream;
 
+import org.spearce.jgit.diff.Edit;
+import org.spearce.jgit.diff.EditList;
 import org.spearce.jgit.lib.AbbreviatedObjectId;
 import org.spearce.jgit.util.MutableInteger;
 
@@ -161,6 +163,52 @@ public int getLinesContext() {
 		return nContext;
 	}
 
+	/** @return a list describing the content edits performed within the hunk. */
+	public EditList toEditList() {
+		final EditList r = new EditList();
+		final byte[] buf = file.buf;
+		int c = nextLF(buf, startOffset);
+		int oLine = old.startLine;
+		int nLine = newStartLine;
+		Edit in = null;
+
+		SCAN: for (; c < endOffset; c = nextLF(buf, c)) {
+			switch (buf[c]) {
+			case ' ':
+			case '\n':
+				in = null;
+				oLine++;
+				nLine++;
+				continue;
+
+			case '-':
+				if (in == null) {
+					in = new Edit(oLine - 1, nLine - 1);
+					r.add(in);
+				}
+				oLine++;
+				in.extendA();
+				continue;
+
+			case '+':
+				if (in == null) {
+					in = new Edit(oLine - 1, nLine - 1);
+					r.add(in);
+				}
+				nLine++;
+				in.extendB();
+				continue;
+
+			case '\\': // Matches "\ No newline at end of file"
+				continue;
+
+			default:
+				break SCAN;
+			}
+		}
+		return r;
+	}
+
 	void parseHeader() {
 		// Parse "@@ -236,9 +236,9 @@ protected boolean"
 		//
-- 
1.6.3.rc3.212.g8c698

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [JGIT PATCH 6/6] Add diff.DiffFormatter to create Git style unified patch scripts
  2009-05-02  2:08         ` [JGIT PATCH 5/6] Teach FileHeader, HunkHeader how to create an EditList Shawn O. Pearce
@ 2009-05-02  2:08           ` Shawn O. Pearce
  2009-05-03  0:03             ` [JGIT PATCH v2 " Shawn O. Pearce
  0 siblings, 1 reply; 15+ messages in thread
From: Shawn O. Pearce @ 2009-05-02  2:08 UTC (permalink / raw)
  To: Robin Rosenberg, Johannes Schindelin; +Cc: git, Johannes E. Schindelin

From: Johannes E. Schindelin <johannes.schindelin@gmx.de>

Currently the formatter can only copy an existing FileHeader, one
that has been parsed from an existing patch script.  This makes
it only useful for increasing or decreasing the number of lines
of context, assuming we already have both the old and new contet
images to match.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../src/org/spearce/jgit/diff/DiffFormatter.java   |  187 ++++++++++++++++++++
 1 files changed, 187 insertions(+), 0 deletions(-)
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/diff/DiffFormatter.java

diff --git a/org.spearce.jgit/src/org/spearce/jgit/diff/DiffFormatter.java b/org.spearce.jgit/src/org/spearce/jgit/diff/DiffFormatter.java
new file mode 100644
index 0000000..aa0f4a6
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/diff/DiffFormatter.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2008, Johannes E. Schindelin <johannes.schindelin@gmx.de>
+ * Copyright (C) 2009, 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.diff;
+
+import static org.spearce.jgit.lib.Constants.encodeASCII;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+
+import org.spearce.jgit.patch.FileHeader;
+
+/**
+ * Format an {@link EditList} as a Git style unified patch script.
+ */
+public class DiffFormatter {
+	private int context;
+
+	/** Create a new formatter with a default level of context. */
+	public DiffFormatter() {
+		setContext(3);
+	}
+
+	/**
+	 * Change the number of lines of context to display.
+	 * 
+	 * @param lineCount
+	 *            number of lines of context to see before the first
+	 *            modification and after the last modification within a hunk of
+	 *            the modified file.
+	 */
+	public void setContext(final int lineCount) {
+		if (lineCount < 0)
+			throw new IllegalArgumentException("context must be >= 0");
+		context = lineCount;
+	}
+
+	/**
+	 * Format a patch script, reusing a previously parsed FileHeader.
+	 * <p>
+	 * This formatter is primarily useful for editing an existing patch script
+	 * to increase or reduce the number of lines of context within the script.
+	 * All header lines are reused as-is from the supplied FileHeader.
+	 * 
+	 * @param out
+	 *            stream to write the patch script out to.
+	 * @param head
+	 *            existing file header containing the header lines to copy.
+	 * @param a
+	 *            text source for the pre-image version of the content. This
+	 *            must match the content of {@link FileHeader#getOldId()}.
+	 * @param b
+	 *            text source for the post-image version of the content. This
+	 *            must match the content of {@link FileHeader#getNewId()}.
+	 * @throws IOException
+	 *             writing to the supplied stream failed.
+	 */
+	public void format(final OutputStream out, final FileHeader head,
+			final RawText a, final RawText b)
+			throws IOException {
+		// Reuse the existing FileHeader as-is by blindly copying its
+		// header lines, but avoiding its hunks. Instead we recreate
+		// the hunks from the text instances we have been supplied.
+		//
+		final int start = head.getStartOffset();
+		int end = head.getEndOffset();
+		if (!head.getHunks().isEmpty())
+			end = head.getHunks().get(0).getStartOffset();
+		out.write(head.getBuffer(), start, end - start);
+
+		formatEdits(out, a, b, head.toEditList());
+	}
+
+	private void formatEdits(final OutputStream out, final RawText a,
+			final RawText b, final EditList edits) throws IOException {
+		for (int curIdx = 0; curIdx < edits.size();) {
+			Edit curEdit = edits.get(curIdx);
+			final int endIdx = findCombinedEnd(edits, curIdx);
+			final Edit endEdit = edits.get(endIdx);
+
+			int aCur = Math.max(0, curEdit.getBeginA() - context);
+			int bCur = Math.max(0, curEdit.getBeginB() - context);
+			final int aEnd = Math.min(a.size(), endEdit.getEndA() + context);
+			final int bEnd = Math.min(b.size(), endEdit.getEndB() + context);
+
+			out.write('@');
+			out.write('@');
+			writeLineGroup(out, '-', aCur + 1, aEnd - aCur);
+			writeLineGroup(out, '+', bCur + 1, bEnd - bCur);
+			out.write(' ');
+			out.write('@');
+			out.write('@');
+			out.write('\n');
+
+			while (aCur < aEnd || bCur < bEnd) {
+				if (aCur < curEdit.getBeginA() || endIdx + 1 < curIdx) {
+					out.write(' ');
+					a.writeLine(out, aCur);
+					out.write('\n');
+					aCur++;
+					bCur++;
+
+				} else if (aCur < curEdit.getEndA()) {
+					out.write('-');
+					a.writeLine(out, aCur);
+					out.write('\n');
+					aCur++;
+
+				} else if (bCur < curEdit.getEndB()) {
+					out.write('+');
+					b.writeLine(out, bCur);
+					out.write('\n');
+					bCur++;
+				}
+
+				if (!in(curEdit, aCur, bCur) && ++curIdx < edits.size())
+					curEdit = edits.get(curIdx);
+			}
+		}
+	}
+
+	private static void writeLineGroup(final OutputStream out,
+			final char prefix, final int begin, final int end)
+			throws IOException {
+		out.write(' ');
+		out.write(prefix);
+		out.write(encodeASCII(begin));
+		out.write(',');
+		out.write(encodeASCII(end));
+	}
+
+	private int findCombinedEnd(final List<Edit> edits, final int i) {
+		int end = i + 1;
+		while (end < edits.size()
+				&& (combineA(edits, end) || combineB(edits, end)))
+			end++;
+		return end - 1;
+	}
+
+	private boolean combineA(final List<Edit> e, final int i) {
+		return e.get(i).getBeginA() - e.get(i - 1).getEndA() <= 2 * context;
+	}
+
+	private boolean combineB(final List<Edit> e, final int i) {
+		return e.get(i).getBeginB() - e.get(i - 1).getEndB() <= 2 * context;
+	}
+
+	private static boolean in(final Edit edit, final int a, final int b) {
+		return a < edit.getEndA() && b < edit.getEndB();
+	}
+}
-- 
1.6.3.rc3.212.g8c698

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [JGIT PATCH v2 6/6] Add diff.DiffFormatter to create Git style unified patch scripts
  2009-05-02  2:08           ` [JGIT PATCH 6/6] Add diff.DiffFormatter to create Git style unified patch scripts Shawn O. Pearce
@ 2009-05-03  0:03             ` Shawn O. Pearce
  2009-05-03  0:29               ` [JGIT PATCH v3 " Shawn O. Pearce
  0 siblings, 1 reply; 15+ messages in thread
From: Shawn O. Pearce @ 2009-05-03  0:03 UTC (permalink / raw)
  To: Robin Rosenberg, Johannes Schindelin; +Cc: git

From: Johannes E. Schindelin <johannes.schindelin@gmx.de>

Currently the formatter can only copy an existing FileHeader, one
that has been parsed from an existing patch script.  This makes
it only useful for increasing or decreasing the number of lines
of context, assuming we already have both the old and new content
images to match.

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

 Replacement fixes a lot of bugs, and includes a bunch of test
 cases that helped me find and fix those bugs in the first place. :-)

 There's a quirk about setContext(0) differing in JGit and CGit,
 see the thread I just posted a short while ago.

 .../tst-rsrc/org/spearce/jgit/diff/E.patch         |    6 +
 .../tst-rsrc/org/spearce/jgit/diff/E_PostImage     |    1 +
 .../tst-rsrc/org/spearce/jgit/diff/X.patch         |   24 ++
 .../tst-rsrc/org/spearce/jgit/diff/X_PostImage     |   28 +++
 .../tst-rsrc/org/spearce/jgit/diff/X_PreImage      |   25 +++
 .../tst-rsrc/org/spearce/jgit/diff/Y.patch         |    8 +
 .../tst-rsrc/org/spearce/jgit/diff/Y_PostImage     |    1 +
 .../tst-rsrc/org/spearce/jgit/diff/Y_PreImage      |    1 +
 .../tst-rsrc/org/spearce/jgit/diff/Z.patch         |    8 +
 .../tst-rsrc/org/spearce/jgit/diff/Z_PostImage     |    1 +
 .../tst-rsrc/org/spearce/jgit/diff/Z_PreImage      |    1 +
 .../org/spearce/jgit/diff/testContext1.out         |   24 ++
 .../org/spearce/jgit/diff/testContext10.out        |   37 ++++
 .../org/spearce/jgit/diff/testContext100.out       |   37 ++++
 .../org/spearce/jgit/diff/testContext3.out         |   30 +++
 .../org/spearce/jgit/diff/testContext5.out         |   34 +++
 .../spearce/jgit/diff/DiffFormatterReflowTest.java |  170 +++++++++++++++
 .../src/org/spearce/jgit/diff/DiffFormatter.java   |  220 ++++++++++++++++++++
 .../src/org/spearce/jgit/diff/RawText.java         |   12 +
 19 files changed, 668 insertions(+), 0 deletions(-)
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/E.patch
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/E_PostImage
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/E_PreImage
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/X.patch
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/X_PostImage
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/X_PreImage
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Y.patch
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Y_PostImage
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Y_PreImage
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Z.patch
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Z_PostImage
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Z_PreImage
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext1.out
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext10.out
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext100.out
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext3.out
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext5.out
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/diff/DiffFormatterReflowTest.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/diff/DiffFormatter.java

diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/E.patch b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/E.patch
new file mode 100644
index 0000000..9b8fa98
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/E.patch
@@ -0,0 +1,6 @@
+diff --git a/E b/E
+index e69de29..7898192 100644
+--- a/E
++++ b/E
+@@ -0,0 +1 @@
++a
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/E_PostImage b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/E_PostImage
new file mode 100644
index 0000000..7898192
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/E_PostImage
@@ -0,0 +1 @@
+a
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/E_PreImage b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/E_PreImage
new file mode 100644
index 0000000..e69de29
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/X.patch b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/X.patch
new file mode 100644
index 0000000..e5363eb
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/X.patch
@@ -0,0 +1,24 @@
+diff --git a/X b/X
+index a3648a1..2d44096 100644
+--- a/X
++++ b/X
+@@ -2,2 +2,3 @@ a
+ b
++c
+ d
+@@ -16,4 +17,2 @@ p
+ q
+-r
+-s
+ t
+@@ -22,4 +21,8 @@ v
+ w
+-x
+-y
++0
++1
++2
++3
++4
++5
+ z
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/X_PostImage b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/X_PostImage
new file mode 100644
index 0000000..2d44096
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/X_PostImage
@@ -0,0 +1,28 @@
+a
+b
+c
+d
+e
+f
+g
+h
+i
+j
+k
+l
+m
+n
+o
+p
+q
+t
+u
+v
+w
+0
+1
+2
+3
+4
+5
+z
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/X_PreImage b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/X_PreImage
new file mode 100644
index 0000000..a3648a1
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/X_PreImage
@@ -0,0 +1,25 @@
+a
+b
+d
+e
+f
+g
+h
+i
+j
+k
+l
+m
+n
+o
+p
+q
+r
+s
+t
+u
+v
+w
+x
+y
+z
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Y.patch b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Y.patch
new file mode 100644
index 0000000..a2c9a0b
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Y.patch
@@ -0,0 +1,8 @@
+diff --git a/Y b/Y
+index 2e65efe..7898192 100644
+--- a/Y
++++ b/Y
+@@ -1 +1 @@
+-a
+\ No newline at end of file
++a
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Y_PostImage b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Y_PostImage
new file mode 100644
index 0000000..7898192
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Y_PostImage
@@ -0,0 +1 @@
+a
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Y_PreImage b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Y_PreImage
new file mode 100644
index 0000000..2e65efe
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Y_PreImage
@@ -0,0 +1 @@
+a
\ No newline at end of file
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Z.patch b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Z.patch
new file mode 100644
index 0000000..35a06d6
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Z.patch
@@ -0,0 +1,8 @@
+diff --git a/Z b/Z
+index 7898192..2e65efe 100644
+--- a/Z
++++ b/Z
+@@ -1 +1 @@
+-a
++a
+\ No newline at end of file
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Z_PostImage b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Z_PostImage
new file mode 100644
index 0000000..2e65efe
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Z_PostImage
@@ -0,0 +1 @@
+a
\ No newline at end of file
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Z_PreImage b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Z_PreImage
new file mode 100644
index 0000000..7898192
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Z_PreImage
@@ -0,0 +1 @@
+a
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext1.out b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext1.out
new file mode 100644
index 0000000..d0d847d
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext1.out
@@ -0,0 +1,24 @@
+diff --git a/X b/X
+index a3648a1..2d44096 100644
+--- a/X
++++ b/X
+@@ -2,2 +2,3 @@
+ b
++c
+ d
+@@ -16,4 +17,2 @@
+ q
+-r
+-s
+ t
+@@ -22,4 +21,8 @@
+ w
+-x
+-y
++0
++1
++2
++3
++4
++5
+ z
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext10.out b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext10.out
new file mode 100644
index 0000000..1d4f242
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext10.out
@@ -0,0 +1,37 @@
+diff --git a/X b/X
+index a3648a1..2d44096 100644
+--- a/X
++++ b/X
+@@ -1,25 +1,28 @@
+ a
+ b
++c
+ d
+ e
+ f
+ g
+ h
+ i
+ j
+ k
+ l
+ m
+ n
+ o
+ p
+ q
+-r
+-s
+ t
+ u
+ v
+ w
+-x
+-y
++0
++1
++2
++3
++4
++5
+ z
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext100.out b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext100.out
new file mode 100644
index 0000000..1d4f242
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext100.out
@@ -0,0 +1,37 @@
+diff --git a/X b/X
+index a3648a1..2d44096 100644
+--- a/X
++++ b/X
+@@ -1,25 +1,28 @@
+ a
+ b
++c
+ d
+ e
+ f
+ g
+ h
+ i
+ j
+ k
+ l
+ m
+ n
+ o
+ p
+ q
+-r
+-s
+ t
+ u
+ v
+ w
+-x
+-y
++0
++1
++2
++3
++4
++5
+ z
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext3.out b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext3.out
new file mode 100644
index 0000000..2564016
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext3.out
@@ -0,0 +1,30 @@
+diff --git a/X b/X
+index a3648a1..2d44096 100644
+--- a/X
++++ b/X
+@@ -1,5 +1,6 @@
+ a
+ b
++c
+ d
+ e
+ f
+@@ -14,12 +15,14 @@
+ o
+ p
+ q
+-r
+-s
+ t
+ u
+ v
+ w
+-x
+-y
++0
++1
++2
++3
++4
++5
+ z
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext5.out b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext5.out
new file mode 100644
index 0000000..3073c5f
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext5.out
@@ -0,0 +1,34 @@
+diff --git a/X b/X
+index a3648a1..2d44096 100644
+--- a/X
++++ b/X
+@@ -1,7 +1,8 @@
+ a
+ b
++c
+ d
+ e
+ f
+ g
+ h
+@@ -12,14 +13,16 @@
+ m
+ n
+ o
+ p
+ q
+-r
+-s
+ t
+ u
+ v
+ w
+-x
+-y
++0
++1
++2
++3
++4
++5
+ z
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/diff/DiffFormatterReflowTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/diff/DiffFormatterReflowTest.java
new file mode 100644
index 0000000..f47282c
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/diff/DiffFormatterReflowTest.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2009, 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.diff;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import junit.framework.TestCase;
+
+import org.spearce.jgit.patch.FileHeader;
+import org.spearce.jgit.patch.Patch;
+import org.spearce.jgit.util.RawParseUtils;
+
+public class DiffFormatterReflowTest extends TestCase {
+	private RawText a;
+
+	private RawText b;
+
+	private FileHeader file;
+
+	private ByteArrayOutputStream out;
+
+	private DiffFormatter fmt;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		out = new ByteArrayOutputStream();
+		fmt = new DiffFormatter();
+	}
+
+	public void testNegativeContextFails() throws IOException {
+		init("X");
+		try {
+			fmt.setContext(-1);
+			fail("accepted negative context");
+		} catch (IllegalArgumentException e) {
+			// pass
+		}
+	}
+
+	public void testContext1() throws IOException {
+		init("X");
+		fmt.setContext(1);
+		assertFormatted();
+	}
+
+	public void testContext3() throws IOException {
+		init("X");
+		fmt.setContext(3);
+		assertFormatted();
+	}
+
+	public void testContext5() throws IOException {
+		init("X");
+		fmt.setContext(5);
+		assertFormatted();
+	}
+
+	public void testContext10() throws IOException {
+		init("X");
+		fmt.setContext(10);
+		assertFormatted();
+	}
+
+	public void testContext100() throws IOException {
+		init("X");
+		fmt.setContext(100);
+		assertFormatted();
+	}
+
+	public void testEmpty1() throws IOException {
+		init("E");
+		assertFormatted("E.patch");
+	}
+
+	public void testNoNewLine1() throws IOException {
+		init("Y");
+		assertFormatted("Y.patch");
+	}
+
+	public void testNoNewLine2() throws IOException {
+		init("Z");
+		assertFormatted("Z.patch");
+	}
+
+	private void init(final String name) throws IOException {
+		a = new RawText(readFile(name + "_PreImage"));
+		b = new RawText(readFile(name + "_PostImage"));
+		file = parseTestPatchFile(name + ".patch").getFiles().get(0);
+	}
+
+	private void assertFormatted() throws IOException {
+		assertFormatted(getName() + ".out");
+	}
+
+	private void assertFormatted(final String name) throws IOException {
+		fmt.format(out, file, a, b);
+		final String exp = RawParseUtils.decode(readFile(name));
+		assertEquals(exp, RawParseUtils.decode(out.toByteArray()));
+	}
+
+	private byte[] readFile(final String patchFile) throws IOException {
+		final InputStream in = getClass().getResourceAsStream(patchFile);
+		if (in == null) {
+			fail("No " + patchFile + " test vector");
+			return null; // Never happens
+		}
+		try {
+			final byte[] buf = new byte[1024];
+			final ByteArrayOutputStream temp = new ByteArrayOutputStream();
+			int n;
+			while ((n = in.read(buf)) > 0)
+				temp.write(buf, 0, n);
+			return temp.toByteArray();
+		} finally {
+			in.close();
+		}
+	}
+
+	private Patch parseTestPatchFile(final String patchFile) throws IOException {
+		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/src/org/spearce/jgit/diff/DiffFormatter.java b/org.spearce.jgit/src/org/spearce/jgit/diff/DiffFormatter.java
new file mode 100644
index 0000000..97db9a2
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/diff/DiffFormatter.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2008, Johannes E. Schindelin <johannes.schindelin@gmx.de>
+ * Copyright (C) 2009, 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.diff;
+
+import static org.spearce.jgit.lib.Constants.encodeASCII;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+
+import org.spearce.jgit.patch.FileHeader;
+
+/**
+ * Format an {@link EditList} as a Git style unified patch script.
+ */
+public class DiffFormatter {
+	private static final byte[] noNewLine = encodeASCII("\\ No newline at end of file\n");
+
+	private int context;
+
+	/** Create a new formatter with a default level of context. */
+	public DiffFormatter() {
+		setContext(3);
+	}
+
+	/**
+	 * Change the number of lines of context to display.
+	 * 
+	 * @param lineCount
+	 *            number of lines of context to see before the first
+	 *            modification and after the last modification within a hunk of
+	 *            the modified file.
+	 */
+	public void setContext(final int lineCount) {
+		if (lineCount < 0)
+			throw new IllegalArgumentException("context must be >= 0");
+		context = lineCount;
+	}
+
+	/**
+	 * Format a patch script, reusing a previously parsed FileHeader.
+	 * <p>
+	 * This formatter is primarily useful for editing an existing patch script
+	 * to increase or reduce the number of lines of context within the script.
+	 * All header lines are reused as-is from the supplied FileHeader.
+	 * 
+	 * @param out
+	 *            stream to write the patch script out to.
+	 * @param head
+	 *            existing file header containing the header lines to copy.
+	 * @param a
+	 *            text source for the pre-image version of the content. This
+	 *            must match the content of {@link FileHeader#getOldId()}.
+	 * @param b
+	 *            text source for the post-image version of the content. This
+	 *            must match the content of {@link FileHeader#getNewId()}.
+	 * @throws IOException
+	 *             writing to the supplied stream failed.
+	 */
+	public void format(final OutputStream out, final FileHeader head,
+			final RawText a, final RawText b) throws IOException {
+		// Reuse the existing FileHeader as-is by blindly copying its
+		// header lines, but avoiding its hunks. Instead we recreate
+		// the hunks from the text instances we have been supplied.
+		//
+		final int start = head.getStartOffset();
+		int end = head.getEndOffset();
+		if (!head.getHunks().isEmpty())
+			end = head.getHunks().get(0).getStartOffset();
+		out.write(head.getBuffer(), start, end - start);
+
+		formatEdits(out, a, b, head.toEditList());
+	}
+
+	private void formatEdits(final OutputStream out, final RawText a,
+			final RawText b, final EditList edits) throws IOException {
+		for (int curIdx = 0; curIdx < edits.size();) {
+			Edit curEdit = edits.get(curIdx);
+			final int endIdx = findCombinedEnd(edits, curIdx);
+			final Edit endEdit = edits.get(endIdx);
+
+			int aCur = Math.max(0, curEdit.getBeginA() - context);
+			int bCur = Math.max(0, curEdit.getBeginB() - context);
+			final int aEnd = Math.min(a.size(), endEdit.getEndA() + context);
+			final int bEnd = Math.min(b.size(), endEdit.getEndB() + context);
+
+			writeHunkHeader(out, aCur, aEnd, bCur, bEnd, curIdx == 0);
+
+			while (aCur < aEnd || bCur < bEnd) {
+				if (aCur < curEdit.getBeginA() || endIdx + 1 < curIdx) {
+					writeLine(out, ' ', a, aCur);
+					aCur++;
+					bCur++;
+
+				} else if (aCur < curEdit.getEndA()) {
+					writeLine(out, '-', a, aCur++);
+
+				} else if (bCur < curEdit.getEndB()) {
+					writeLine(out, '+', b, bCur++);
+				}
+
+				if (end(curEdit, aCur, bCur) && ++curIdx < edits.size())
+					curEdit = edits.get(curIdx);
+			}
+		}
+	}
+
+	private void writeHunkHeader(final OutputStream out, final int aCur,
+			final int aEnd, final int bCur, final int bEnd,
+			final boolean firstHunk) throws IOException {
+		out.write('@');
+		out.write('@');
+		if (firstHunk) {
+			writeFirstLineGroup(out, '-', aCur + 1, aEnd - aCur);
+			writeFirstLineGroup(out, '+', bCur + 1, bEnd - bCur);
+		} else {
+			writeAnyLineGroup(out, '-', aCur + 1, aEnd - aCur);
+			writeAnyLineGroup(out, '+', bCur + 1, bEnd - bCur);
+		}
+		out.write(' ');
+		out.write('@');
+		out.write('@');
+		out.write('\n');
+	}
+
+	private static void writeFirstLineGroup(final OutputStream out,
+			final char prefix, final int begin, final int cnt)
+			throws IOException {
+		if (begin == 1 && cnt == 0) {
+			out.write(' ');
+			out.write(prefix);
+			out.write('0');
+			out.write(',');
+			out.write('0');
+
+		} else if (cnt == 1) {
+			out.write(' ');
+			out.write(prefix);
+			out.write(encodeASCII(begin));
+
+		} else {
+			writeAnyLineGroup(out, prefix, begin, cnt);
+		}
+	}
+
+	private static void writeAnyLineGroup(final OutputStream out,
+			final char prefix, final int begin, final int cnt)
+			throws IOException {
+		out.write(' ');
+		out.write(prefix);
+		out.write(encodeASCII(begin));
+		out.write(',');
+		out.write(encodeASCII(cnt));
+	}
+
+	private static void writeLine(final OutputStream out, final char prefix,
+			final RawText text, final int cur) throws IOException {
+		out.write(prefix);
+		text.writeLine(out, cur);
+		out.write('\n');
+		if (cur + 1 == text.size() && text.isMissingNewlineAtEnd())
+			out.write(noNewLine);
+	}
+
+	private int findCombinedEnd(final List<Edit> edits, final int i) {
+		int end = i + 1;
+		while (end < edits.size()
+				&& (combineA(edits, end) || combineB(edits, end)))
+			end++;
+		return end - 1;
+	}
+
+	private boolean combineA(final List<Edit> e, final int i) {
+		return e.get(i).getBeginA() - e.get(i - 1).getEndA() <= 2 * context;
+	}
+
+	private boolean combineB(final List<Edit> e, final int i) {
+		return e.get(i).getBeginB() - e.get(i - 1).getEndB() <= 2 * context;
+	}
+
+	private static boolean end(final Edit edit, final int a, final int b) {
+		return edit.getEndA() <= a && edit.getEndB() <= b;
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/diff/RawText.java b/org.spearce.jgit/src/org/spearce/jgit/diff/RawText.java
index 1d10082..e10ee46 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/diff/RawText.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/diff/RawText.java
@@ -140,6 +140,18 @@ public void writeLine(final OutputStream out, final int i)
 		out.write(content, start, end - start);
 	}
 
+	/**
+	 * Determine if the file ends with a LF ('\n').
+	 * 
+	 * @return true if the last line has an LF; false otherwise.
+	 */
+	public boolean isMissingNewlineAtEnd() {
+		final int end = lines.get(lines.size() - 1);
+		if (end == 0)
+			return true;
+		return content[end - 1] != '\n';
+	}
+
 	private IntList computeHashes() {
 		final IntList r = new IntList(lines.size());
 		r.add(0);
-- 
1.6.3.rc4.190.g4648

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [JGIT PATCH v3 6/6] Add diff.DiffFormatter to create Git style unified patch scripts
  2009-05-03  0:03             ` [JGIT PATCH v2 " Shawn O. Pearce
@ 2009-05-03  0:29               ` Shawn O. Pearce
  0 siblings, 0 replies; 15+ messages in thread
From: Shawn O. Pearce @ 2009-05-03  0:29 UTC (permalink / raw)
  To: Robin Rosenberg, Johannes Schindelin; +Cc: git

From: Johannes E. Schindelin <johannes.schindelin@gmx.de>

Currently the formatter can only copy an existing FileHeader, one
that has been parsed from an existing patch script.  This makes
it only useful for increasing or decreasing the number of lines
of context, assuming we already have both the old and new content
images to match.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
  "Shawn O. Pearce" <spearce@spearce.org> wrote:
  >  There's a quirk about setContext(0) differing in JGit and CGit,
  >  see the thread I just posted a short while ago.

  v3 fixes this issue, thanks to Junio's comments.

  This includes 7/6, which now passes, and makes 8/6 obsolete.

 .../tst-rsrc/org/spearce/jgit/diff/E.patch         |    6 +
 .../tst-rsrc/org/spearce/jgit/diff/E_PostImage     |    1 +
 .../tst-rsrc/org/spearce/jgit/diff/X.patch         |   24 +++
 .../tst-rsrc/org/spearce/jgit/diff/X_PostImage     |   28 +++
 .../tst-rsrc/org/spearce/jgit/diff/X_PreImage      |   25 +++
 .../tst-rsrc/org/spearce/jgit/diff/Y.patch         |    8 +
 .../tst-rsrc/org/spearce/jgit/diff/Y_PostImage     |    1 +
 .../tst-rsrc/org/spearce/jgit/diff/Y_PreImage      |    1 +
 .../tst-rsrc/org/spearce/jgit/diff/Z.patch         |    8 +
 .../tst-rsrc/org/spearce/jgit/diff/Z_PostImage     |    1 +
 .../tst-rsrc/org/spearce/jgit/diff/Z_PreImage      |    1 +
 .../org/spearce/jgit/diff/testContext0.out         |   18 ++
 .../org/spearce/jgit/diff/testContext1.out         |   24 +++
 .../org/spearce/jgit/diff/testContext10.out        |   37 ++++
 .../org/spearce/jgit/diff/testContext100.out       |   37 ++++
 .../org/spearce/jgit/diff/testContext3.out         |   30 +++
 .../org/spearce/jgit/diff/testContext5.out         |   34 +++
 .../spearce/jgit/diff/DiffFormatterReflowTest.java |  176 ++++++++++++++++
 .../src/org/spearce/jgit/diff/DiffFormatter.java   |  214 ++++++++++++++++++++
 .../src/org/spearce/jgit/diff/RawText.java         |   12 +
 20 files changed, 686 insertions(+), 0 deletions(-)
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/E.patch
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/E_PostImage
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/E_PreImage
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/X.patch
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/X_PostImage
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/X_PreImage
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Y.patch
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Y_PostImage
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Y_PreImage
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Z.patch
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Z_PostImage
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Z_PreImage
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext0.out
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext1.out
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext10.out
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext100.out
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext3.out
 create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext5.out
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/diff/DiffFormatterReflowTest.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/diff/DiffFormatter.java

diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/E.patch b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/E.patch
new file mode 100644
index 0000000..9b8fa98
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/E.patch
@@ -0,0 +1,6 @@
+diff --git a/E b/E
+index e69de29..7898192 100644
+--- a/E
++++ b/E
+@@ -0,0 +1 @@
++a
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/E_PostImage b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/E_PostImage
new file mode 100644
index 0000000..7898192
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/E_PostImage
@@ -0,0 +1 @@
+a
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/E_PreImage b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/E_PreImage
new file mode 100644
index 0000000..e69de29
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/X.patch b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/X.patch
new file mode 100644
index 0000000..e5363eb
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/X.patch
@@ -0,0 +1,24 @@
+diff --git a/X b/X
+index a3648a1..2d44096 100644
+--- a/X
++++ b/X
+@@ -2,2 +2,3 @@ a
+ b
++c
+ d
+@@ -16,4 +17,2 @@ p
+ q
+-r
+-s
+ t
+@@ -22,4 +21,8 @@ v
+ w
+-x
+-y
++0
++1
++2
++3
++4
++5
+ z
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/X_PostImage b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/X_PostImage
new file mode 100644
index 0000000..2d44096
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/X_PostImage
@@ -0,0 +1,28 @@
+a
+b
+c
+d
+e
+f
+g
+h
+i
+j
+k
+l
+m
+n
+o
+p
+q
+t
+u
+v
+w
+0
+1
+2
+3
+4
+5
+z
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/X_PreImage b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/X_PreImage
new file mode 100644
index 0000000..a3648a1
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/X_PreImage
@@ -0,0 +1,25 @@
+a
+b
+d
+e
+f
+g
+h
+i
+j
+k
+l
+m
+n
+o
+p
+q
+r
+s
+t
+u
+v
+w
+x
+y
+z
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Y.patch b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Y.patch
new file mode 100644
index 0000000..a2c9a0b
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Y.patch
@@ -0,0 +1,8 @@
+diff --git a/Y b/Y
+index 2e65efe..7898192 100644
+--- a/Y
++++ b/Y
+@@ -1 +1 @@
+-a
+\ No newline at end of file
++a
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Y_PostImage b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Y_PostImage
new file mode 100644
index 0000000..7898192
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Y_PostImage
@@ -0,0 +1 @@
+a
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Y_PreImage b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Y_PreImage
new file mode 100644
index 0000000..2e65efe
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Y_PreImage
@@ -0,0 +1 @@
+a
\ No newline at end of file
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Z.patch b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Z.patch
new file mode 100644
index 0000000..35a06d6
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Z.patch
@@ -0,0 +1,8 @@
+diff --git a/Z b/Z
+index 7898192..2e65efe 100644
+--- a/Z
++++ b/Z
+@@ -1 +1 @@
+-a
++a
+\ No newline at end of file
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Z_PostImage b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Z_PostImage
new file mode 100644
index 0000000..2e65efe
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Z_PostImage
@@ -0,0 +1 @@
+a
\ No newline at end of file
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Z_PreImage b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Z_PreImage
new file mode 100644
index 0000000..7898192
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/Z_PreImage
@@ -0,0 +1 @@
+a
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext0.out b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext0.out
new file mode 100644
index 0000000..d36e3fa
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext0.out
@@ -0,0 +1,18 @@
+diff --git a/X b/X
+index a3648a1..2d44096 100644
+--- a/X
++++ b/X
+@@ -2,0 +3 @@
++c
+@@ -17,2 +17,0 @@
+-r
+-s
+@@ -23,2 +22,6 @@
+-x
+-y
++0
++1
++2
++3
++4
++5
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext1.out b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext1.out
new file mode 100644
index 0000000..d0d847d
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext1.out
@@ -0,0 +1,24 @@
+diff --git a/X b/X
+index a3648a1..2d44096 100644
+--- a/X
++++ b/X
+@@ -2,2 +2,3 @@
+ b
++c
+ d
+@@ -16,4 +17,2 @@
+ q
+-r
+-s
+ t
+@@ -22,4 +21,8 @@
+ w
+-x
+-y
++0
++1
++2
++3
++4
++5
+ z
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext10.out b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext10.out
new file mode 100644
index 0000000..1d4f242
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext10.out
@@ -0,0 +1,37 @@
+diff --git a/X b/X
+index a3648a1..2d44096 100644
+--- a/X
++++ b/X
+@@ -1,25 +1,28 @@
+ a
+ b
++c
+ d
+ e
+ f
+ g
+ h
+ i
+ j
+ k
+ l
+ m
+ n
+ o
+ p
+ q
+-r
+-s
+ t
+ u
+ v
+ w
+-x
+-y
++0
++1
++2
++3
++4
++5
+ z
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext100.out b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext100.out
new file mode 100644
index 0000000..1d4f242
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext100.out
@@ -0,0 +1,37 @@
+diff --git a/X b/X
+index a3648a1..2d44096 100644
+--- a/X
++++ b/X
+@@ -1,25 +1,28 @@
+ a
+ b
++c
+ d
+ e
+ f
+ g
+ h
+ i
+ j
+ k
+ l
+ m
+ n
+ o
+ p
+ q
+-r
+-s
+ t
+ u
+ v
+ w
+-x
+-y
++0
++1
++2
++3
++4
++5
+ z
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext3.out b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext3.out
new file mode 100644
index 0000000..2564016
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext3.out
@@ -0,0 +1,30 @@
+diff --git a/X b/X
+index a3648a1..2d44096 100644
+--- a/X
++++ b/X
+@@ -1,5 +1,6 @@
+ a
+ b
++c
+ d
+ e
+ f
+@@ -14,12 +15,14 @@
+ o
+ p
+ q
+-r
+-s
+ t
+ u
+ v
+ w
+-x
+-y
++0
++1
++2
++3
++4
++5
+ z
diff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext5.out b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext5.out
new file mode 100644
index 0000000..3073c5f
--- /dev/null
+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/diff/testContext5.out
@@ -0,0 +1,34 @@
+diff --git a/X b/X
+index a3648a1..2d44096 100644
+--- a/X
++++ b/X
+@@ -1,7 +1,8 @@
+ a
+ b
++c
+ d
+ e
+ f
+ g
+ h
+@@ -12,14 +13,16 @@
+ m
+ n
+ o
+ p
+ q
+-r
+-s
+ t
+ u
+ v
+ w
+-x
+-y
++0
++1
++2
++3
++4
++5
+ z
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/diff/DiffFormatterReflowTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/diff/DiffFormatterReflowTest.java
new file mode 100644
index 0000000..5d2ee40
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/diff/DiffFormatterReflowTest.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2009, 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.diff;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import junit.framework.TestCase;
+
+import org.spearce.jgit.patch.FileHeader;
+import org.spearce.jgit.patch.Patch;
+import org.spearce.jgit.util.RawParseUtils;
+
+public class DiffFormatterReflowTest extends TestCase {
+	private RawText a;
+
+	private RawText b;
+
+	private FileHeader file;
+
+	private ByteArrayOutputStream out;
+
+	private DiffFormatter fmt;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		out = new ByteArrayOutputStream();
+		fmt = new DiffFormatter();
+	}
+
+	public void testNegativeContextFails() throws IOException {
+		init("X");
+		try {
+			fmt.setContext(-1);
+			fail("accepted negative context");
+		} catch (IllegalArgumentException e) {
+			// pass
+		}
+	}
+
+	public void testContext0() throws IOException {
+		init("X");
+		fmt.setContext(0);
+		assertFormatted();
+	}
+
+	public void testContext1() throws IOException {
+		init("X");
+		fmt.setContext(1);
+		assertFormatted();
+	}
+
+	public void testContext3() throws IOException {
+		init("X");
+		fmt.setContext(3);
+		assertFormatted();
+	}
+
+	public void testContext5() throws IOException {
+		init("X");
+		fmt.setContext(5);
+		assertFormatted();
+	}
+
+	public void testContext10() throws IOException {
+		init("X");
+		fmt.setContext(10);
+		assertFormatted();
+	}
+
+	public void testContext100() throws IOException {
+		init("X");
+		fmt.setContext(100);
+		assertFormatted();
+	}
+
+	public void testEmpty1() throws IOException {
+		init("E");
+		assertFormatted("E.patch");
+	}
+
+	public void testNoNewLine1() throws IOException {
+		init("Y");
+		assertFormatted("Y.patch");
+	}
+
+	public void testNoNewLine2() throws IOException {
+		init("Z");
+		assertFormatted("Z.patch");
+	}
+
+	private void init(final String name) throws IOException {
+		a = new RawText(readFile(name + "_PreImage"));
+		b = new RawText(readFile(name + "_PostImage"));
+		file = parseTestPatchFile(name + ".patch").getFiles().get(0);
+	}
+
+	private void assertFormatted() throws IOException {
+		assertFormatted(getName() + ".out");
+	}
+
+	private void assertFormatted(final String name) throws IOException {
+		fmt.format(out, file, a, b);
+		final String exp = RawParseUtils.decode(readFile(name));
+		assertEquals(exp, RawParseUtils.decode(out.toByteArray()));
+	}
+
+	private byte[] readFile(final String patchFile) throws IOException {
+		final InputStream in = getClass().getResourceAsStream(patchFile);
+		if (in == null) {
+			fail("No " + patchFile + " test vector");
+			return null; // Never happens
+		}
+		try {
+			final byte[] buf = new byte[1024];
+			final ByteArrayOutputStream temp = new ByteArrayOutputStream();
+			int n;
+			while ((n = in.read(buf)) > 0)
+				temp.write(buf, 0, n);
+			return temp.toByteArray();
+		} finally {
+			in.close();
+		}
+	}
+
+	private Patch parseTestPatchFile(final String patchFile) throws IOException {
+		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/src/org/spearce/jgit/diff/DiffFormatter.java b/org.spearce.jgit/src/org/spearce/jgit/diff/DiffFormatter.java
new file mode 100644
index 0000000..9a81901
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/diff/DiffFormatter.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2008, Johannes E. Schindelin <johannes.schindelin@gmx.de>
+ * Copyright (C) 2009, 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.diff;
+
+import static org.spearce.jgit.lib.Constants.encodeASCII;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+
+import org.spearce.jgit.patch.FileHeader;
+
+/**
+ * Format an {@link EditList} as a Git style unified patch script.
+ */
+public class DiffFormatter {
+	private static final byte[] noNewLine = encodeASCII("\\ No newline at end of file\n");
+
+	private int context;
+
+	/** Create a new formatter with a default level of context. */
+	public DiffFormatter() {
+		setContext(3);
+	}
+
+	/**
+	 * Change the number of lines of context to display.
+	 * 
+	 * @param lineCount
+	 *            number of lines of context to see before the first
+	 *            modification and after the last modification within a hunk of
+	 *            the modified file.
+	 */
+	public void setContext(final int lineCount) {
+		if (lineCount < 0)
+			throw new IllegalArgumentException("context must be >= 0");
+		context = lineCount;
+	}
+
+	/**
+	 * Format a patch script, reusing a previously parsed FileHeader.
+	 * <p>
+	 * This formatter is primarily useful for editing an existing patch script
+	 * to increase or reduce the number of lines of context within the script.
+	 * All header lines are reused as-is from the supplied FileHeader.
+	 * 
+	 * @param out
+	 *            stream to write the patch script out to.
+	 * @param head
+	 *            existing file header containing the header lines to copy.
+	 * @param a
+	 *            text source for the pre-image version of the content. This
+	 *            must match the content of {@link FileHeader#getOldId()}.
+	 * @param b
+	 *            text source for the post-image version of the content. This
+	 *            must match the content of {@link FileHeader#getNewId()}.
+	 * @throws IOException
+	 *             writing to the supplied stream failed.
+	 */
+	public void format(final OutputStream out, final FileHeader head,
+			final RawText a, final RawText b) throws IOException {
+		// Reuse the existing FileHeader as-is by blindly copying its
+		// header lines, but avoiding its hunks. Instead we recreate
+		// the hunks from the text instances we have been supplied.
+		//
+		final int start = head.getStartOffset();
+		int end = head.getEndOffset();
+		if (!head.getHunks().isEmpty())
+			end = head.getHunks().get(0).getStartOffset();
+		out.write(head.getBuffer(), start, end - start);
+
+		formatEdits(out, a, b, head.toEditList());
+	}
+
+	private void formatEdits(final OutputStream out, final RawText a,
+			final RawText b, final EditList edits) throws IOException {
+		for (int curIdx = 0; curIdx < edits.size();) {
+			Edit curEdit = edits.get(curIdx);
+			final int endIdx = findCombinedEnd(edits, curIdx);
+			final Edit endEdit = edits.get(endIdx);
+
+			int aCur = Math.max(0, curEdit.getBeginA() - context);
+			int bCur = Math.max(0, curEdit.getBeginB() - context);
+			final int aEnd = Math.min(a.size(), endEdit.getEndA() + context);
+			final int bEnd = Math.min(b.size(), endEdit.getEndB() + context);
+
+			writeHunkHeader(out, aCur, aEnd, bCur, bEnd);
+
+			while (aCur < aEnd || bCur < bEnd) {
+				if (aCur < curEdit.getBeginA() || endIdx + 1 < curIdx) {
+					writeLine(out, ' ', a, aCur);
+					aCur++;
+					bCur++;
+
+				} else if (aCur < curEdit.getEndA()) {
+					writeLine(out, '-', a, aCur++);
+
+				} else if (bCur < curEdit.getEndB()) {
+					writeLine(out, '+', b, bCur++);
+				}
+
+				if (end(curEdit, aCur, bCur) && ++curIdx < edits.size())
+					curEdit = edits.get(curIdx);
+			}
+		}
+	}
+
+	private void writeHunkHeader(final OutputStream out, int aCur, int aEnd,
+			int bCur, int bEnd) throws IOException {
+		out.write('@');
+		out.write('@');
+		writeRange(out, '-', aCur + 1, aEnd - aCur);
+		writeRange(out, '+', bCur + 1, bEnd - bCur);
+		out.write(' ');
+		out.write('@');
+		out.write('@');
+		out.write('\n');
+	}
+
+	private static void writeRange(final OutputStream out, final char prefix,
+			final int begin, final int cnt) throws IOException {
+		out.write(' ');
+		out.write(prefix);
+		switch (cnt) {
+		case 0:
+			// If the range is empty, its beginning number must be the
+			// line just before the range, or 0 if the range is at the
+			// start of the file stream. Here, begin is always 1 based,
+			// so an empty file would produce "0,0".
+			//
+			out.write(encodeASCII(begin - 1));
+			out.write(',');
+			out.write('0');
+			break;
+
+		case 1:
+			// If the range is exactly one line, produce only the number.
+			//
+			out.write(encodeASCII(begin));
+			break;
+
+		default:
+			out.write(encodeASCII(begin));
+			out.write(',');
+			out.write(encodeASCII(cnt));
+			break;
+		}
+	}
+
+	private static void writeLine(final OutputStream out, final char prefix,
+			final RawText text, final int cur) throws IOException {
+		out.write(prefix);
+		text.writeLine(out, cur);
+		out.write('\n');
+		if (cur + 1 == text.size() && text.isMissingNewlineAtEnd())
+			out.write(noNewLine);
+	}
+
+	private int findCombinedEnd(final List<Edit> edits, final int i) {
+		int end = i + 1;
+		while (end < edits.size()
+				&& (combineA(edits, end) || combineB(edits, end)))
+			end++;
+		return end - 1;
+	}
+
+	private boolean combineA(final List<Edit> e, final int i) {
+		return e.get(i).getBeginA() - e.get(i - 1).getEndA() <= 2 * context;
+	}
+
+	private boolean combineB(final List<Edit> e, final int i) {
+		return e.get(i).getBeginB() - e.get(i - 1).getEndB() <= 2 * context;
+	}
+
+	private static boolean end(final Edit edit, final int a, final int b) {
+		return edit.getEndA() <= a && edit.getEndB() <= b;
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/diff/RawText.java b/org.spearce.jgit/src/org/spearce/jgit/diff/RawText.java
index 1d10082..e10ee46 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/diff/RawText.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/diff/RawText.java
@@ -140,6 +140,18 @@ public void writeLine(final OutputStream out, final int i)
 		out.write(content, start, end - start);
 	}
 
+	/**
+	 * Determine if the file ends with a LF ('\n').
+	 * 
+	 * @return true if the last line has an LF; false otherwise.
+	 */
+	public boolean isMissingNewlineAtEnd() {
+		final int end = lines.get(lines.size() - 1);
+		if (end == 0)
+			return true;
+		return content[end - 1] != '\n';
+	}
+
 	private IntList computeHashes() {
 		final IntList r = new IntList(lines.size());
 		r.add(0);
-- 
1.6.3.rc4.190.g4648

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [JGIT PATCH 1/6] Add set to IntList
  2009-05-02  2:08 ` [JGIT PATCH 1/6] Add set to IntList Shawn O. Pearce
  2009-05-02  2:08   ` [JGIT PATCH 2/6] Add diff.Edit to describe an edit region within a file Shawn O. Pearce
@ 2009-05-03  7:07   ` Robin Rosenberg
  2009-05-04 14:22     ` Shawn O. Pearce
  1 sibling, 1 reply; 15+ messages in thread
From: Robin Rosenberg @ 2009-05-03  7:07 UTC (permalink / raw)
  To: Shawn O. Pearce; +Cc: Johannes Schindelin, git

lördag 02 maj 2009 04:08:42 skrev "Shawn O. Pearce" <spearce@spearce.org>:
> Some applications may wish to modify an int list.
> 
> Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
> ---
>  .../tst/org/spearce/jgit/util/IntListTest.java     |   21 ++++++++++++++++++++
>  .../src/org/spearce/jgit/util/IntList.java         |   17 ++++++++++++++++
>  2 files changed, 38 insertions(+), 0 deletions(-)
> 
> diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/util/IntListTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/util/IntListTest.java
> index c470d55..ce0d7af 100644
> --- a/org.spearce.jgit.test/tst/org/spearce/jgit/util/IntListTest.java
> +++ b/org.spearce.jgit.test/tst/org/spearce/jgit/util/IntListTest.java
> @@ -144,6 +144,27 @@ public void testClear() {
>  		}
>  	}
>  
> +	public void testSet() {
> +		final IntList i = new IntList();
> +		i.add(1);
> +		assertEquals(1, i.size());
> +		assertEquals(1, i.get(0));
> +		
> +		i.set(0, 5);
> +		assertEquals(5, i.get(0));
> +		
> +		try {
> +			i.set(5, 5);
> +			fail("accepted set of 5 beyond end of list");
> +		} catch (ArrayIndexOutOfBoundsException e){
> +			assertTrue(true);
> +		}
> +
> +		i.set(1, 2);
Oh, you grow the array here. Not obvious.

> +		assertEquals(2, i.size());
> +		assertEquals(2, i.get(1));
> +	}
> +
>  	public void testToString() {
>  		final IntList i = new IntList();
>  		i.add(1);
> diff --git a/org.spearce.jgit/src/org/spearce/jgit/util/IntList.java b/org.spearce.jgit/src/org/spearce/jgit/util/IntList.java
> index 0a84793..9d86a5c 100644
> --- a/org.spearce.jgit/src/org/spearce/jgit/util/IntList.java
> +++ b/org.spearce.jgit/src/org/spearce/jgit/util/IntList.java
> @@ -94,6 +94,23 @@ public void add(final int n) {
>  	}
>  
>  	/**
> +	 * Assign an entry in the list.
> +	 * 
> +	 * @param index
> +	 *            index to set, must be in the range [0, {@link #size()}).
> +	 * @param n
> +	 *            value to store at the position.
> +	 */
> +	public void set(final int index, final int n) {
> +		if (count < index)
> +			throw new ArrayIndexOutOfBoundsException(index);
> +		else if (count == index)
> +			add(n);

The interface is quite obscure here. One the one hand it checks for assignment
outside the set but it does grow the array when only one entry is missing. The
reader of the code won't see that easily. A different name and reflection of the
behaviour in the javadoc is needed. The class javadoc says "A more efficient 
List<Integer> using a primitive integer array." and I know of no java.util.List's 
that expand implicitly.

-- robin

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [JGIT PATCH 0/6] Diff processing utilities
  2009-05-02  2:08 [JGIT PATCH 0/6] Diff processing utilities Shawn O. Pearce
  2009-05-02  2:08 ` [JGIT PATCH 1/6] Add set to IntList Shawn O. Pearce
@ 2009-05-03 14:24 ` Johannes Schindelin
  1 sibling, 0 replies; 15+ messages in thread
From: Johannes Schindelin @ 2009-05-03 14:24 UTC (permalink / raw)
  To: Shawn O. Pearce; +Cc: Robin Rosenberg, git

Hi,

On Fri, 1 May 2009, Shawn O. Pearce wrote:

> To be clear, this series *does not* contain a way to create a diff. 
> Dscho wrote much more code which is not yet ready for inclusion.

Indeed.  I'm on it.

> Dscho, can you give us yea-or-nay for SBO lines for the patches
> I've attributed to you?

Yays!

(I had only time to look over them once, but as I am working on the diff 
stuff again, I think I'll be able to find bugs on the go ;-)

Many thanks to you and Robin for finding bugs early!

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [JGIT PATCH 1/6] Add set to IntList
  2009-05-03  7:07   ` [JGIT PATCH 1/6] Add set to IntList Robin Rosenberg
@ 2009-05-04 14:22     ` Shawn O. Pearce
  2009-05-04 14:50       ` Johannes Schindelin
  0 siblings, 1 reply; 15+ messages in thread
From: Shawn O. Pearce @ 2009-05-04 14:22 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: Johannes Schindelin, git

Robin Rosenberg <robin.rosenberg.lists@dewire.com> wrote:
> l?rdag 02 maj 2009 04:08:42 skrev "Shawn O. Pearce" <spearce@spearce.org>:
> > Some applications may wish to modify an int list.

OK, as it turns out, this is used by code which Dscho wrote but I
haven't packaged up as patches yet.  And I don't think that even uses
the weird set(size(), ...) expands the list rules I implemented.
So drop this patch for now from the series and I'll respin it at
a later date.

> >  .../tst/org/spearce/jgit/util/IntListTest.java     |   21 ++++++++++++++++++++
> >  .../src/org/spearce/jgit/util/IntList.java         |   17 ++++++++++++++++
> >  2 files changed, 38 insertions(+), 0 deletions(-)

-- 
Shawn.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [JGIT PATCH 1/6] Add set to IntList
  2009-05-04 14:22     ` Shawn O. Pearce
@ 2009-05-04 14:50       ` Johannes Schindelin
  2009-05-04 14:55         ` Shawn O. Pearce
  0 siblings, 1 reply; 15+ messages in thread
From: Johannes Schindelin @ 2009-05-04 14:50 UTC (permalink / raw)
  To: Shawn O. Pearce; +Cc: Robin Rosenberg, git

Hi,

On Mon, 4 May 2009, Shawn O. Pearce wrote:

> Robin Rosenberg <robin.rosenberg.lists@dewire.com> wrote:
> > l?rdag 02 maj 2009 04:08:42 skrev "Shawn O. Pearce" <spearce@spearce.org>:
> > > Some applications may wish to modify an int list.
> 
> OK, as it turns out, this is used by code which Dscho wrote but I 
> haven't packaged up as patches yet.  And I don't think that even uses 
> the weird set(size(), ...) expands the list rules I implemented. So drop 
> this patch for now from the series and I'll respin it at a later date.

Indeed, my code only needs add(int i), which should expand the array on 
demand (i.e. after a clear(), it should _not_ blindly resize the array).

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [JGIT PATCH 1/6] Add set to IntList
  2009-05-04 14:50       ` Johannes Schindelin
@ 2009-05-04 14:55         ` Shawn O. Pearce
  2009-05-04 15:10           ` Johannes Schindelin
  0 siblings, 1 reply; 15+ messages in thread
From: Shawn O. Pearce @ 2009-05-04 14:55 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Robin Rosenberg, git

Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:
> On Mon, 4 May 2009, Shawn O. Pearce wrote:
> 
> > Robin Rosenberg <robin.rosenberg.lists@dewire.com> wrote:
> > > l?rdag 02 maj 2009 04:08:42 skrev "Shawn O. Pearce" <spearce@spearce.org>:
> > > > Some applications may wish to modify an int list.
> > 
> > OK, as it turns out, this is used by code which Dscho wrote but I 
> > haven't packaged up as patches yet.  And I don't think that even uses 
> > the weird set(size(), ...) expands the list rules I implemented. So drop 
> > this patch for now from the series and I'll respin it at a later date.
> 
> Indeed, my code only needs add(int i), which should expand the array on 
> demand (i.e. after a clear(), it should _not_ blindly resize the array).

Really?  When I was starting to bring your code in and moved from
IntArray to IntList I had compile errors on set calls, so I added
it to IntList.

Huh.  Well, add(int) is already there, and clear() resets the size
to 0 but doesn't resize the internal array, so future adds up to
the prior capacity are free.

-- 
Shawn.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [JGIT PATCH 1/6] Add set to IntList
  2009-05-04 14:55         ` Shawn O. Pearce
@ 2009-05-04 15:10           ` Johannes Schindelin
  0 siblings, 0 replies; 15+ messages in thread
From: Johannes Schindelin @ 2009-05-04 15:10 UTC (permalink / raw)
  To: Shawn O. Pearce; +Cc: Robin Rosenberg, git

Hi,

On Mon, 4 May 2009, Shawn O. Pearce wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:
> > On Mon, 4 May 2009, Shawn O. Pearce wrote:
> > 
> > > Robin Rosenberg <robin.rosenberg.lists@dewire.com> wrote:
> > > > l?rdag 02 maj 2009 04:08:42 skrev "Shawn O. Pearce" 
> > > > <spearce@spearce.org>:
> > > > > Some applications may wish to modify an int list.
> > > 
> > > OK, as it turns out, this is used by code which Dscho wrote but I 
> > > haven't packaged up as patches yet.  And I don't think that even uses 
> > > the weird set(size(), ...) expands the list rules I implemented. So drop 
> > > this patch for now from the series and I'll respin it at a later date.
> > 
> > Indeed, my code only needs add(int i), which should expand the array on 
> > demand (i.e. after a clear(), it should _not_ blindly resize the array).
> 
> Really?  When I was starting to bring your code in and moved from 
> IntArray to IntList I had compile errors on set calls, so I added it to 
> IntList.

Well, in the code you have, that might be true, but as I promised, I 
picked up work on it again.  And my new version only needs add() ;-)

> Huh.  Well, add(int) is already there, and clear() resets the size
> to 0 but doesn't resize the internal array, so future adds up to
> the prior capacity are free.

Exactly as it should be!

Thanks,
Dscho

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2009-05-04 15:10 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-05-02  2:08 [JGIT PATCH 0/6] Diff processing utilities Shawn O. Pearce
2009-05-02  2:08 ` [JGIT PATCH 1/6] Add set to IntList Shawn O. Pearce
2009-05-02  2:08   ` [JGIT PATCH 2/6] Add diff.Edit to describe an edit region within a file Shawn O. Pearce
2009-05-02  2:08     ` [JGIT PATCH 3/6] Add diff.EditList to provide for a list of Edit instances Shawn O. Pearce
2009-05-02  2:08       ` [JGIT PATCH 4/6] Add diff.RawText to index a file content for later compares Shawn O. Pearce
2009-05-02  2:08         ` [JGIT PATCH 5/6] Teach FileHeader, HunkHeader how to create an EditList Shawn O. Pearce
2009-05-02  2:08           ` [JGIT PATCH 6/6] Add diff.DiffFormatter to create Git style unified patch scripts Shawn O. Pearce
2009-05-03  0:03             ` [JGIT PATCH v2 " Shawn O. Pearce
2009-05-03  0:29               ` [JGIT PATCH v3 " Shawn O. Pearce
2009-05-03  7:07   ` [JGIT PATCH 1/6] Add set to IntList Robin Rosenberg
2009-05-04 14:22     ` Shawn O. Pearce
2009-05-04 14:50       ` Johannes Schindelin
2009-05-04 14:55         ` Shawn O. Pearce
2009-05-04 15:10           ` Johannes Schindelin
2009-05-03 14:24 ` [JGIT PATCH 0/6] Diff processing utilities Johannes Schindelin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).