* [JGIT PATCH 11/12] Add a test for delta binary patch parsing and fix a bug in it
From: Shawn O. Pearce @ 2008-12-12 22:05 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
In-Reply-To: <1229119558-1293-11-git-send-email-spearce@spearce.org>
We had the wrong header code in this case, so we didn't parse
the length correctly for delta style binary hunks. Without a
test case for it we never noticed the problem.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
.../tst/org/spearce/jgit/patch/PatchTest.java | 39 +++++++++++++++++++-
.../jgit/patch/testParse_GitBinaryDelta.patch | 21 +++++++++++
...nary.patch => testParse_GitBinaryLiteral.patch} | 0
.../src/org/spearce/jgit/patch/BinaryHunk.java | 2 +-
4 files changed, 60 insertions(+), 2 deletions(-)
create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_GitBinaryDelta.patch
rename org.spearce.jgit.test/tst/org/spearce/jgit/patch/{testParse_GitBinary.patch => testParse_GitBinaryLiteral.patch} (100%)
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
index 2c617d3..8309951 100644
--- a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
@@ -184,7 +184,7 @@ assertTrue(fh.getNewName().startsWith(
assertEquals(272, fh.getHunks().get(0).getOldImage().getStartLine());
}
- public void testParse_GitBinary() throws IOException {
+ public void testParse_GitBinaryLiteral() throws IOException {
final Patch p = parseTestPatchFile();
final int[] binsizes = { 359, 393, 372, 404 };
assertEquals(5, p.getFiles().size());
@@ -229,6 +229,43 @@ assertTrue(fh.getNewName().startsWith(
assertEquals(272, fh.getHunks().get(0).getOldImage().getStartLine());
}
+ public void testParse_GitBinaryDelta() throws IOException {
+ final Patch p = parseTestPatchFile();
+ assertEquals(1, p.getFiles().size());
+ assertTrue(p.getErrors().isEmpty());
+
+ final FileHeader fh = p.getFiles().get(0);
+ assertTrue(fh.getNewName().startsWith("zero.bin"));
+ assertSame(FileHeader.ChangeType.MODIFY, fh.getChangeType());
+ assertSame(FileHeader.PatchType.GIT_BINARY, fh.getPatchType());
+ assertSame(FileMode.REGULAR_FILE, fh.getNewMode());
+
+ assertNotNull(fh.getOldId());
+ assertNotNull(fh.getNewId());
+ assertEquals("08e7df176454f3ee5eeda13efa0adaa54828dfd8", fh.getOldId()
+ .name());
+ assertEquals("d70d8710b6d32ff844af0ee7c247e4b4b051867f", fh.getNewId()
+ .name());
+
+ assertTrue(fh.getHunks().isEmpty());
+ assertFalse(fh.hasMetaDataChanges());
+
+ final BinaryHunk fwd = fh.getForwardBinaryHunk();
+ final BinaryHunk rev = fh.getReverseBinaryHunk();
+ assertNotNull(fwd);
+ assertNotNull(rev);
+ assertEquals(12, fwd.getSize());
+ assertEquals(11, rev.getSize());
+
+ assertSame(fh, fwd.getFileHeader());
+ assertSame(fh, rev.getFileHeader());
+
+ assertSame(BinaryHunk.Type.DELTA_DEFLATED, fwd.getType());
+ assertSame(BinaryHunk.Type.DELTA_DEFLATED, rev.getType());
+
+ assertEquals(496, fh.endOffset);
+ }
+
public void testParse_FixNoNewline() throws IOException {
final Patch p = parseTestPatchFile();
assertEquals(1, p.getFiles().size());
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_GitBinaryDelta.patch b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_GitBinaryDelta.patch
new file mode 100644
index 0000000..5b2c9c6
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_GitBinaryDelta.patch
@@ -0,0 +1,21 @@
+From 7e49721ad0efdec3a81e20bc58e385ea5d2b87b7 Mon Sep 17 00:00:00 2001
+From: Shawn O. Pearce <sop@google.com>
+Date: Fri, 12 Dec 2008 12:45:17 -0800
+Subject: [PATCH] make zero have a 3
+
+---
+ zero.bin | Bin 4096 -> 4096 bytes
+ 1 files changed, 0 insertions(+), 0 deletions(-)
+
+diff --git a/zero.bin b/zero.bin
+index 08e7df176454f3ee5eeda13efa0adaa54828dfd8..d70d8710b6d32ff844af0ee7c247e4b4b051867f 100644
+GIT binary patch
+delta 12
+TcmZorXi%6C%4ociaTPxR8IA+R
+
+delta 11
+ScmZorXi(Uguz-JJK>`37u>@iO
+
+--
+1.6.1.rc2.306.ge5d5e
+
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_GitBinary.patch b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_GitBinaryLiteral.patch
similarity index 100%
rename from org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_GitBinary.patch
rename to org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_GitBinaryLiteral.patch
diff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/BinaryHunk.java b/org.spearce.jgit/src/org/spearce/jgit/patch/BinaryHunk.java
index 3e07ec4..92eab86 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/patch/BinaryHunk.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/BinaryHunk.java
@@ -100,7 +100,7 @@ int parseHunk(int ptr, final int end) {
} else if (match(buf, ptr, DELTA) >= 0) {
type = Type.DELTA_DEFLATED;
- length = parseBase10(buf, ptr + LITERAL.length, null);
+ length = parseBase10(buf, ptr + DELTA.length, null);
} else {
// Not a valid binary hunk. Signal to the caller that
--
1.6.1.rc2.306.ge5d5e
^ permalink raw reply related
* [JGIT PATCH 12/12] Add support for parsing "diff --cc" style patches
From: Shawn O. Pearce @ 2008-12-12 22:05 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
In-Reply-To: <1229119558-1293-12-git-send-email-spearce@spearce.org>
Even though the diff --cc format used by Git is only meant to be
read by humans, JGit needs to be able to parse these to get the
patch metadata so it can be shown in a user interface to facilitate
code review processes.
Patches are parsed into the specialized CombinedFileHeader and
CombinedHunkHeader classes, where the old image information is
augmented with additional fields for the arbitrary number of parents
that can appear in such patches. These cost more in terms of memory,
but "diff --cc" style patches tend to occur very infrequently as
they only occur during a merge conflict resolution.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
.../org/spearce/jgit/patch/PatchCcErrorTest.java | 97 +++++++++
.../tst/org/spearce/jgit/patch/PatchCcTest.java | 200 ++++++++++++++++++
.../jgit/patch/testError_CcTruncatedOld.patch | 24 +++
.../jgit/patch/testParse_CcDeleteFile.patch | 12 +
.../spearce/jgit/patch/testParse_CcNewFile.patch | 14 ++
.../spearce/jgit/patch/testParse_OneFileCc.patch | 27 +++
| 213 ++++++++++++++++++++
| 191 ++++++++++++++++++
| 56 +++---
| 2 +-
.../src/org/spearce/jgit/patch/Patch.java | 7 +-
11 files changed, 814 insertions(+), 29 deletions(-)
create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchCcErrorTest.java
create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchCcTest.java
create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_CcTruncatedOld.patch
create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_CcDeleteFile.patch
create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_CcNewFile.patch
create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_OneFileCc.patch
create mode 100644 org.spearce.jgit/src/org/spearce/jgit/patch/CombinedFileHeader.java
create mode 100644 org.spearce.jgit/src/org/spearce/jgit/patch/CombinedHunkHeader.java
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchCcErrorTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchCcErrorTest.java
new file mode 100644
index 0000000..a2f3a19
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchCcErrorTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2008, Google Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.patch;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import junit.framework.TestCase;
+
+public class PatchCcErrorTest extends TestCase {
+ public void testError_CcTruncatedOld() throws IOException {
+ final Patch p = parseTestPatchFile();
+ assertEquals(1, p.getFiles().size());
+ assertEquals(3, p.getErrors().size());
+ {
+ final FormatError e = p.getErrors().get(0);
+ assertSame(FormatError.Severity.ERROR, e.getSeverity());
+ assertEquals(
+ "Truncated hunk, at least 1 lines is missing for ancestor 1",
+ e.getMessage());
+ assertEquals(346, e.getOffset());
+ assertTrue(e.getLineText().startsWith(
+ "@@@ -55,12 -163,13 +163,15 @@@ public "));
+ }
+ {
+ final FormatError e = p.getErrors().get(1);
+ assertSame(FormatError.Severity.ERROR, e.getSeverity());
+ assertEquals(
+ "Truncated hunk, at least 2 lines is missing for ancestor 2",
+ e.getMessage());
+ assertEquals(346, e.getOffset());
+ assertTrue(e.getLineText().startsWith(
+ "@@@ -55,12 -163,13 +163,15 @@@ public "));
+ }
+ {
+ final FormatError e = p.getErrors().get(2);
+ assertSame(FormatError.Severity.ERROR, e.getSeverity());
+ assertEquals("Truncated hunk, at least 3 new lines is missing", e
+ .getMessage());
+ assertEquals(346, e.getOffset());
+ assertTrue(e.getLineText().startsWith(
+ "@@@ -55,12 -163,13 +163,15 @@@ public "));
+ }
+ }
+
+ private Patch parseTestPatchFile() throws IOException {
+ final String patchFile = getName() + ".patch";
+ final InputStream in = getClass().getResourceAsStream(patchFile);
+ if (in == null) {
+ fail("No " + patchFile + " test vector");
+ return null; // Never happens
+ }
+ try {
+ final Patch p = new Patch();
+ p.parse(in);
+ return p;
+ } finally {
+ in.close();
+ }
+ }
+
+}
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchCcTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchCcTest.java
new file mode 100644
index 0000000..9e8650b
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchCcTest.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2008, Google Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.patch;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.spearce.jgit.lib.FileMode;
+
+import junit.framework.TestCase;
+
+public class PatchCcTest extends TestCase {
+ public void testParse_OneFileCc() throws IOException {
+ final Patch p = parseTestPatchFile();
+ assertEquals(1, p.getFiles().size());
+ assertTrue(p.getErrors().isEmpty());
+
+ final CombinedFileHeader cfh = (CombinedFileHeader) p.getFiles().get(0);
+
+ assertEquals("org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java",
+ cfh.getNewName());
+ assertEquals(cfh.getNewName(), cfh.getOldName());
+
+ assertEquals(98, cfh.startOffset);
+
+ assertEquals(2, cfh.getParentCount());
+ assertSame(cfh.getOldId(0), cfh.getOldId());
+ assertEquals("169356b", cfh.getOldId(0).name());
+ assertEquals("dd8c317", cfh.getOldId(1).name());
+ assertEquals("fd85931", cfh.getNewId().name());
+
+ assertSame(cfh.getOldMode(0), cfh.getOldMode());
+ assertSame(FileMode.REGULAR_FILE, cfh.getOldMode(0));
+ assertSame(FileMode.REGULAR_FILE, cfh.getOldMode(1));
+ assertSame(FileMode.EXECUTABLE_FILE, cfh.getNewMode());
+ assertSame(FileHeader.ChangeType.MODIFY, cfh.getChangeType());
+ assertSame(FileHeader.PatchType.UNIFIED, cfh.getPatchType());
+
+ assertEquals(1, cfh.getHunks().size());
+ {
+ final CombinedHunkHeader h = cfh.getHunks().get(0);
+
+ assertSame(cfh, h.getFileHeader());
+ assertEquals(346, h.startOffset);
+ assertEquals(764, h.endOffset);
+
+ assertSame(h.getOldImage(0), h.getOldImage());
+ assertSame(cfh.getOldId(0), h.getOldImage(0).getId());
+ assertSame(cfh.getOldId(1), h.getOldImage(1).getId());
+
+ assertEquals(55, h.getOldImage(0).getStartLine());
+ assertEquals(12, h.getOldImage(0).getLineCount());
+ assertEquals(3, h.getOldImage(0).getLinesAdded());
+ assertEquals(0, h.getOldImage(0).getLinesDeleted());
+
+ assertEquals(163, h.getOldImage(1).getStartLine());
+ assertEquals(13, h.getOldImage(1).getLineCount());
+ assertEquals(2, h.getOldImage(1).getLinesAdded());
+ assertEquals(0, h.getOldImage(1).getLinesDeleted());
+
+ assertEquals(163, h.getNewStartLine());
+ assertEquals(15, h.getNewLineCount());
+
+ assertEquals(10, h.getLinesContext());
+ }
+ }
+
+ public void testParse_CcNewFile() throws IOException {
+ final Patch p = parseTestPatchFile();
+ assertEquals(1, p.getFiles().size());
+ assertTrue(p.getErrors().isEmpty());
+
+ final CombinedFileHeader cfh = (CombinedFileHeader) p.getFiles().get(0);
+
+ assertSame(FileHeader.DEV_NULL, cfh.getOldName());
+ assertEquals("d", cfh.getNewName());
+
+ assertEquals(187, cfh.startOffset);
+
+ assertEquals(2, cfh.getParentCount());
+ assertSame(cfh.getOldId(0), cfh.getOldId());
+ assertEquals("0000000", cfh.getOldId(0).name());
+ assertEquals("0000000", cfh.getOldId(1).name());
+ assertEquals("4bcfe98", cfh.getNewId().name());
+
+ assertSame(cfh.getOldMode(0), cfh.getOldMode());
+ assertSame(FileMode.MISSING, cfh.getOldMode(0));
+ assertSame(FileMode.MISSING, cfh.getOldMode(1));
+ assertSame(FileMode.REGULAR_FILE, cfh.getNewMode());
+ assertSame(FileHeader.ChangeType.ADD, cfh.getChangeType());
+ assertSame(FileHeader.PatchType.UNIFIED, cfh.getPatchType());
+
+ assertEquals(1, cfh.getHunks().size());
+ {
+ final CombinedHunkHeader h = cfh.getHunks().get(0);
+
+ assertSame(cfh, h.getFileHeader());
+ assertEquals(273, h.startOffset);
+ assertEquals(300, h.endOffset);
+
+ assertSame(h.getOldImage(0), h.getOldImage());
+ assertSame(cfh.getOldId(0), h.getOldImage(0).getId());
+ assertSame(cfh.getOldId(1), h.getOldImage(1).getId());
+
+ assertEquals(1, h.getOldImage(0).getStartLine());
+ assertEquals(0, h.getOldImage(0).getLineCount());
+ assertEquals(1, h.getOldImage(0).getLinesAdded());
+ assertEquals(0, h.getOldImage(0).getLinesDeleted());
+
+ assertEquals(1, h.getOldImage(1).getStartLine());
+ assertEquals(0, h.getOldImage(1).getLineCount());
+ assertEquals(1, h.getOldImage(1).getLinesAdded());
+ assertEquals(0, h.getOldImage(1).getLinesDeleted());
+
+ assertEquals(1, h.getNewStartLine());
+ assertEquals(1, h.getNewLineCount());
+
+ assertEquals(0, h.getLinesContext());
+ }
+ }
+
+ public void testParse_CcDeleteFile() throws IOException {
+ final Patch p = parseTestPatchFile();
+ assertEquals(1, p.getFiles().size());
+ assertTrue(p.getErrors().isEmpty());
+
+ final CombinedFileHeader cfh = (CombinedFileHeader) p.getFiles().get(0);
+
+ assertEquals("a", cfh.getOldName());
+ assertSame(FileHeader.DEV_NULL, cfh.getNewName());
+
+ assertEquals(187, cfh.startOffset);
+
+ assertEquals(2, cfh.getParentCount());
+ assertSame(cfh.getOldId(0), cfh.getOldId());
+ assertEquals("7898192", cfh.getOldId(0).name());
+ assertEquals("2e65efe", cfh.getOldId(1).name());
+ assertEquals("0000000", cfh.getNewId().name());
+
+ assertSame(cfh.getOldMode(0), cfh.getOldMode());
+ assertSame(FileMode.REGULAR_FILE, cfh.getOldMode(0));
+ assertSame(FileMode.REGULAR_FILE, cfh.getOldMode(1));
+ assertSame(FileMode.MISSING, cfh.getNewMode());
+ assertSame(FileHeader.ChangeType.DELETE, cfh.getChangeType());
+ assertSame(FileHeader.PatchType.UNIFIED, cfh.getPatchType());
+
+ assertTrue(cfh.getHunks().isEmpty());
+ }
+
+ private Patch parseTestPatchFile() throws IOException {
+ final String patchFile = getName() + ".patch";
+ final InputStream in = getClass().getResourceAsStream(patchFile);
+ if (in == null) {
+ fail("No " + patchFile + " test vector");
+ return null; // Never happens
+ }
+ try {
+ final Patch p = new Patch();
+ p.parse(in);
+ return p;
+ } finally {
+ in.close();
+ }
+ }
+}
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_CcTruncatedOld.patch b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_CcTruncatedOld.patch
new file mode 100644
index 0000000..1bbcfb5
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_CcTruncatedOld.patch
@@ -0,0 +1,24 @@
+commit 1a56639bbea8e8cbfbe5da87746de97f9217ce9b
+Date: Tue May 13 00:43:56 2008 +0200
+ ...
+
+diff --cc org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java
+index 169356b,dd8c317..fd85931
+mode 100644,100644..100755
+--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java
++++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java
+@@@ -55,12 -163,13 +163,15 @@@ public class UIText extends NLS
+
+ /** */
+ public static String ResourceHistory_toggleCommentWrap;
++
+ /** */
+ + /** */
+ public static String ResourceHistory_toggleRevDetail;
+ /** */
+ public static String ResourceHistory_toggleRevComment;
+ /** */
+ public static String ResourceHistory_toggleTooltips;
+
+
+commit 1a56639bbea8e8cbfbe5da87746de97f9217ce9b
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_CcDeleteFile.patch b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_CcDeleteFile.patch
new file mode 100644
index 0000000..2654e09
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_CcDeleteFile.patch
@@ -0,0 +1,12 @@
+commit 740709ece2412856c0c3eabd4dc4a4cf115b0de6
+Merge: 5c19b43... 13a2c0d...
+Author: Shawn O. Pearce <sop@google.com>
+Date: Fri Dec 12 13:26:52 2008 -0800
+
+ Merge branch 'b' into d
+
+diff --cc a
+index 7898192,2e65efe..0000000
+deleted file mode 100644,100644
+--- a/a
++++ /dev/null
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_CcNewFile.patch b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_CcNewFile.patch
new file mode 100644
index 0000000..1a9b7b0
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_CcNewFile.patch
@@ -0,0 +1,14 @@
+commit 6cb8160a4717d51fd3cc0baf721946daa60cf921
+Merge: 5c19b43... 13a2c0d...
+Author: Shawn O. Pearce <sop@google.com>
+Date: Fri Dec 12 13:26:52 2008 -0800
+
+ Merge branch 'b' into d
+
+diff --cc d
+index 0000000,0000000..4bcfe98
+new file mode 100644
+--- /dev/null
++++ b/d
+@@@ -1,0 -1,0 +1,1 @@@
+++d
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_OneFileCc.patch b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_OneFileCc.patch
new file mode 100644
index 0000000..c096b33
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_OneFileCc.patch
@@ -0,0 +1,27 @@
+commit 1a56639bbea8e8cbfbe5da87746de97f9217ce9b
+Date: Tue May 13 00:43:56 2008 +0200
+ ...
+
+diff --cc org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java
+index 169356b,dd8c317..fd85931
+mode 100644,100644..100755
+--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java
++++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java
+@@@ -55,12 -163,13 +163,15 @@@ public class UIText extends NLS
+
+ /** */
+ public static String ResourceHistory_toggleCommentWrap;
++
+ /** */
+ + public static String ResourceHistory_toggleCommentFill;
+ + /** */
+ public static String ResourceHistory_toggleRevDetail;
++
+ /** */
+ public static String ResourceHistory_toggleRevComment;
++
+ /** */
+ public static String ResourceHistory_toggleTooltips;
+
+
+commit 1a56639bbea8e8cbfbe5da87746de97f9217ce9b
--git a/org.spearce.jgit/src/org/spearce/jgit/patch/CombinedFileHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/CombinedFileHeader.java
new file mode 100644
index 0000000..7171600
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/CombinedFileHeader.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2008, Google Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.patch;
+
+import static org.spearce.jgit.lib.Constants.encodeASCII;
+import static org.spearce.jgit.util.RawParseUtils.match;
+import static org.spearce.jgit.util.RawParseUtils.nextLF;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.spearce.jgit.lib.AbbreviatedObjectId;
+import org.spearce.jgit.lib.FileMode;
+
+/**
+ * A file in the Git "diff --cc" or "diff --combined" format.
+ * <p>
+ * A combined diff shows an n-way comparison between two or more ancestors and
+ * the final revision. Its primary function is to perform code reviews on a
+ * merge which introduces changes not in any ancestor.
+ */
+public class CombinedFileHeader extends FileHeader {
+ private static final byte[] MODE = encodeASCII("mode ");
+
+ private AbbreviatedObjectId[] oldIds;
+
+ private FileMode[] oldModes;
+
+ CombinedFileHeader(final byte[] b, final int offset) {
+ super(b, offset);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public List<? extends CombinedHunkHeader> getHunks() {
+ return (List<CombinedHunkHeader>) super.getHunks();
+ }
+
+ /** @return number of ancestor revisions mentioned in this diff. */
+ @Override
+ public int getParentCount() {
+ return oldIds.length;
+ }
+
+ /** @return get the file mode of the first parent. */
+ @Override
+ public FileMode getOldMode() {
+ return getOldMode(0);
+ }
+
+ /**
+ * Get the file mode of the nth ancestor
+ *
+ * @param nthParent
+ * the ancestor to get the mode of
+ * @return the mode of the requested ancestor.
+ */
+ public FileMode getOldMode(final int nthParent) {
+ return oldModes[nthParent];
+ }
+
+ /** @return get the object id of the first parent. */
+ @Override
+ public AbbreviatedObjectId getOldId() {
+ return getOldId(0);
+ }
+
+ /**
+ * Get the ObjectId of the nth ancestor
+ *
+ * @param nthParent
+ * the ancestor to get the object id of
+ * @return the id of the requested ancestor.
+ */
+ public AbbreviatedObjectId getOldId(final int nthParent) {
+ return oldIds[nthParent];
+ }
+
+ int parseGitHeaders(int ptr, final int end) {
+ while (ptr < end) {
+ final int eol = nextLF(buf, ptr);
+ if (isHunkHdr(buf, ptr, end) >= 1) {
+ // First hunk header; break out and parse them later.
+ break;
+
+ } else if (match(buf, ptr, OLD_NAME) >= 0) {
+ parseOldName(ptr, eol);
+
+ } else if (match(buf, ptr, NEW_NAME) >= 0) {
+ parseNewName(ptr, eol);
+
+ } else if (match(buf, ptr, INDEX) >= 0) {
+ parseIndexLine(ptr + INDEX.length, eol);
+
+ } else if (match(buf, ptr, MODE) >= 0) {
+ parseModeLine(ptr + MODE.length, eol);
+
+ } else if (match(buf, ptr, NEW_FILE_MODE) >= 0) {
+ parseNewFileMode(ptr, eol);
+
+ } else if (match(buf, ptr, DELETED_FILE_MODE) >= 0) {
+ parseDeletedFileMode(ptr + DELETED_FILE_MODE.length, eol);
+
+ } else {
+ // Probably an empty patch (stat dirty).
+ break;
+ }
+
+ ptr = eol;
+ }
+ return ptr;
+ }
+
+ @Override
+ protected void parseIndexLine(int ptr, final int eol) {
+ // "index $asha1,$bsha1..$csha1"
+ //
+ final List<AbbreviatedObjectId> ids = new ArrayList<AbbreviatedObjectId>();
+ while (ptr < eol) {
+ final int comma = nextLF(buf, ptr, ',');
+ if (eol <= comma)
+ break;
+ ids.add(AbbreviatedObjectId.fromString(buf, ptr, comma - 1));
+ ptr = comma;
+ }
+
+ oldIds = new AbbreviatedObjectId[ids.size() + 1];
+ ids.toArray(oldIds);
+ final int dot2 = nextLF(buf, ptr, '.');
+ oldIds[ids.size()] = AbbreviatedObjectId.fromString(buf, ptr, dot2 - 1);
+ newId = AbbreviatedObjectId.fromString(buf, dot2 + 1, eol - 1);
+ oldModes = new FileMode[oldIds.length];
+ }
+
+ @Override
+ protected void parseNewFileMode(final int ptr, final int eol) {
+ for (int i = 0; i < oldModes.length; i++)
+ oldModes[i] = FileMode.MISSING;
+ super.parseNewFileMode(ptr, eol);
+ }
+
+ @Override
+ HunkHeader newHunkHeader(final int offset) {
+ return new CombinedHunkHeader(this, offset);
+ }
+
+ private void parseModeLine(int ptr, final int eol) {
+ // "mode $amode,$bmode..$cmode"
+ //
+ int n = 0;
+ while (ptr < eol) {
+ final int comma = nextLF(buf, ptr, ',');
+ if (eol <= comma)
+ break;
+ oldModes[n++] = parseFileMode(ptr, comma);
+ ptr = comma;
+ }
+ final int dot2 = nextLF(buf, ptr, '.');
+ oldModes[n] = parseFileMode(ptr, dot2);
+ newMode = parseFileMode(dot2 + 1, eol);
+ }
+
+ private void parseDeletedFileMode(int ptr, final int eol) {
+ // "deleted file mode $amode,$bmode"
+ //
+ changeType = ChangeType.DELETE;
+ int n = 0;
+ while (ptr < eol) {
+ final int comma = nextLF(buf, ptr, ',');
+ if (eol <= comma)
+ break;
+ oldModes[n++] = parseFileMode(ptr, comma);
+ ptr = comma;
+ }
+ oldModes[n] = parseFileMode(ptr, eol);
+ newMode = FileMode.MISSING;
+ }
+}
--git a/org.spearce.jgit/src/org/spearce/jgit/patch/CombinedHunkHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/CombinedHunkHeader.java
new file mode 100644
index 0000000..bebeafa
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/CombinedHunkHeader.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2008, Google Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.patch;
+
+import static org.spearce.jgit.util.RawParseUtils.nextLF;
+import static org.spearce.jgit.util.RawParseUtils.parseBase10;
+
+import org.spearce.jgit.lib.AbbreviatedObjectId;
+import org.spearce.jgit.util.MutableInteger;
+
+/** Hunk header for a hunk appearing in a "diff --cc" style patch. */
+public class CombinedHunkHeader extends HunkHeader {
+ private static abstract class CombinedOldImage extends OldImage {
+ int nContext;
+ }
+
+ private CombinedOldImage[] old;
+
+ CombinedHunkHeader(final CombinedFileHeader fh, final int offset) {
+ super(fh, offset, null);
+ old = new CombinedOldImage[fh.getParentCount()];
+ for (int i = 0; i < old.length; i++) {
+ final int imagePos = i;
+ old[i] = new CombinedOldImage() {
+ @Override
+ public AbbreviatedObjectId getId() {
+ return fh.getOldId(imagePos);
+ }
+ };
+ }
+ }
+
+ @Override
+ public CombinedFileHeader getFileHeader() {
+ return (CombinedFileHeader) super.getFileHeader();
+ }
+
+ @Override
+ public OldImage getOldImage() {
+ return getOldImage(0);
+ }
+
+ /**
+ * Get the OldImage data related to the nth ancestor
+ *
+ * @param nthParent
+ * the ancestor to get the old image data of
+ * @return image data of the requested ancestor.
+ */
+ public OldImage getOldImage(final int nthParent) {
+ return old[nthParent];
+ }
+
+ @Override
+ void parseHeader(final int end) {
+ // Parse "@@@ -55,12 -163,13 +163,15 @@@ protected boolean"
+ //
+ final byte[] buf = file.buf;
+ final MutableInteger ptr = new MutableInteger();
+ ptr.value = nextLF(buf, startOffset, ' ');
+
+ for (int n = 0; n < old.length; n++) {
+ old[n].startLine = -parseBase10(buf, ptr.value, ptr);
+ if (buf[ptr.value] == ',')
+ old[n].lineCount = parseBase10(buf, ptr.value + 1, ptr);
+ else
+ old[n].lineCount = 1;
+ }
+
+ newStartLine = parseBase10(buf, ptr.value + 1, ptr);
+ if (buf[ptr.value] == ',')
+ newLineCount = parseBase10(buf, ptr.value + 1, ptr);
+ else
+ newLineCount = 1;
+ }
+
+ @Override
+ int parseBody(final Patch script, final int end) {
+ final byte[] buf = file.buf;
+ int c = nextLF(buf, startOffset);
+
+ for (final CombinedOldImage o : old) {
+ o.nDeleted = 0;
+ o.nAdded = 0;
+ o.nContext = 0;
+ }
+ nContext = 0;
+ int nAdded = 0;
+
+ SCAN: for (int eol; c < end; c = eol) {
+ eol = nextLF(buf, c);
+
+ if (eol - c < old.length + 1) {
+ // Line isn't long enough to mention the state of each
+ // ancestor. It must be the end of the hunk.
+ break SCAN;
+ }
+
+ switch (buf[c]) {
+ case ' ':
+ case '-':
+ case '+':
+ break;
+
+ default:
+ // Line can't possibly be part of this hunk; the first
+ // ancestor information isn't recognizable.
+ //
+ break SCAN;
+ }
+
+ int localcontext = 0;
+ for (int ancestor = 0; ancestor < old.length; ancestor++) {
+ switch (buf[c + ancestor]) {
+ case ' ':
+ localcontext++;
+ old[ancestor].nContext++;
+ continue;
+
+ case '-':
+ old[ancestor].nDeleted++;
+ continue;
+
+ case '+':
+ old[ancestor].nAdded++;
+ nAdded++;
+ continue;
+
+ default:
+ break SCAN;
+ }
+ }
+ if (localcontext == old.length)
+ nContext++;
+ }
+
+ for (int ancestor = 0; ancestor < old.length; ancestor++) {
+ final CombinedOldImage o = old[ancestor];
+ final int cmp = o.nContext + o.nDeleted;
+ if (cmp < o.lineCount) {
+ final int missingCnt = o.lineCount - cmp;
+ script.error(buf, startOffset, "Truncated hunk, at least "
+ + missingCnt + " lines is missing for ancestor "
+ + (ancestor + 1));
+ }
+ }
+
+ if (nContext + nAdded < newLineCount) {
+ final int missingCount = newLineCount - (nContext + nAdded);
+ script.error(buf, startOffset, "Truncated hunk, at least "
+ + missingCount + " new lines is missing");
+ }
+
+ return c;
+ }
+}
--git a/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java
index 48d7623..79e4b0a 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java
@@ -61,9 +61,9 @@
private static final byte[] NEW_MODE = encodeASCII("new mode ");
- private static final byte[] DELETED_FILE_MODE = encodeASCII("deleted file mode ");
+ protected static final byte[] DELETED_FILE_MODE = encodeASCII("deleted file mode ");
- private static final byte[] NEW_FILE_MODE = encodeASCII("new file mode ");
+ protected static final byte[] NEW_FILE_MODE = encodeASCII("new file mode ");
private static final byte[] COPY_FROM = encodeASCII("copy from ");
@@ -81,7 +81,7 @@
private static final byte[] DISSIMILARITY_INDEX = encodeASCII("dissimilarity index ");
- private static final byte[] INDEX = encodeASCII("index ");
+ protected static final byte[] INDEX = encodeASCII("index ");
static final byte[] OLD_NAME = encodeASCII("--- ");
@@ -136,10 +136,10 @@
private FileMode oldMode;
/** New mode of the file, if described by the patch, else null. */
- private FileMode newMode;
+ protected FileMode newMode;
/** General type of change indicated by the patch. */
- private ChangeType changeType;
+ protected ChangeType changeType;
/** Similarity score if {@link #changeType} is a copy or rename. */
private int score;
@@ -148,7 +148,7 @@
private AbbreviatedObjectId oldId;
/** ObjectId listed on the index line for the new (post-image) */
- private AbbreviatedObjectId newId;
+ protected AbbreviatedObjectId newId;
/** Type of patch used to modify this file */
PatchType patchType;
@@ -264,7 +264,7 @@ public boolean hasMetaDataChanges() {
}
/** @return hunks altering this file; in order of appearance in patch */
- public List<HunkHeader> getHunks() {
+ public List<? extends HunkHeader> getHunks() {
if (hunks == null)
return Collections.emptyList();
return hunks;
@@ -369,14 +369,10 @@ int parseGitHeaders(int ptr, final int end) {
break;
} else if (match(buf, ptr, OLD_NAME) >= 0) {
- oldName = p1(parseName(oldName, ptr + OLD_NAME.length, eol));
- if (oldName == DEV_NULL)
- changeType = ChangeType.ADD;
+ parseOldName(ptr, eol);
} else if (match(buf, ptr, NEW_NAME) >= 0) {
- newName = p1(parseName(newName, ptr + NEW_NAME.length, eol));
- if (newName == DEV_NULL)
- changeType = ChangeType.DELETE;
+ parseNewName(ptr, eol);
} else if (match(buf, ptr, OLD_MODE) >= 0) {
oldMode = parseFileMode(ptr + OLD_MODE.length, eol);
@@ -390,9 +386,7 @@ int parseGitHeaders(int ptr, final int end) {
changeType = ChangeType.DELETE;
} else if (match(buf, ptr, NEW_FILE_MODE) >= 0) {
- oldMode = FileMode.MISSING;
- newMode = parseFileMode(ptr + NEW_FILE_MODE.length, eol);
- changeType = ChangeType.ADD;
+ parseNewFileMode(ptr, eol);
} else if (match(buf, ptr, COPY_FROM) >= 0) {
oldName = parseName(oldName, ptr + COPY_FROM.length, eol);
@@ -437,6 +431,24 @@ int parseGitHeaders(int ptr, final int end) {
return ptr;
}
+ protected void parseOldName(int ptr, final int eol) {
+ oldName = p1(parseName(oldName, ptr + OLD_NAME.length, eol));
+ if (oldName == DEV_NULL)
+ changeType = ChangeType.ADD;
+ }
+
+ protected void parseNewName(int ptr, final int eol) {
+ newName = p1(parseName(newName, ptr + NEW_NAME.length, eol));
+ if (newName == DEV_NULL)
+ changeType = ChangeType.DELETE;
+ }
+
+ protected void parseNewFileMode(int ptr, final int eol) {
+ oldMode = FileMode.MISSING;
+ newMode = parseFileMode(ptr + NEW_FILE_MODE.length, eol);
+ changeType = ChangeType.ADD;
+ }
+
int parseTraditionalHeaders(int ptr, final int end) {
while (ptr < end) {
final int eol = nextLF(buf, ptr);
@@ -445,14 +457,10 @@ int parseTraditionalHeaders(int ptr, final int end) {
break;
} else if (match(buf, ptr, OLD_NAME) >= 0) {
- oldName = p1(parseName(oldName, ptr + OLD_NAME.length, eol));
- if (oldName == DEV_NULL)
- changeType = ChangeType.ADD;
+ parseOldName(ptr, eol);
} else if (match(buf, ptr, NEW_NAME) >= 0) {
- newName = p1(parseName(newName, ptr + NEW_NAME.length, eol));
- if (newName == DEV_NULL)
- changeType = ChangeType.DELETE;
+ parseNewName(ptr, eol);
} else {
// Possibly an empty patch.
@@ -494,7 +502,7 @@ private static String p1(final String r) {
return s > 0 ? r.substring(s + 1) : r;
}
- private FileMode parseFileMode(int ptr, final int end) {
+ protected FileMode parseFileMode(int ptr, final int end) {
int tmp = 0;
while (ptr < end - 1) {
tmp <<= 3;
@@ -503,7 +511,7 @@ private FileMode parseFileMode(int ptr, final int end) {
return FileMode.fromBits(tmp);
}
- private void parseIndexLine(int ptr, final int end) {
+ protected void parseIndexLine(int ptr, final int end) {
// "index $asha1..$bsha1[ $mode]" where $asha1 and $bsha1
// can be unique abbreviations
//
--git a/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java
index f543aed..fc149ac 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java
@@ -84,7 +84,7 @@ public int getLinesAdded() {
public abstract AbbreviatedObjectId getId();
}
- private final FileHeader file;
+ final FileHeader file;
/** Offset within {@link #file}.buf to the "@@ -" line. */
final int startOffset;
diff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java
index 51f1fe5..f23ba69 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java
@@ -94,7 +94,7 @@ public void addFile(final FileHeader fh) {
}
/** @return list of files described in the patch, in occurrence order. */
- public List<FileHeader> getFiles() {
+ public List<? extends FileHeader> getFiles() {
return files;
}
@@ -238,9 +238,8 @@ private int parseDiffCombined(final byte[] hdr, final byte[] buf,
if (ptr < 0)
return skipFile(buf, start, end);
- // TODO Support parsing diff --cc headers
- // TODO parse diff --cc hunks
- warn(buf, start, "diff --cc format not supported");
+ ptr = fh.parseGitHeaders(ptr, end);
+ ptr = parseHunks(fh, ptr, end);
fh.endOffset = ptr;
addFile(fh);
return ptr;
--
1.6.1.rc2.306.ge5d5e
^ permalink raw reply related
* [JGIT PATCH 10/12] Use FileMode.MISSING when a file is added or deleted rather than null
From: Shawn O. Pearce @ 2008-12-12 22:05 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
In-Reply-To: <1229119558-1293-10-git-send-email-spearce@spearce.org>
Null is better used to indicate "no mode information at all" in the
patch, while FileMode.MISSING is already commonly used within the
TreeWalk code to mean "this path doesn't exist in this tree". In
the context of a patch to create or delete a file, MISSING makes a
lot more sense for the application to work with.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
| 4 ++--
.../tst/org/spearce/jgit/patch/PatchTest.java | 1 +
| 2 ++
3 files changed, 5 insertions(+), 2 deletions(-)
--git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/FileHeaderTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/FileHeaderTest.java
index 36c528e..69e06ab 100644
--- a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/FileHeaderTest.java
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/FileHeaderTest.java
@@ -153,7 +153,7 @@ public void testParseUnicodeName_NewFile() {
assertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType());
assertTrue(fh.hasMetaDataChanges());
- assertNull(fh.getOldMode());
+ assertSame(FileMode.MISSING, fh.getOldMode());
assertSame(FileMode.REGULAR_FILE, fh.getNewMode());
assertEquals("0000000", fh.getOldId().name());
@@ -179,7 +179,7 @@ public void testParseUnicodeName_DeleteFile() {
assertTrue(fh.hasMetaDataChanges());
assertSame(FileMode.REGULAR_FILE, fh.getOldMode());
- assertNull(fh.getNewMode());
+ assertSame(FileMode.MISSING, fh.getNewMode());
assertEquals("7898192", fh.getOldId().name());
assertEquals("0000000", fh.getNewId().name());
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
index 13eab5f..2c617d3 100644
--- a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
@@ -160,6 +160,7 @@ public void testParse_NoBinary() throws IOException {
assertNotNull(fh.getOldId());
assertNotNull(fh.getNewId());
assertEquals("0000000", fh.getOldId().name());
+ assertSame(FileMode.MISSING, fh.getOldMode());
assertSame(FileMode.REGULAR_FILE, fh.getNewMode());
assertTrue(fh.getNewName().startsWith(
"org.spearce.egit.ui/icons/toolbar/"));
--git a/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java
index f93129d..48d7623 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java
@@ -386,9 +386,11 @@ int parseGitHeaders(int ptr, final int end) {
} else if (match(buf, ptr, DELETED_FILE_MODE) >= 0) {
oldMode = parseFileMode(ptr + DELETED_FILE_MODE.length, eol);
+ newMode = FileMode.MISSING;
changeType = ChangeType.DELETE;
} else if (match(buf, ptr, NEW_FILE_MODE) >= 0) {
+ oldMode = FileMode.MISSING;
newMode = parseFileMode(ptr + NEW_FILE_MODE.length, eol);
changeType = ChangeType.ADD;
--
1.6.1.rc2.306.ge5d5e
^ permalink raw reply related
* [JGIT PATCH 07/12] Allow a stray LF at the end of a hunk
From: Shawn O. Pearce @ 2008-12-12 22:05 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
In-Reply-To: <1229119558-1293-7-git-send-email-spearce@spearce.org>
If a hunk ends and is followed by a stray LF its not worth creating
a warning for. A single extra blank line isn't all that interesting
relative to the other sorts of data we might find at the end of a
patch hunk.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
.../src/org/spearce/jgit/patch/Patch.java | 13 ++++++++++---
1 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java
index 05d034d..51f1fe5 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java
@@ -287,9 +287,16 @@ private int parseHunks(final FileHeader fh, int c, final int end) {
c = h.parseBody(this, end);
h.endOffset = c;
fh.addHunk(h);
- if (c < end && buf[c] != '@' && buf[c] != 'd'
- && match(buf, c, SIG_FOOTER) < 0) {
- warn(buf, c, "Unexpected hunk trailer");
+ if (c < end) {
+ switch (buf[c]) {
+ case '@':
+ case 'd':
+ case '\n':
+ break;
+ default:
+ if (match(buf, c, SIG_FOOTER) < 0)
+ warn(buf, c, "Unexpected hunk trailer");
+ }
}
continue;
}
--
1.6.1.rc2.306.ge5d5e
^ permalink raw reply related
* [JGIT PATCH 09/12] Add test cases for parsing "\ No newline at end of file" style patches
From: Shawn O. Pearce @ 2008-12-12 22:05 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
In-Reply-To: <1229119558-1293-9-git-send-email-spearce@spearce.org>
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
.../tst/org/spearce/jgit/patch/PatchTest.java | 68 ++++++++++++++++++++
.../jgit/patch/testParse_AddNoNewline.patch | 20 ++++++
.../jgit/patch/testParse_FixNoNewline.patch | 20 ++++++
3 files changed, 108 insertions(+), 0 deletions(-)
create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_AddNoNewline.patch
create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_FixNoNewline.patch
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
index c81356b..13eab5f 100644
--- a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
@@ -228,6 +228,74 @@ assertTrue(fh.getNewName().startsWith(
assertEquals(272, fh.getHunks().get(0).getOldImage().getStartLine());
}
+ public void testParse_FixNoNewline() throws IOException {
+ final Patch p = parseTestPatchFile();
+ assertEquals(1, p.getFiles().size());
+ assertTrue(p.getErrors().isEmpty());
+
+ final FileHeader f = p.getFiles().get(0);
+
+ assertEquals("a", f.getNewName());
+ assertEquals(252, f.startOffset);
+
+ assertEquals("2e65efe", f.getOldId().name());
+ assertEquals("f2ad6c7", f.getNewId().name());
+ assertSame(FileHeader.PatchType.UNIFIED, f.getPatchType());
+ assertSame(FileMode.REGULAR_FILE, f.getOldMode());
+ assertSame(FileMode.REGULAR_FILE, f.getNewMode());
+ assertEquals(1, f.getHunks().size());
+ {
+ final HunkHeader h = f.getHunks().get(0);
+ assertSame(f, h.getFileHeader());
+ assertEquals(317, h.startOffset);
+ assertEquals(1, h.getOldImage().getStartLine());
+ assertEquals(1, h.getOldImage().getLineCount());
+ assertEquals(1, h.getNewStartLine());
+ assertEquals(1, h.getNewLineCount());
+
+ assertEquals(0, h.getLinesContext());
+ assertEquals(1, h.getOldImage().getLinesAdded());
+ assertEquals(1, h.getOldImage().getLinesDeleted());
+ assertSame(f.getOldId(), h.getOldImage().getId());
+
+ assertEquals(363, h.endOffset);
+ }
+ }
+
+ public void testParse_AddNoNewline() throws IOException {
+ final Patch p = parseTestPatchFile();
+ assertEquals(1, p.getFiles().size());
+ assertTrue(p.getErrors().isEmpty());
+
+ final FileHeader f = p.getFiles().get(0);
+
+ assertEquals("a", f.getNewName());
+ assertEquals(256, f.startOffset);
+
+ assertEquals("f2ad6c7", f.getOldId().name());
+ assertEquals("c59d9b6", f.getNewId().name());
+ assertSame(FileHeader.PatchType.UNIFIED, f.getPatchType());
+ assertSame(FileMode.REGULAR_FILE, f.getOldMode());
+ assertSame(FileMode.REGULAR_FILE, f.getNewMode());
+ assertEquals(1, f.getHunks().size());
+ {
+ final HunkHeader h = f.getHunks().get(0);
+ assertSame(f, h.getFileHeader());
+ assertEquals(321, h.startOffset);
+ assertEquals(1, h.getOldImage().getStartLine());
+ assertEquals(1, h.getOldImage().getLineCount());
+ assertEquals(1, h.getNewStartLine());
+ assertEquals(1, h.getNewLineCount());
+
+ assertEquals(0, h.getLinesContext());
+ assertEquals(1, h.getOldImage().getLinesAdded());
+ assertEquals(1, h.getOldImage().getLinesDeleted());
+ assertSame(f.getOldId(), h.getOldImage().getId());
+
+ assertEquals(367, h.endOffset);
+ }
+ }
+
private Patch parseTestPatchFile() throws IOException {
final String patchFile = getName() + ".patch";
final InputStream in = getClass().getResourceAsStream(patchFile);
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_AddNoNewline.patch b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_AddNoNewline.patch
new file mode 100644
index 0000000..3060952
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_AddNoNewline.patch
@@ -0,0 +1,20 @@
+From ca4719a4b2d93a469f61d1ddfb3e39ecbabfcd69 Mon Sep 17 00:00:00 2001
+From: Shawn O. Pearce <sop@google.com>
+Date: Fri, 12 Dec 2008 12:35:14 -0800
+Subject: [PATCH] introduce no lf again
+
+---
+ a | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/a b/a
+index f2ad6c7..c59d9b6 100644
+--- a/a
++++ b/a
+@@ -1 +1 @@
+-c
++d
+\ No newline at end of file
+--
+1.6.1.rc2.306.ge5d5e
+
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_FixNoNewline.patch b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_FixNoNewline.patch
new file mode 100644
index 0000000..e8af2e7
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_FixNoNewline.patch
@@ -0,0 +1,20 @@
+From 1beb3ec1fe68ff18b0287396096442e12c34787a Mon Sep 17 00:00:00 2001
+From: Shawn O. Pearce <sop@google.com>
+Date: Fri, 12 Dec 2008 12:29:45 -0800
+Subject: [PATCH] make c and add lf
+
+---
+ a | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/a b/a
+index 2e65efe..f2ad6c7 100644
+--- a/a
++++ b/a
+@@ -1 +1 @@
+-a
+\ No newline at end of file
++c
+--
+1.6.1.rc2.306.ge5d5e
+
--
1.6.1.rc2.306.ge5d5e
^ permalink raw reply related
* [JGIT PATCH 05/12] Refactor the old/pre-image data in HunkHeader to support >1 ancestor
From: Shawn O. Pearce @ 2008-12-12 22:05 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
In-Reply-To: <1229119558-1293-5-git-send-email-spearce@spearce.org>
The "diff --cc" format uses more than one ancestor in each hunk,
so we need to expand the hunk header information in a way that
allows access to the data for each ancestor. This change moves
the information relative to the old/pre-image ancestor in a 2-way
patch into an OldImage object. In a 2-way patch we only have
one OldImage, but in a "diff --cc" patch we will have more than
one of these available in each hunk.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
.../spearce/jgit/patch/EGitPatchHistoryTest.java | 4 +-
.../tst/org/spearce/jgit/patch/PatchTest.java | 36 +++---
| 124 ++++++++++++--------
3 files changed, 95 insertions(+), 69 deletions(-)
diff --git a/org.spearce.jgit.test/exttst/org/spearce/jgit/patch/EGitPatchHistoryTest.java b/org.spearce.jgit.test/exttst/org/spearce/jgit/patch/EGitPatchHistoryTest.java
index d0c2632..b170dc2 100644
--- a/org.spearce.jgit.test/exttst/org/spearce/jgit/patch/EGitPatchHistoryTest.java
+++ b/org.spearce.jgit.test/exttst/org/spearce/jgit/patch/EGitPatchHistoryTest.java
@@ -110,8 +110,8 @@ void onCommit(String cid, byte[] buf) {
assertNotNull("No " + nid, s);
int added = 0, deleted = 0;
for (final HunkHeader h : fh.getHunks()) {
- added += h.getLinesAdded();
- deleted += h.getLinesDeleted();
+ added += h.getOldImage().getLinesAdded();
+ deleted += h.getOldImage().getLinesDeleted();
}
if (s.added == added) {
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
index ebd23b4..4eceeb5 100644
--- a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
@@ -82,14 +82,14 @@ assertSame(FileHeader.PatchType.UNIFIED, fRepositoryConfigTest
final HunkHeader h = fRepositoryConfigTest.getHunks().get(0);
assertSame(fRepositoryConfigTest, h.getFileHeader());
assertEquals(921, h.startOffset);
- assertEquals(109, h.getOldStartLine());
- assertEquals(4, h.getOldLineCount());
+ assertEquals(109, h.getOldImage().getStartLine());
+ assertEquals(4, h.getOldImage().getLineCount());
assertEquals(109, h.getNewStartLine());
assertEquals(11, h.getNewLineCount());
assertEquals(4, h.getLinesContext());
- assertEquals(7, h.getLinesAdded());
- assertEquals(0, h.getLinesDeleted());
+ assertEquals(7, h.getOldImage().getLinesAdded());
+ assertEquals(0, h.getOldImage().getLinesDeleted());
assertEquals(1490, h.endOffset);
}
@@ -105,42 +105,42 @@ assertSame(FileHeader.PatchType.UNIFIED, fRepositoryConfig
final HunkHeader h = fRepositoryConfig.getHunks().get(0);
assertSame(fRepositoryConfig, h.getFileHeader());
assertEquals(1803, h.startOffset);
- assertEquals(236, h.getOldStartLine());
- assertEquals(9, h.getOldLineCount());
+ assertEquals(236, h.getOldImage().getStartLine());
+ assertEquals(9, h.getOldImage().getLineCount());
assertEquals(236, h.getNewStartLine());
assertEquals(9, h.getNewLineCount());
assertEquals(7, h.getLinesContext());
- assertEquals(2, h.getLinesAdded());
- assertEquals(2, h.getLinesDeleted());
+ assertEquals(2, h.getOldImage().getLinesAdded());
+ assertEquals(2, h.getOldImage().getLinesDeleted());
assertEquals(2434, h.endOffset);
}
{
final HunkHeader h = fRepositoryConfig.getHunks().get(1);
assertEquals(2434, h.startOffset);
- assertEquals(300, h.getOldStartLine());
- assertEquals(7, h.getOldLineCount());
+ assertEquals(300, h.getOldImage().getStartLine());
+ assertEquals(7, h.getOldImage().getLineCount());
assertEquals(300, h.getNewStartLine());
assertEquals(7, h.getNewLineCount());
assertEquals(6, h.getLinesContext());
- assertEquals(1, h.getLinesAdded());
- assertEquals(1, h.getLinesDeleted());
+ assertEquals(1, h.getOldImage().getLinesAdded());
+ assertEquals(1, h.getOldImage().getLinesDeleted());
assertEquals(2816, h.endOffset);
}
{
final HunkHeader h = fRepositoryConfig.getHunks().get(2);
assertEquals(2816, h.startOffset);
- assertEquals(954, h.getOldStartLine());
- assertEquals(7, h.getOldLineCount());
+ assertEquals(954, h.getOldImage().getStartLine());
+ assertEquals(7, h.getOldImage().getLineCount());
assertEquals(954, h.getNewStartLine());
assertEquals(7, h.getNewLineCount());
assertEquals(6, h.getLinesContext());
- assertEquals(1, h.getLinesAdded());
- assertEquals(1, h.getLinesDeleted());
+ assertEquals(1, h.getOldImage().getLinesAdded());
+ assertEquals(1, h.getOldImage().getLinesDeleted());
assertEquals(3035, h.endOffset);
}
@@ -177,7 +177,7 @@ assertTrue(fh.getNewName().startsWith(
assertNull(fh.getForwardBinaryHunk());
assertNull(fh.getReverseBinaryHunk());
assertEquals(1, fh.getHunks().size());
- assertEquals(272, fh.getHunks().get(0).getOldStartLine());
+ assertEquals(272, fh.getHunks().get(0).getOldImage().getStartLine());
}
public void testParse_GitBinary() throws IOException {
@@ -222,7 +222,7 @@ assertTrue(fh.getNewName().startsWith(
assertNull(fh.getForwardBinaryHunk());
assertNull(fh.getReverseBinaryHunk());
assertEquals(1, fh.getHunks().size());
- assertEquals(272, fh.getHunks().get(0).getOldStartLine());
+ assertEquals(272, fh.getHunks().get(0).getOldImage().getStartLine());
}
private Patch parseTestPatchFile() throws IOException {
--git a/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java
index c3bd642..842519e 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java
@@ -41,10 +41,49 @@
import static org.spearce.jgit.util.RawParseUtils.nextLF;
import static org.spearce.jgit.util.RawParseUtils.parseBase10;
+import org.spearce.jgit.lib.AbbreviatedObjectId;
import org.spearce.jgit.util.MutableInteger;
/** Hunk header describing the layout of a single block of lines */
public class HunkHeader {
+ /** Details about an old image of the file. */
+ public abstract static class OldImage {
+ /** First line number the hunk starts on in this file. */
+ int startLine;
+
+ /** Total number of lines this hunk covers in this file. */
+ int lineCount;
+
+ /** Number of lines deleted by the post-image from this file. */
+ int nDeleted;
+
+ /** Number of lines added by the post-image not in this file. */
+ int nAdded;
+
+ /** @return first line number the hunk starts on in this file. */
+ public int getStartLine() {
+ return startLine;
+ }
+
+ /** @return total number of lines this hunk covers in this file. */
+ public int getLineCount() {
+ return lineCount;
+ }
+
+ /** @return number of lines deleted by the post-image from this file. */
+ public int getLinesDeleted() {
+ return nDeleted;
+ }
+
+ /** @return number of lines added by the post-image not in this file. */
+ public int getLinesAdded() {
+ return nAdded;
+ }
+
+ /** @return object id of the pre-image file. */
+ public abstract AbbreviatedObjectId getId();
+ }
+
private final FileHeader file;
/** Offset within {@link #file}.buf to the "@@ -" line. */
@@ -53,11 +92,7 @@
/** Position 1 past the end of this hunk within {@link #file}'s buf. */
int endOffset;
- /** First line number in the pre-image file where the hunk starts */
- int oldStartLine;
-
- /** Total number of pre-image lines this hunk covers (context + deleted) */
- int oldLineCount;
+ private final OldImage old;
/** First line number in the post-image file where the hunk starts */
int newStartLine;
@@ -68,15 +103,19 @@
/** Total number of lines of context appearing in this hunk */
int nContext;
- /** Number of lines removed by this hunk */
- int nDeleted;
-
- /** Number of lines added by this hunk */
- int nAdded;
-
HunkHeader(final FileHeader fh, final int offset) {
+ this(fh, offset, new OldImage() {
+ @Override
+ public AbbreviatedObjectId getId() {
+ return fh.getOldId();
+ }
+ });
+ }
+
+ HunkHeader(final FileHeader fh, final int offset, final OldImage oi) {
file = fh;
startOffset = offset;
+ old = oi;
}
/** @return header for the file this hunk applies to */
@@ -84,14 +123,9 @@ public FileHeader getFileHeader() {
return file;
}
- /** @return first line number in the pre-image file where the hunk starts */
- public int getOldStartLine() {
- return oldStartLine;
- }
-
- /** @return total number of pre-image lines this hunk covers */
- public int getOldLineCount() {
- return oldLineCount;
+ /** @return information about the old image mentioned in this hunk. */
+ public OldImage getOldImage() {
+ return old;
}
/** @return first line number in the post-image file where the hunk starts */
@@ -109,28 +143,18 @@ public int getLinesContext() {
return nContext;
}
- /** @return number of lines removed by this hunk */
- public int getLinesDeleted() {
- return nDeleted;
- }
-
- /** @return number of lines added by this hunk */
- public int getLinesAdded() {
- return nAdded;
- }
-
void parseHeader(final int end) {
// Parse "@@ -236,9 +236,9 @@ protected boolean"
//
final byte[] buf = file.buf;
final MutableInteger ptr = new MutableInteger();
ptr.value = nextLF(buf, startOffset, ' ');
- oldStartLine = -parseBase10(buf, ptr.value, ptr);
+ old.startLine = -parseBase10(buf, ptr.value, ptr);
if (buf[ptr.value] == ',')
- oldLineCount = parseBase10(buf, ptr.value + 1, ptr);
+ old.lineCount = parseBase10(buf, ptr.value + 1, ptr);
else {
- oldLineCount = oldStartLine;
- oldStartLine = 0;
+ old.lineCount = old.startLine;
+ old.startLine = 0;
}
newStartLine = parseBase10(buf, ptr.value + 1, ptr);
@@ -146,8 +170,8 @@ int parseBody(final Patch script, final int end) {
final byte[] buf = file.buf;
int c = nextLF(buf, startOffset), last = c;
- nDeleted = 0;
- nAdded = 0;
+ old.nDeleted = 0;
+ old.nAdded = 0;
SCAN: for (; c < end; last = c, c = nextLF(buf, c)) {
switch (buf[c]) {
@@ -157,11 +181,11 @@ int parseBody(final Patch script, final int end) {
continue;
case '-':
- nDeleted++;
+ old.nDeleted++;
continue;
case '+':
- nAdded++;
+ old.nAdded++;
continue;
case '\\': // Matches "\ No newline at end of file"
@@ -172,33 +196,35 @@ int parseBody(final Patch script, final int end) {
}
}
- if (last < end && nContext + nDeleted - 1 == oldLineCount
- && nContext + nAdded == newLineCount
+ if (last < end && nContext + old.nDeleted - 1 == old.lineCount
+ && nContext + old.nAdded == newLineCount
&& match(buf, last, Patch.SIG_FOOTER) >= 0) {
// This is an extremely common occurrence of "corruption".
// Users add footers with their signatures after this mark,
// and git diff adds the git executable version number.
// Let it slide; the hunk otherwise looked sound.
//
- nDeleted--;
+ old.nDeleted--;
return last;
}
- if (nContext + nDeleted < oldLineCount) {
- final int missingCount = oldLineCount - (nContext + nDeleted);
+ if (nContext + old.nDeleted < old.lineCount) {
+ final int missingCount = old.lineCount - (nContext + old.nDeleted);
script.error(buf, startOffset, "Truncated hunk, at least "
+ missingCount + " old lines is missing");
- } else if (nContext + nAdded < newLineCount) {
- final int missingCount = newLineCount - (nContext + nAdded);
+ } else if (nContext + old.nAdded < newLineCount) {
+ final int missingCount = newLineCount - (nContext + old.nAdded);
script.error(buf, startOffset, "Truncated hunk, at least "
+ missingCount + " new lines is missing");
- } else if (nContext + nDeleted > oldLineCount
- || nContext + nAdded > newLineCount) {
- script.warn(buf, startOffset, "Hunk header " + oldLineCount + ":"
- + newLineCount + " does not match body line count of "
- + (nContext + nDeleted) + ":" + (nContext + nAdded));
+ } else if (nContext + old.nDeleted > old.lineCount
+ || nContext + old.nAdded > newLineCount) {
+ final String oldcnt = old.lineCount + ":" + newLineCount;
+ final String newcnt = (nContext + old.nDeleted) + ":"
+ + (nContext + old.nAdded);
+ script.warn(buf, startOffset, "Hunk header " + oldcnt
+ + " does not match body line count of " + newcnt);
}
return c;
--
1.6.1.rc2.306.ge5d5e
^ permalink raw reply related
* [JGIT PATCH 04/12] Allow FileHeader to create its HunkHeader children
From: Shawn O. Pearce @ 2008-12-12 22:05 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
In-Reply-To: <1229119558-1293-4-git-send-email-spearce@spearce.org>
By using a factory method on FileHeader we can later subclass the
FileHeader class to handle "diff --cc" style patches, and let it
create its own subclass of HunkHeader to handle the specialized
form of the n-way diff.
The getParentCount() method is hard-coded to return 1 in the 2-way
diff case as there is exactly one parent. But in a "diff --cc" we
need to verify the hunk header has the same number of parents as
the file header in order to parse the hunk. So a subclass of the
FileHeader would need to override getParentCount() to return the
actual number of '@' symbols (less 1) that should appear in each
hunk header line. (E.g. a 3-way diff shows "@@@ -" so the parent
count should be 2.)
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
| 8 ++++++++
.../src/org/spearce/jgit/patch/Patch.java | 4 ++--
2 files changed, 10 insertions(+), 2 deletions(-)
--git a/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java
index 5fe2acf..f93129d 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java
@@ -169,6 +169,10 @@ FileHeader(final byte[] b, final int offset) {
patchType = PatchType.UNIFIED;
}
+ int getParentCount() {
+ return 1;
+ }
+
/**
* Get the old name associated with this file.
* <p>
@@ -274,6 +278,10 @@ void addHunk(final HunkHeader h) {
hunks.add(h);
}
+ HunkHeader newHunkHeader(final int offset) {
+ return new HunkHeader(this, offset);
+ }
+
/** @return if a {@link PatchType#GIT_BINARY}, the new-image delta/literal */
public BinaryHunk getForwardBinaryHunk() {
return forwardBinaryHunk;
diff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java
index 77ae02f..05d034d 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java
@@ -281,8 +281,8 @@ private int parseHunks(final FileHeader fh, int c, final int end) {
if (match(buf, c, NEW_NAME) >= 0)
break;
- if (isHunkHdr(buf, c, end) == 1) {
- final HunkHeader h = new HunkHeader(fh, c);
+ if (isHunkHdr(buf, c, end) == fh.getParentCount()) {
+ final HunkHeader h = fh.newHunkHeader(c);
h.parseHeader(end);
c = h.parseBody(this, end);
h.endOffset = c;
--
1.6.1.rc2.306.ge5d5e
^ permalink raw reply related
* [JGIT PATCH 03/12] Add a simple toString to FormatError to facilitate debugging
From: Shawn O. Pearce @ 2008-12-12 22:05 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
In-Reply-To: <1229119558-1293-3-git-send-email-spearce@spearce.org>
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
.../src/org/spearce/jgit/patch/FormatError.java | 14 ++++++++++++++
1 files changed, 14 insertions(+), 0 deletions(-)
diff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/FormatError.java b/org.spearce.jgit/src/org/spearce/jgit/patch/FormatError.java
index e6f0a03..ab75c63 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/patch/FormatError.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/FormatError.java
@@ -92,4 +92,18 @@ public String getLineText() {
final int eol = RawParseUtils.nextLF(buf, offset);
return RawParseUtils.decode(Constants.CHARSET, buf, offset, eol);
}
+
+ @Override
+ public String toString() {
+ final StringBuilder r = new StringBuilder();
+ r.append(getSeverity().name().toLowerCase());
+ r.append(": at offset ");
+ r.append(getOffset());
+ r.append(": ");
+ r.append(getMessage());
+ r.append("\n");
+ r.append(" in ");
+ r.append(getLineText());
+ return r.toString();
+ }
}
--
1.6.1.rc2.306.ge5d5e
^ permalink raw reply related
* [JGIT PATCH 02/12] Add tests to cover more methods of BinaryHunk
From: Shawn O. Pearce @ 2008-12-12 22:05 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
In-Reply-To: <1229119558-1293-2-git-send-email-spearce@spearce.org>
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
.../tst/org/spearce/jgit/patch/PatchTest.java | 16 ++++++++++++----
1 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
index 5850364..ebd23b4 100644
--- a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
@@ -199,10 +199,18 @@ assertTrue(fh.getNewName().startsWith(
assertTrue(fh.getHunks().isEmpty());
assertTrue(fh.hasMetaDataChanges());
- assertNotNull(fh.getForwardBinaryHunk());
- assertNotNull(fh.getReverseBinaryHunk());
- assertEquals(binsizes[i], fh.getForwardBinaryHunk().getSize());
- assertEquals(0, fh.getReverseBinaryHunk().getSize());
+ final BinaryHunk fwd = fh.getForwardBinaryHunk();
+ final BinaryHunk rev = fh.getReverseBinaryHunk();
+ assertNotNull(fwd);
+ assertNotNull(rev);
+ assertEquals(binsizes[i], fwd.getSize());
+ assertEquals(0, rev.getSize());
+
+ assertSame(fh, fwd.getFileHeader());
+ assertSame(fh, rev.getFileHeader());
+
+ assertSame(BinaryHunk.Type.LITERAL_DEFLATED, fwd.getType());
+ assertSame(BinaryHunk.Type.LITERAL_DEFLATED, rev.getType());
}
final FileHeader fh = p.getFiles().get(4);
--
1.6.1.rc2.306.ge5d5e
^ permalink raw reply related
* [JGIT PATCH 01/12] Assert the HunkHeader.getFileHeader returns the right file
From: Shawn O. Pearce @ 2008-12-12 22:05 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
In-Reply-To: <1229119558-1293-1-git-send-email-spearce@spearce.org>
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
.../tst/org/spearce/jgit/patch/PatchTest.java | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
index 7c69fff..5850364 100644
--- a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java
@@ -80,6 +80,7 @@ assertSame(FileHeader.PatchType.UNIFIED, fRepositoryConfigTest
assertEquals(1, fRepositoryConfigTest.getHunks().size());
{
final HunkHeader h = fRepositoryConfigTest.getHunks().get(0);
+ assertSame(fRepositoryConfigTest, h.getFileHeader());
assertEquals(921, h.startOffset);
assertEquals(109, h.getOldStartLine());
assertEquals(4, h.getOldLineCount());
@@ -102,6 +103,7 @@ assertSame(FileHeader.PatchType.UNIFIED, fRepositoryConfig
assertEquals(3, fRepositoryConfig.getHunks().size());
{
final HunkHeader h = fRepositoryConfig.getHunks().get(0);
+ assertSame(fRepositoryConfig, h.getFileHeader());
assertEquals(1803, h.startOffset);
assertEquals(236, h.getOldStartLine());
assertEquals(9, h.getOldLineCount());
--
1.6.1.rc2.306.ge5d5e
^ permalink raw reply related
* [JGIT PATCH 00/12] Patch API bug fixes and diff --cc suport
From: Shawn O. Pearce @ 2008-12-12 22:05 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
This series addes new unit tests for the patch API, closing some
gaps in our test coverage. Some of those tests identified bugs in
the implementation, and those are now fixed.
The final patch in the series adds support for "diff -cc" patches,
used to show an "evil merge" commit's conflict resolution.
Shawn O. Pearce (12):
Assert the HunkHeader.getFileHeader returns the right file
Add tests to cover more methods of BinaryHunk
Add a simple toString to FormatError to facilitate debugging
Allow FileHeader to create its HunkHeader children
Refactor the old/pre-image data in HunkHeader to support >1 ancestor
Assert the ChunkHeader.OldImage.getId uses FileHeader.getOldImage
Allow a stray LF at the end of a hunk
Fix HunkHeader start line when parsing "@@ -1 +1 @@" style headers
Add test cases for parsing "\ No newline at end of file" style
patches
Use FileMode.MISSING when a file is added or deleted rather than null
Add a test for delta binary patch parsing and fix a bug in it
Add support for parsing "diff --cc" style patches
.../spearce/jgit/patch/EGitPatchHistoryTest.java | 4 +-
.../tst/org/spearce/jgit/patch/FileHeaderTest.java | 4 +-
.../org/spearce/jgit/patch/PatchCcErrorTest.java | 97 +++++++++
.../tst/org/spearce/jgit/patch/PatchCcTest.java | 200 ++++++++++++++++++
.../tst/org/spearce/jgit/patch/PatchTest.java | 165 +++++++++++++--
.../jgit/patch/testError_CcTruncatedOld.patch | 24 +++
.../jgit/patch/testParse_AddNoNewline.patch | 20 ++
.../jgit/patch/testParse_CcDeleteFile.patch | 12 +
.../spearce/jgit/patch/testParse_CcNewFile.patch | 14 ++
.../jgit/patch/testParse_FixNoNewline.patch | 20 ++
.../jgit/patch/testParse_GitBinaryDelta.patch | 21 ++
...nary.patch => testParse_GitBinaryLiteral.patch} | 0
.../spearce/jgit/patch/testParse_OneFileCc.patch | 27 +++
.../src/org/spearce/jgit/patch/BinaryHunk.java | 2 +-
.../org/spearce/jgit/patch/CombinedFileHeader.java | 213 ++++++++++++++++++++
.../org/spearce/jgit/patch/CombinedHunkHeader.java | 191 ++++++++++++++++++
.../src/org/spearce/jgit/patch/FileHeader.java | 64 ++++--
.../src/org/spearce/jgit/patch/FormatError.java | 14 ++
.../src/org/spearce/jgit/patch/HunkHeader.java | 134 +++++++-----
.../src/org/spearce/jgit/patch/Patch.java | 24 ++-
20 files changed, 1134 insertions(+), 116 deletions(-)
create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchCcErrorTest.java
create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchCcTest.java
create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_CcTruncatedOld.patch
create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_AddNoNewline.patch
create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_CcDeleteFile.patch
create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_CcNewFile.patch
create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_FixNoNewline.patch
create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_GitBinaryDelta.patch
rename org.spearce.jgit.test/tst/org/spearce/jgit/patch/{testParse_GitBinary.patch => testParse_GitBinaryLiteral.patch} (100%)
create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_OneFileCc.patch
create mode 100644 org.spearce.jgit/src/org/spearce/jgit/patch/CombinedFileHeader.java
create mode 100644 org.spearce.jgit/src/org/spearce/jgit/patch/CombinedHunkHeader.java
^ permalink raw reply
* [PATCH 1/2] mergetool: Add prompt to continue after failing to merge a file
From: Charles Bailey @ 2008-12-12 21:48 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Jeff King, Andreas Ericsson, Theodore Ts'o,
William Pursell
In-Reply-To: <1229118521-22923-1-git-send-email-charles@hashpling.org>
This option stops git mergetool from aborting at the first failed merge.
After a failed merge the user will be prompted to indicated whether he
wishes to continue with attempting to merge subsequent paths or to
abort.
This allows some additional use patterns. Merge conflicts can now be
previewed one at time and merges can also be skipped so that they can be
performed in a later pass.
Signed-off-by: Charles Bailey <charles@hashpling.org>
---
git-mergetool.sh | 52 +++++++++++++++++++++++++++++++++++++++++++---------
1 files changed, 43 insertions(+), 9 deletions(-)
diff --git a/git-mergetool.sh b/git-mergetool.sh
index 507028f..5144971 100755
--- a/git-mergetool.sh
+++ b/git-mergetool.sh
@@ -70,16 +70,16 @@ resolve_symlink_merge () {
git checkout-index -f --stage=2 -- "$MERGED"
git add -- "$MERGED"
cleanup_temp_files --save-backup
- return
+ return 0
;;
[rR]*)
git checkout-index -f --stage=3 -- "$MERGED"
git add -- "$MERGED"
cleanup_temp_files --save-backup
- return
+ return 0
;;
[aA]*)
- exit 1
+ return 1
;;
esac
done
@@ -97,15 +97,15 @@ resolve_deleted_merge () {
[mMcC]*)
git add -- "$MERGED"
cleanup_temp_files --save-backup
- return
+ return 0
;;
[dD]*)
git rm -- "$MERGED" > /dev/null
cleanup_temp_files
- return
+ return 0
;;
[aA]*)
- exit 1
+ return 1
;;
esac
done
@@ -137,7 +137,7 @@ merge_file () {
else
echo "$MERGED: file does not need merging"
fi
- exit 1
+ return 1
fi
ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')"
@@ -269,7 +269,7 @@ merge_file () {
if test "$status" -ne 0; then
echo "merge of $MERGED failed" 1>&2
mv -- "$BACKUP" "$MERGED"
- exit 1
+ return 1
fi
if test "$merge_keep_backup" = "true"; then
@@ -280,6 +280,7 @@ merge_file () {
git add -- "$MERGED"
cleanup_temp_files
+ return 0
}
prompt=$(git config --bool mergetool.prompt || echo true)
@@ -350,6 +351,22 @@ init_merge_tool_path() {
fi
}
+prompt_after_failed_merge() {
+ while true; do
+ printf "Continue merging other unresolved paths (y/n) ? "
+ read ans
+ case "$ans" in
+
+ [yY]*)
+ return 0
+ ;;
+
+ [nN]*)
+ return 1
+ ;;
+ esac
+ done
+}
if test -z "$merge_tool"; then
merge_tool=`git config merge.tool`
@@ -409,6 +426,8 @@ else
fi
fi
+last_status=0
+rollup_status=0
if test $# -eq 0 ; then
files=`git ls-files -u | sed -e 's/^[^ ]* //' | sort -u`
@@ -422,14 +441,29 @@ if test $# -eq 0 ; then
sort -u |
while IFS= read i
do
+ if test $last_status -ne 0; then
+ prompt_after_failed_merge < /dev/tty || exit 1
+ fi
printf "\n"
merge_file "$i" < /dev/tty > /dev/tty
+ last_status=$?
+ if test $last_status -ne 0; then
+ rollup_status=1
+ fi
done
else
while test $# -gt 0; do
+ if test $last_status -ne 0; then
+ prompt_after_failed_merge || exit 1
+ fi
printf "\n"
merge_file "$1"
+ last_status=$?
+ if test $last_status -ne 0; then
+ rollup_status=1
+ fi
shift
done
fi
-exit 0
+
+exit $rollup_status
--
1.6.1.rc1.342.g83b24d
^ permalink raw reply related
* [PATCH 2/2] mergetool: Don't keep temporary merge files unless told to
From: Charles Bailey @ 2008-12-12 21:48 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Jeff King, Andreas Ericsson, Theodore Ts'o,
William Pursell
In-Reply-To: <1229118521-22923-2-git-send-email-charles@hashpling.org>
This changes git mergetool to remove the temporary files used to invoke
the merge tool even if it returns non-zero.
This also adds a configuration option (mergetool.keepTemporaries) to
retain the previous behaviour if desired.
Signed-off-by: Charles Bailey <charles@hashpling.org>
---
Documentation/config.txt | 7 +++++++
git-mergetool.sh | 6 ++++++
2 files changed, 13 insertions(+), 0 deletions(-)
diff --git a/Documentation/config.txt b/Documentation/config.txt
index bc5642d..3d5a8df 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -976,6 +976,13 @@ mergetool.keepBackup::
is set to `false` then this file is not preserved. Defaults to
`true` (i.e. keep the backup files).
+mergetool.keepTemporaries::
+ When invoking a custom merge tool, git uses a set of temporary
+ files to pass to the tool. If the tool returns an error and this
+ variable is set to `true`, then these temporary files will be
+ preserved, otherwise they will be removed after the tool has
+ exited. Defaults to `false`.
+
mergetool.prompt::
Prompt before each invocation of the merge resolution program.
diff --git a/git-mergetool.sh b/git-mergetool.sh
index 5144971..f04240d 100755
--- a/git-mergetool.sh
+++ b/git-mergetool.sh
@@ -269,6 +269,11 @@ merge_file () {
if test "$status" -ne 0; then
echo "merge of $MERGED failed" 1>&2
mv -- "$BACKUP" "$MERGED"
+
+ if test "$merge_keep_temporaries" = "false"; then
+ cleanup_temp_files
+ fi
+
return 1
fi
@@ -415,6 +420,7 @@ else
init_merge_tool_path "$merge_tool"
merge_keep_backup="$(git config --bool merge.keepBackup || echo true)"
+ merge_keep_temporaries="$(git config --bool mergetool.keepTemporaries || echo false)"
if test -z "$merge_tool_cmd" && ! type "$merge_tool_path" > /dev/null 2>&1; then
echo "The merge tool $merge_tool is not available as '$merge_tool_path'"
--
1.6.1.rc1.342.g83b24d
^ permalink raw reply related
* Updates to the previous mergetool patches
From: Charles Bailey @ 2008-12-12 21:48 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Jeff King, Andreas Ericsson, Theodore Ts'o,
William Pursell
These two patches go on top of 682b451f and replace the 'DONTMERGE'
commit: 09b511016. If it would be easier if I cooked these in a
different way then let me know.
I've implemented a replacement for the "keep going" option which is now
to prompt after a failed merge to see if the user wants to continue
merging the remaining paths. This gets rid of a need for an option of
configuration option.
I haven't implemented any change to the logic as to whether to pause
before invoking the merge tool based on which merge tool is being used.
The added tests for the return status of the merge are slightly less
neat than I would have liked, but I think are necessary. I test after
invoking the merge tool to make sure that the "roll-up" status is
correct, and before invoking the next tool to see if the user wants to
continue despite the failure of the previous merge.
Only testing after the merge tool invocation can end up with a "do you
want to continue" before mergetool has determined that there are, in
fact, no more files to merge. Only testing before the merge means that
the status of the last merge doesn't contribute to the script's return
value. I don't know how many people rely on the return value of git
mergetool, but why not keep it consistent?
The second patch makes optional the retention of temporary files on a
failed merge optional. It seperates out the behaviour change that I'd
forgotten I'd left in the previous version of the "-k" patch.
^ permalink raw reply
* Re: [PATCH] Simplified GIT usage guide
From: Chris Friesen @ 2008-12-12 21:34 UTC (permalink / raw)
To: Jeff Garzik; +Cc: David Howells, torvalds, git, linux-kernel
In-Reply-To: <4942C2D1.4090309@garzik.org>
Jeff Garzik wrote:
> What do you feel is missing from the Kernel Hackers' Guide to Git? :)
>
> http://linux.yyz.us/git-howto.html
This is useful. I also keep a bookmark to the Git User's Manual:
http://www.kernel.org/pub/software/scm/git/docs/user-manual.html
Chris
^ permalink raw reply
* Re: Unable to index file
From: Linus Torvalds @ 2008-12-12 20:33 UTC (permalink / raw)
To: Ramon Tayag; +Cc: git
In-Reply-To: <f25d5ad20812121227o77cf2bw34e6bc15d9801215@mail.gmail.com>
On Sat, 13 Dec 2008, Ramon Tayag wrote:
>
> I'm on Ubuntu 8.10. The files I'm working on live on an NTFS drive
> (my "storage" drive; yes, I still have to boot into Windows). If it
> being on NTFS makes a difference I'll try this on ext3 and let you
> know what happens :)
Ok, it's almost certainly the NTFS part.
And at the same time, I'm almost certain that my patch should fix it and
is the right thing for git to do anyway.
Linus
^ permalink raw reply
* Re: Unable to index file
From: Ramon Tayag @ 2008-12-12 20:27 UTC (permalink / raw)
To: Linus Torvalds; +Cc: git
In-Reply-To: <alpine.LFD.2.00.0812120956050.3340@localhost.localdomain>
Oh thank you for your very detailed explanation. I was bothering
people on #rubyonrails and #git but got no answers.
I'm on Ubuntu 8.10. The files I'm working on live on an NTFS drive
(my "storage" drive; yes, I still have to boot into Windows). If it
being on NTFS makes a difference I'll try this on ext3 and let you
know what happens :)
Thanks,
Ramon Tayag
On Sat, Dec 13, 2008 at 2:07 AM, Linus Torvalds
<torvalds@linux-foundation.org> wrote:
>
>
> On Fri, 12 Dec 2008, Ramon Tayag wrote:
>>
>> I've come across a problem that I don't believe lies in Rails. You
>> needn't be familiar, I think, with Rails to see what's wrong.
>>
>> I can't seem to add the files that are in
>> http://dev.rubyonrails.org/archive/rails_edge.zip
>>
>> 1) Unpack the zip
>> 2) Initialize a git repo inside the folder that was unpacked
>> 3) git add .
>>
>> See the errors.. :o http://pastie.org/337571
>
> What platform/filesystem is this?
>
> Git is rather particular about symlinks, and it looks like your platform
> does something odd, and that makes git unhappy about your symlink.
>
> In particular:
>
> ls -l vendor/rails/actionpack/test/fixtures/layout_tests/layouts/
> ...
> lrwxrwxrwx 1 root root 48 2008-12-12 18:22 symlinked -> ../../symlink_parent
>
> notice how the symlink content is "../../symlink_parent", but then take a
> look at the _size_ of the symlink: 48 bytes.
>
> Git expects the lstat() information to match the return from readlink(),
> and it doesn't.
>
> For exact details, see "index_path()" in sha1_file.c:
>
> case S_IFLNK:
> len = xsize_t(st->st_size);
> target = xmalloc(len + 1);
> if (readlink(path, target, len + 1) != st->st_size) {
> char *errstr = strerror(errno);
>
> ie we consider it an error if we get less than st_size characters back
> from readlink().
>
> Now, admittedly git is probably being really annoyingly anal about this
> all, and we probably should loosen the restrictions on it a bit, but I'd
> like to know why it happens. I cannot recall this having been reported
> before, so it's some specific filesystem or OS that causes this, I think.
>
> Linus
>
^ permalink raw reply
* Re: GitLib2
From: Shawn O. Pearce @ 2008-12-12 20:18 UTC (permalink / raw)
To: JD Guzman; +Cc: git
In-Reply-To: <001c01c95c96$901f25e0$b05d71a0$@com>
JD Guzman <jd@jdguzman.com> wrote:
> I've done several searches on libgit2 and have found quite a few references
> to it. I was wondering is there a way to download the code or is it not
> available to the general public yet?
git clone http://www.spearce.org/projects/scm/libgit2/libgit2.git
It has so few features we haven't yet made it more public.
--
Shawn.
^ permalink raw reply
* GitLib2
From: JD Guzman @ 2008-12-12 20:16 UTC (permalink / raw)
To: git
I've done several searches on libgit2 and have found quite a few references
to it. I was wondering is there a way to download the code or is it not
available to the general public yet?
Regards
^ permalink raw reply
* Re: [PATCH] Simplified GIT usage guide
From: Nicolas Pitre @ 2008-12-12 20:07 UTC (permalink / raw)
To: David Howells; +Cc: torvalds, git, linux-kernel
In-Reply-To: <20081212182827.28408.40963.stgit@warthog.procyon.org.uk>
Another obvious correction...
On Fri, 12 Dec 2008, David Howells wrote:
> + A commit object will typically refer to one base commit when someone has
> + merely committed some changes on top of the current state, and two base
> + commits when a couple of trees have been merged.
If you have two bases, then only two trees were merged together. If you
merged a "couple" of trees, then a "couple" of bases are registered.
Nicolas
^ permalink raw reply
* Re: [PATCH] Simplified GIT usage guide
From: Jeff Garzik @ 2008-12-12 20:00 UTC (permalink / raw)
To: David Howells; +Cc: torvalds, git, linux-kernel
In-Reply-To: <20081212182827.28408.40963.stgit@warthog.procyon.org.uk>
David Howells wrote:
> Add a guide to using GIT's simpler features.
>
> Signed-off-by: David Howells <dhowells@redhat.com>
> ---
>
> Documentation/git-haters-guide.txt | 1283 ++++++++++++++++++++++++++++++++++++
> 1 files changed, 1283 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/git-haters-guide.txt
What do you feel is missing from the Kernel Hackers' Guide to Git? :)
http://linux.yyz.us/git-howto.html
Jeff
^ permalink raw reply
* Re: [PATCH] Simplified GIT usage guide
From: Aidan Van Dyk @ 2008-12-12 19:40 UTC (permalink / raw)
To: sverre
Cc: David Howells, Johannes Schindelin, torvalds, git, linux-kernel,
Miklos Vajna
In-Reply-To: <bd6139dc0812121124j49c80bbdhca22ddbdabc14cee@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 945 bytes --]
* Sverre Rabbelier <alturin@gmail.com> [081201 00:00]:
> On Fri, Dec 12, 2008 at 20:12, David Howells <dhowells@redhat.com> wrote:
> > In my opinion, it's much easier to deal with if you can visualise how it
> > works
>
> Ah, this is of course why they teach you how an engine works at the
> driving schools!
No, but all the people I've taught to drive have suddenly "got it" after
a gentle introduction as to how the clutch clutch and engine speed work
together in the transmission to accellerate/decellerate the car...
So, a bit of "understanding" of what's actually going on helped them,
not the "push that pedal towards the floor and pop the other one out!".
But... everyone learns differently...
a.
--
Aidan Van Dyk Create like a god,
aidan@highrise.ca command like a king,
http://www.highrise.ca/ work like a slave.
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* Re: [PATCH] Simplified GIT usage guide
From: J. Bruce Fields @ 2008-12-12 19:47 UTC (permalink / raw)
To: David Howells; +Cc: torvalds, git, linux-kernel
In-Reply-To: <20081212182827.28408.40963.stgit@warthog.procyon.org.uk>
On Fri, Dec 12, 2008 at 06:28:27PM +0000, David Howells wrote:
> Add a guide to using GIT's simpler features.
>
> Signed-off-by: David Howells <dhowells@redhat.com>
Just a couple random thoughts:
- The advantage of adding this to the kernel tree is that you
can tailor it for a more specific audience (kernel developers
and testers). A lot of this (e.g. the object-database
discussion) seems to be generic introduction-to-git stuff.
Is there some canonical external documentation you could refer
to for that stuff, that would allow you to get more quickly to
the more tailored information? If not, is there something you
could improve to the point where you *would* be comfortable
referring to it?
- How much overlap is there with
Documentation/development-process/7.AdvancedTopics? Should
there be cross-references between the two?
There's an awful lot of introductions to git out there now (and I've got
my own share of the blame).
--b.
> ---
>
> Documentation/git-haters-guide.txt | 1283 ++++++++++++++++++++++++++++++++++++
> 1 files changed, 1283 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/git-haters-guide.txt
>
>
> diff --git a/Documentation/git-haters-guide.txt b/Documentation/git-haters-guide.txt
> new file mode 100644
> index 0000000..51e4dac
> --- /dev/null
> +++ b/Documentation/git-haters-guide.txt
> @@ -0,0 +1,1283 @@
> + ===================================
> + THE GIT HATER'S GUIDE TO THE GALAXY
> + ===================================
> +
> +By David Howells <dhowells@redhat.com>
> +
> +Contents:
> +
> + (*) Introduction.
> +
> + - Disclaimer.
> +
> + (*) Overview of GIT.
> +
> + - Git objects.
> + - Symbolic pointers.
> + - The GIT tree.
> + - GIT trees after merging.
> +
> + (*) Downloading upstream trees.
> +
> + - Local mirroring.
> + - Automatic updates.
> + - Using your local mirror.
> +
> + (*) Accessing the repository.
> +
> + - Viewing the history.
> + - Viewing a commit.
> + - Viewing source differences.
> +
> + (*) Making changes.
> +
> + - Applying patches.
> + - Applying formatted patches.
> + - Incorporating GIT trees.
> +
> + (*) Amending and reverting changes.
> +
> + - Amending committed changes.
> + - Discarding committed changes.
> + - Reverting committed changes.
> +
> + (*) Publishing changes by GIT tree.
> +
> + - Setting up.
> + - Updating your development tree.
> + - Publishing your changes.
> +
> + (*) Manually merging failed fetches.
> +
> + (*) Locating bugs.
> +
> + - Bisection.
> + - Blame.
> +
> +
> +============
> +INTRODUCTION
> +============
> +
> +So, you want to do some Linux kernel development? And you hear there's this
> +piece of software called 'GIT' that you probably ought to be using when dealing
> +with the kernel community? Then you find out that not only was Linux started
> +by this Linus Torvalds person, but GIT was too! Perhaps it doesn't seem fair:
> +Linus has not just _one_ huge piece of software named after himself, but _two_!
> +And on top of that, globe spanning hardware vendors just queue up to give him
> +all the herring he can eat!!
> +
> +Then you look at webpages about GIT. You look at the manpages! You run the
> +commands with --help! And you *still* don't know how to do anything complex
> +with it!! You feel certain that there's some secret rite you have to perform
> +to become a GIT initiate - probably something involving two goats, an altar and
> +a full moon - oh, and lots of beer (we *are* talking about kernel developers
> +after all).
> +
> +Then you ask around, and people look at you blankly, hedge or say that it's
> +easy and obvious (they should know - they wrote the damned thing). You realise
> +that the manpages are more an aide-memoire and that what you really want is
> +some sort of crib sheet; something that can hold your hand whilst you cut and
> +paste things from of it until you can see the point.
> +
> +Well, let's see if I can help...
> +
> +
> +DISCLAIMER
> +----------
> +
> +I don't really know what I'm doing with GIT either. I'm not sure anyone really
> +does, apart from Linus (and then only after some strange Finnish snack
> +involving red and white mushrooms). If you'd pause to wonder why things are
> +like they are, you'd realise that only someone totally barking would try to
> +write a kernel in the first place... and then it'd dawn on you what the mental
> +state must be like of someone who'd try writing something like a source code
> +management system from scratch... and then you'd consider what it must take to
> +be someone who'd do *both*.
> +
> +
> +===============
> +OVERVIEW OF GIT
> +===============
> +
> +GIT is a source code management system. You give it your sources to retain,
> +and it manages the history of all the changes and provides you with a set of
> +tools by which that history can be viewed, extracted and extended.
> +
> +GIT is unusual in its design in that the objects it retains are referred to by
> +hashes of their content. Because it is mathematically possible for object IDs
> +to collide, large hash IDs are used to reduce the probability of a collision.
> +If the content of an object changes, rather than updating the existing object,
> +GIT will create a new object with a new hash ID. Objects are _invariant_.
> +
> +The GIT database in a GIT tree has two sets of data:
> +
> + (1) A set of objects, indexed by the object hash ID.
> +
> + (2) A set of symbolic object tree heads, as object hash IDs.
> +
> +
> +GIT OBJECTS
> +-----------
> +
> +There are three basic types of object:
> +
> + (1) File objects.
> +
> + A file object contains the contents of a source file and the attributes of
> + that file (such as file mode).
> +
> + (2) Directory objects.
> +
> + A directory object contains the attributes of that directory plus a list
> + of file and directory objects that are members of this directory. The
> + list includes the names of the entries within that directory and the
> + object ID of each object.
> +
> + (3) Commit objects.
> +
> + A commit object contains the attribute of that commit (the author and the
> + date for instance), a textual description of the change imposed by that
> + commit as provided by the committer, a list of object IDs for the commits
> + on which this commit is based, and the object ID of the root directory
> + object representing the result of this commit.
> +
> + Note that a commit does not literally describe the changes that have been
> + made in the way that, say, a diff file does; it merely carries the current
> + state of the sources after that change, and points to the commits that
> + describe the state of the sources before that change. GIT's tools then
> + infer the changes when asked.
> +
> + A commit object will typically refer to one base commit when someone has
> + merely committed some changes on top of the current state, and two base
> + commits when a couple of trees have been merged.
> +
> +Because objects are invariant, and because they can thus be referred to by a
> +hash of their contents, objects can be shared between trees simply by using the
> +same object ID in two different places. This allows objects to be compared to
> +see whether they are the same thing or not simply by comparing the object ID.
> +
> +
> +SYMBOLIC POINTERS
> +-----------------
> +
> +GIT retains its historical information in a set of overlapping, shared trees,
> +but the notion of where a tree starts isn't really a primary concept with GIT.
> +What it has instead is a number of symbolic pointers to commits within the tree
> +that are considered to be of some sort of significance. These are called
> +'heads' and include:
> +
> + (1) The base for the current working state of the checked out sources (HEAD).
> +
> + (2) Branches (by branch name).
> +
> + (3) Tags (by tag name).
> +
> + (4) Merge base (for incomplete merges).
> +
> + (5) Points of interest, such as those that pertain to a git fetch (FETCH_HEAD
> + and ORIG_HEAD).
> +
> + (6) Bisection points (when bisection is being used to find a bug).
> +
> +In essence, these symbolic pointers are just names or conventions for
> +particular roots in the tree. They are a name that maps to the object ID of a
> +commit object.
> +
> +Some of them have special meanings, such as branches, that can be configured to
> +behave in various ways under certain conditions (such as when a git fetch is
> +performed).
> +
> +
> +THE GIT TREE
> +------------
> +
> +The GIT tree in its simplest terms is a backbone of commits that point to
> +directories that point to files. To give a simple example of the commit
> +process, consider the sources for a project that contains one directory, D,
> +which contains three files, F1, F2 and F3.
> +
> +This could then be committed into GIT to begin a project, in this case as
> +commit C0. This would hold version D0 of the directory, and versions F1A, F2A
> +and F3A of the three files, and the GIT repository HEAD pointer would point to
> +C0:
> +
> + +-----+
> + +-->| F3A |
> + | +-----+
> + |
> + +-----+ +-----+ | +-----+
> + HEAD--->| C0 |------->| D0 |------+-->| F2A |
> + +-----+ +-----+ | +-----+
> + |
> + | +-----+
> + +-->| F1A |
> + +-----+
> +
> +Now imagine that someone changes file F2 and commits the change. F1A and F3A
> +are still useful, and can be shared by the new view of the world, but F2 is now
> +on a new version, F2B. The old directory object, D0, pointed to F2A, so that
> +cannot be reused, and so D1 is generated. The commit process then writes a new
> +commit object, C1, that points to D1 as the state of the tree after this
> +commit, and points to C0 as the commit on which C1 was based. Finally, HEAD is
> +changed to point to C1.
> +
> + +-----+
> + +---->| F2B |
> + +-----+ +-----+ | +-----+
> + HEAD--->| C1 |------->| D1 |----+
> + +-----+ +-----+ |
> + | |
> + | | +-----+
> + | +---->| F3A |
> + | | +-->+-----+
> + V | |
> + +-----+ +-----+ | | +-----+
> + | C0 |------->| D0 |------+-->| F2A |
> + +-----+ +-----+ | | +-----+
> + | |
> + +-|-->+-----+
> + +-->| F1A |
> + +-----+
> +
> +Then imagine that someone changes file F1 and commits the change. F3A is still
> +viable in its original state, and F2B is usable from commit C1, but F1A is now
> +obsolete and gets replaced by version F1B. This means that neither D0 nor D1
> +are usable, so directory object D2 has to be created, and new commit C2 is
> +created to point to that and base commit C1. Then HEAD is set to point to C2:
> +
> + +-----+
> + +------>| F1B |
> + +-----+ +-----+ | +-----+
> + HEAD--->| C2 |------->| D2 |--+
> + +-----+ +-----+ |
> + | |
> + | +------>+-----+
> + V | +---->| F2B |
> + +-----+ +-----+ | | +-----+
> + | C1 |------->| D1 |----+
> + +-----+ +-----+ | |
> + | | |
> + | +-|---->+-----+
> + | +---->| F3A |
> + | | +-->+-----+
> + V | |
> + +-----+ +-----+ | | +-----+
> + | C0 |------->| D0 |------+-->| F2A |
> + +-----+ +-----+ | | +-----+
> + | |
> + +-|-->+-----+
> + +-->| F1A |
> + +-----+
> +
> +Now, consider what would have happened if, instead of changing F1A to be F1B to
> +produce C2, F2B had been reverted to the same state as F2A. GIT would realise
> +that it already has a file object to represent F2A (by comparing object IDs)
> +and would use that rather than creating a new one. The new set of files in the
> +directory would then be F1A, F2A and F3A - but there's already a directory
> +object for that: D0. This would also be discovered by object ID matching, and
> +would be used instead. Commit C3 would then point to base commit C1 and
> +directory D0, and HEAD would be moved to point to C3:
> +
> + +-----+
> + HEAD--->| C3 |---+
> + +-----+ |
> + | |
> + | | +-----+
> + V | +---->| F2B |
> + +-----+ | +-----+ | +-----+
> + | C1 |------->| D1 |----+
> + +-----+ | +-----+ |
> + | | |
> + | | | +-----+
> + | | +---->| F3A |
> + | | | +-->+-----+
> + V | | |
> + +-----+ +--->+-----+ | | +-----+
> + | C0 |------->| D0 |------+-->| F2A |
> + +-----+ +-----+ | | +-----+
> + | |
> + +-|-->+-----+
> + +-->| F1A |
> + +-----+
> +
> +
> +GIT TREES AFTER MERGING
> +-----------------------
> +
> +Now, imagine that two GIT trees are merged. You start off with two sets of
> +commits (for convenience, I'm going to leave out the directories and files, but
> +you can just assume they're there):
> +
> + +-----+ +-----+
> + HEAD--->| C3 | Branch->| B3 |
> + +-----+ +-----+
> + | |
> + V V
> + +-----+ +-----+
> + | C2 | | B2 |
> + +-----+ +-----+
> + | |
> + V V
> + +-----+ +-----+
> + | C1 |<------------------------| B1 |
> + +-----+ +-----+
> + |
> + V
> + +-----+
> + | C0 |
> + +-----+
> +
> +In the above example, I've assumed that you've got your own tree with the head
> +at commit C3, and that you've got a branch that you want to merge, which has
> +its head at commit B3. After merging them, you'd end up with a directed,
> +cyclic tree:
> +
> + +-----+
> + HEAD--->| C4 |----------------------------+
> + +-----+ |
> + | |
> + V V
> + +-----+ +-----+
> + | C3 | Branch->| B3 |
> + +-----+ +-----+
> + | |
> + V V
> + +-----+ +-----+
> + | C2 | | B2 |
> + +-----+ +-----+
> + | |
> + V V
> + +-----+ +-----+
> + | C1 |<------------------------| B1 |
> + +-----+ +-----+
> + |
> + V
> + +-----+
> + | C0 |
> + +-----+
> +
> +and the C4 commit will have pointers to *both* contributing commits, C3 and B3.
> +If GIT stored the differences at each commit rather than the terminal state, it
> +would have to store a delta for each contributing commit.
> +
> +
> +==========================
> +DOWNLOADING UPSTREAM TREES
> +==========================
> +
> +The first thing you'll usually want to do with GIT is to grab a copy of the
> +cutting edge version of an upstream project and build it; perhaps you want to
> +work on it, perhaps because it has a fix in it that you need or perhaps because
> +you like living on the cutting edge and enjoy grepping your disks to recover
> +your data when things go wrong. Whatever your reasons, you need to be able
> +make a local copy of an upstream GIT tree.
> +
> +With GIT-based projects, grabbing a local copy of an upstream repository is
> +very easy:
> +
> + git clone %UPSTREAM_REPO %MY_DIR
> +
> +This will create a checked-out copy of the the upstream repository
> +(%UPSTREAM_REPO) by pulling over the internet and sticking it in a directory on
> +the local machine.
> +
> +For example, to fetch Linus's cutting edge kernel tree, you'd do:
> +
> + git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git \
> + linux-2.6-local
> +
> +Then you look in linux-2.6-local and there is what you're looking for.
> +
> +
> +LOCAL MIRRORING
> +---------------
> +
> +You might find that you wish to run several concurrent, separate developments
> +all based upon a single upstream repository. You could simply clone each one
> +as mentioned above, but that has the potential to use excessive amounts of disk
> +space as each clone would include an independent copy of the entire source
> +repository.
> +
> +What you might want to do is to set up a mirror of the upstream repository, and
> +then share that mirror with each of the clones. Even better, you can share it
> +with other people who can also access the filesystem it is stored upon.
> +
> +So what you can do is create a local mirror:
> +
> + git clone -n %UPSTREAM_REPO %MIRROR_DIR
> +
> +For example:
> +
> + git clone -n git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git \
> + /warthog/git/linux-2.6.git
> +
> +The -n flag tells git to save space by not bothering to check the files out of
> +the repository. You don't really need the checkout if all you're going is to
> +use this as a reference, but you can still check out if you like by omitting
> +the -n.
> +
> +
> +AUTOMATIC UPDATES
> +-----------------
> +
> +Furthermore, you might want to automatically update your sources at some
> +unfeasible hour of the morning when only Australians are awake because, say,
> +your internet supply is rated more cheaply then - but you don't necessarily
> +want the automatic update to dump into the sources you're actively meddling
> +with. A local mirror can help with this too.
> +
> +One way of automatically updating your mirror is to use cron. To do this
> +create a script that looks something like:
> +
> + #!/bin/sh
> + cd %MIRROR_DIR || exit $?
> + exec git pull >/tmp/git-pull.log
> +
> +and chmod u+x it. Then run the crontab program to modify your personal cron
> +schedule and add something like the following line to it (not forgetting to
> +remove the leading tab!):
> +
> + 0 %HOUR * * * %MIRROR_SCRIPT
> +
> +where %HOUR is the hour you want it to go off every day. For my local mirror
> +of Linus's upstream kernel, I use:
> +
> + #!/bin/sh
> + cd /warthog/git/linux-2.6 || exit $?
> + exec git pull >/tmp/git-pull.log
> +
> +and:
> +
> + 0 6 * * * /home/dhowells/bin/do-git-pull.sh
> +
> +which will do the update every day at 6am.
> +
> +
> +USING YOUR LOCAL MIRROR
> +-----------------------
> +
> +You can then create a directory to actually do your development in by:
> +
> + git clone -l -s %MIRROR_DIR %MY_DIR
> +
> +The "-l" tells git clone that the source (mirror) repository is on the local
> +machine, that it shouldn't go over the internet for it, and that it should
> +hardlink GIT objects from the source repository rather than copying them where
> +possible.
> +
> +The "-s" says that git clone should insert a reference under %MY_DIR that
> +points to the %MIRROR_DIR's collection of objects. This means that GIT won't
> +bother to copy the objects that it can get from %MIRROR_DIR at all, it'll just
> +use them out of %MIRROR_DIR.
> +
> + [!] NOTE: This makes %MY_DIR dependent on %MIRROR_DIR: if you delete
> + %MIRROR_DIR or prune it you may make %MY_DIR unusable!
> +
> +You can repeat this again and again from the same mirror. You can even share a
> +mirror with other people that can access the filesystem holding the mirror.
> +You don't need write access to it, only read.
> +
> +
> +========================
> +ACCESSING THE REPOSITORY
> +========================
> +
> +One of the things you'll want to be able to do with what you've downloaded is
> +look at changes other people have made. GIT has some powerful tools to allow
> +you to do this.
> +
> +
> +VIEWING THE HISTORY
> +-------------------
> +
> +You might wish, for example, to look back through the commit tree and see what
> +changes have been made. The command to do this is:
> +
> + git log
> +
> +This will take you back through the commit information, starting at the current
> +HEAD and going all the way back to the beginning if you let it:
> +
> + warthog>git log
> + commit 8b1fae4e4200388b64dd88065639413cb3f1051c
> + Author: Linus Torvalds <torvalds@linux-foundation.org>
> + Date: Wed Dec 10 15:11:51 2008 -0800
> +
> + Linux 2.6.28-rc8
> +
> + commit f9fc05e7620b3ffc93eeeda6d02fc70436676152
> + Merge: b88ed20... 9a2bd24...
> + Author: Linus Torvalds <torvalds@linux-foundation.org>
> + Date: Wed Dec 10 14:41:06 2008 -0800
> +
> + Merge branch 'sched-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
> +
> + * 'sched-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
> + sched: CPU remove deadlock fix
> +
> + commit b88ed20594db2c685555b68c52b693b75738b2f5
> + Author: Hugh Dickins <hugh@veritas.com>
> + Date: Wed Dec 10 20:48:52 2008 +0000
> + ...
> +
> +
> +VIEWING A COMMIT
> +----------------
> +
> +Now that you can see the commit IDs in the history, you can examine one more
> +closely:
> +
> + git show
> +
> +to see the current HEAD commit, or:
> +
> + git show %COMMIT_ID
> +
> +to see a particular commit:
> +
> + warthog>git show 1da177e
> + commit 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2
> + Author: Linus Torvalds <torvalds@ppc970.osdl.org>
> + Date: Sat Apr 16 15:20:36 2005 -0700
> +
> + Linux-2.6.12-rc2
> +
> + Initial git repository build. I'm not bothering with the full history,
> + ...
> + diff --git a/COPYING b/COPYING
> + new file mode 100644
> + index 0000000..2a7e338
> + --- /dev/null
> + +++ b/COPYING
> + @@ -0,0 +1,356 @@
> + +
> + + NOTE! This copyright does *not* cover user programs that use kernel
> + ...
> +
> +
> +VIEWING SOURCE DIFFERENCES
> +--------------------------
> +
> +The 'git-show' command shows you what it thinks the differences are that you
> +want to see, between a commit and its first listed base commit. However, there
> +are other differences you might wish to see.
> +
> +Firstly, you might like to see the differences between what's in the current
> +HEAD commit, and what you've got checked out:
> +
> + git diff
> +
> +or you might wish to see the differences between two particular commits, for
> +example:
> +
> + git diff v2.6.24 v2.6.25
> +
> +
> +==============
> +MAKING CHANGES
> +==============
> +
> +So you've got a fresh development GIT tree and you want to make changes in it
> +and commit them to it. The first is easy enough: just use your preferred text
> +editor to edit the files directly, or you could use sed or perl to apply some
> +textual transformations - that's entirely up to you.
> +
> +However, once you've made those changes and you've compiled and tested them,
> +you'll probably want to consign them to GIT.
> +
> +Files you've added must be marked by:
> +
> + git add <filename>
> +
> +and files you've deleted must be noted by:
> +
> + git rm <filename>
> +
> +so that GIT knows to include or exclude these files from its tree.
> +Furthermore, you must tell GIT about any files that have changed that you want
> +to be updated also:
> +
> + git add <filename>
> +
> +You can then commit your changes. This is done by running:
> +
> + git commit
> +
> +Rather than doing lots of git add and git rm commands to register updated and
> +removed files, you can give git commit a '-a' flag. Note, though, that this
> +takes no account of new files that git doesn't already know about. Those must
> +be added manually.
> +
> +git commit will pop up your favourite editor, asking you to enter a commit
> +message describing your changes (don't forget to add your sign-off). It will
> +list the files it sees that have been added, altered and removed, and will
> +differentiate between those that it has been told about (and thus will include)
> +and those it hasn't (which will be ignored).
> +
> +After git commit completes successfully, 'git show' should show the new commit
> +you've just made, and gitk should show the new tree structure with your new
> +commit at the top.
> +
> +
> +APPLYING PATCHES
> +----------------
> +
> +If you have a patch file you wish to apply, you can do that with:
> +
> + git apply <patch-file>
> +
> +This will make the changes specified by the patch, but it won't register any of
> +the changes and won't record any of the metadata that might be in the patch
> +file, such as authorship, description or attribution. That has to be done
> +manually as if you'd made the changes yourself.
> +
> +
> +APPLYING FORMATTED PATCHES
> +--------------------------
> +
> +Sometimes you may wish to incorporate a patch that someone has emailed you.
> +You could use the 'patch' or 'git apply' programs and then set up the commit
> +information manually, but if someone has sent you an appropriately formatted
> +message - perhaps in an email - you can have GIT import the metadata from the
> +message rather than you having to type it manually.
> +
> +If someone has given you an email or appropriately formatted patch file, the
> +following command can import it:
> +
> + git am <patch-file>
> +
> +If successful, this will automatically register all added, altered and removed
> +files and commit the changes for you. The commit message will be concocted
> +from the description and email headers (From: and Subject: for instance). If
> +you want to add your own sign-off to the bottom of the commit message whilst
> +you're at it, you can add a '-s' flag:
> +
> + git am -s <patch-file>
> +
> +You may find it convenient to edit unformatted patches to make it possible to
> +use 'git am' rather than 'git apply'.
> +
> +
> +INCORPORATING GIT TREES
> +-----------------------
> +
> +And sometimes, rather then sending you patches, people may attempt to
> +contribute changes to you that are contained within GIT trees and you may wish
> +to incorporate these into your development tree.
> +
> +To do this, the following command will work:
> +
> + git pull %CONTRIB_REPO %CONTRIB_BRANCH
> +
> +where %CONTRIB_REPO is the URL of a repository and %CONTRIB_BRANCH is the name
> +of the branch within that repository (usually this will be 'master').
> +
> +If successful, this will either just stack the pulled changes directly on top
> +of your tree (assuming the contributed tree is based on the head of your tree)
> +or it will automatically produce a merge commit indicating that the resulting
> +tree is a union of the changes in your tree and the contributed tree.
> +
> +If unsuccessful due to conflicting changes, you'll need to perform the merge
> +manually and perform the commit yourself. See the "Manually merging failed
> +fetches" section.
> +
> +An example of the command line you might use is:
> +
> + git pull git://git.infradead.org/mtd-2.6.git master
> +
> +which will pull master branch of the upstream MTD tree into the GIT tree you're
> +currently in.
> +
> +
> +==============================
> +AMENDING AND REVERTING CHANGES
> +==============================
> +
> +There will be times when you make a mistake in your changes, and you find that
> +you either want to amend them, or you want to discard them entirely. GIT
> +provides a number of tools to do this.
> +
> +If you make a mistake in changes you haven't yet committed, you can just edit
> +them again with your text editor, or if you'd prefer to discard all the changes
> +you made to a particular file, you can do:
> +
> + git checkout <filename>
> +
> +This will just wipe away the changes that you've made and restore the file to
> +the state it has recorded for it as part of the topmost commit.
> +
> +
> +AMENDING COMMITTED CHANGES
> +--------------------------
> +
> +If you've committed some changes and you realise that those changes are
> +incorrect, you can amend them without precisely making a whole new commit -
> +provided you haven't committed anything else on top of them.
> +
> +To do this, you make your changes, run git add and git rm as normal, and then
> +do:
> +
> + git commit --amend
> +
> +This will replace the topmost commit with a similar commit that includes the
> +amendments. The old commit will be displaced from the tree and will not appear
> +again.
> +
> +Changes that are buried beneath further commits unfortunately have to be
> +altered by making a new commit with the amendments, unless you wish to discard
> +all the commits down to the one that needs amending, and then apply them all
> +again.
> +
> +
> +DISCARDING COMMITTED CHANGES
> +----------------------------
> +
> +Upon occasion, you'll want to discard one or more commits entirely from the top
> +of your tree. To do this you need to find the ID of the latest commit that you
> +want to keep. Everything from the commit after that to the current commit will
> +be discarded.
> +
> +You can find the commit ID in a number of ways. Firstly, you can use 'git log'
> +to look back through the commits. The commit ID is shown as something like:
> +
> + commit 6c34bc2976b30dc8b56392c020e25bae1f363cab
> +
> +Secondly, you can use gitk: select the commit of interest; the commit ID
> +appears in the box labelled "SHA1 ID".
> +
> +You can then perform the discard with the following command:
> +
> + git reset --hard %COMMIT_ID
> +
> +Using the above commit ID as an example, you could do:
> +
> + git reset --hard 6c34bc2976b30dc8b56392c020e25bae1f363cab
> +
> +
> +REVERTING COMMITTED CHANGES
> +---------------------------
> +
> +And sometimes you'll want to revert changes that you've committed, but that are
> +now buried beneath other commits. Short of discarding and reapplying commits,
> +you have to apply a reverse patch:
> +
> + git diff %COMMIT_ID | patch -p1 -R
> +
> +and then commit it. Both the original application and the reversion will be
> +retained by GIT.
> +
> +
> +==============================
> +PUBLISHING CHANGES BY GIT TREE
> +==============================
> +
> +Now that you've got a tree and have mangled it in unspeakable ways, you
> +probably want to donate the glory of your works back to the community - usually
> +with an eye to getting your changes pulled into an upstream maintainer's
> +repository. Your upstream maintainer may then push your changes on to their
> +upstream maintainer, until it ends into the ultimate upstream repository
> +(Linus's linux-2.6 tree in the case of the Linux kernel).
> +
> +You could, of course, just push patches to the upstream maintainer, be that
> +Linus or one of his cronies in the case of the Linux kernel, or some other
> +person if some other project.
> +
> +GIT, however, leans strongly towards another option. If you can get access to
> +a computer that is accessible by way of the internet, you might be able to set
> +up a public GIT tree upon it and ask an appropriate upstream maintainer to pull
> +from that.
> +
> +That computer, however, may not be particularly convenient for developing on:
> +it may be remote from where you're working, for example, perhaps even on a
> +different continent - so you'll probably want to have two trees: a remote,
> +public, published tree, and a local private tree where you can break stuff at
> +will. I'm going to assume the two trees approach.
> +
> +
> +SETTING UP
> +----------
> +
> +First of all, you'll need to set up your two trees. There are a number of
> +steps to go through to do this:
> +
> + (1) Find somewhere that's accessible by the internet (%REMOTE_BOX) that you
> + have SSH access to, and set up a public GIT tree that's a clone of the
> + upstream tree you want to use as a base:
> +
> + ssh %REMOTE_BOX
> + cd /my/git/trees
> + git clone -n --bare %UPSTREAM_REPO %MY_DIR
> +
> + Where %UPSTREAM_REPO is something like:
> +
> + git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
> +
> + This will create a directory called /my/git/trees/%MY_DIR that contains a
> + bare GIT repository to which you can upload your changes. There will be
> + no checked out files here, and everything that would usually be in the
> + .git directory is in the top directory instead.
> +
> + If your tree is on the same box as the tree you want to fork, you can
> + tell GIT to use that rather than going to the internet:
> +
> + git clone -l -s -n --bare %UPSTREAM_DIR %MY_DIR
> +
> + For example, I might wish to set up a tree to publish NOMMU changes so
> + that they're available through git.kernel.org. To that end, I would do:
> +
> + ssh master.kernel.org
> + cd /pub/scm/linux/kernel/git/dhowells
> + git clone -l -n -s --bare \
> + /pub/scm/linux/kernel/git/torvalds/linux-2.6.git linux-2.6-nommu
> +
> +
> + (2) You should set the description on your public repository:
> +
> + echo %DESCRIPTION >%MY_DIR/description
> +
> + For example:
> +
> + echo "NOMMU development" >linux-2.6-nommu/description
> +
> + This will be published through the GIT web interface if one is set up, and
> + so can be viewed by going to the appropriate URL. For instance:
> +
> + http://git.kernel.org/?p=linux/kernel/git/dhowells/linux-2.6-nommu.git
> +
> +
> + (3) Now go to the work machine on which you'll be doing your development.
> + You'll need to create a local fork of your public GIT repository. You can
> + do this by:
> +
> + git clone ssh://%REMOTE_BOX/my/git/trees/%MY_DIR %DEVEL_DIR
> +
> + This will create a checked-out GIT tree in a directory (%DEVEL_DIR) that
> + you can later use for development. If you have a local mirror of the
> + upstream tree that you're using as a base, you can tell git to use the
> + objects from that to save space:
> +
> + git clone --reference %LOCAL_UPSTREAM_MIRROR \
> + ssh://%REMOTE_BOX/my/git/trees/%MY_DIR \
> + %DEVEL_DIR
> +
> + [!] NOTE: You must use ssh: and not git: to clone your tree because you
> + need to be able to push back (write) to your public tree.
> +
> + To continue my example, I have a local mirror of Linus's kernel, regularly
> + updated by cron, and so to make my local NOMMU development tree, I would
> + do:
> +
> + git clone --reference /warthog/git/linux-2.6 \
> + ssh://master.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-2.6-nommu.git \
> + linux-2.6-nommu
> +
> +
> + (4) Now you need to set up your local GIT tree to make it possible (a) update
> + your development tree by pulling in the upstream tree, and (b) publish
> + your changes by pushing them to your public tree.
> +
> + cd %DEVEL_DIR
> +
> + Tell your repository where to find the upstream tree:
> +
> + git remote add %UPSTREAM %UPSTREAM_REPO
> +
> + where %UPSTREAM is the name you by which you want to refer to the upstream
> + repository to git pull. For Linus's upstream kernel, you might wish to
> + use 'linus' for example.
> +
> + In my example, I did the following to pull Linus's tree into branches of
> + my tree:
> +
> + cd linux-2.6-nommu
> + git remote add linus \
> + git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
> +
> + Looking in .git/config, I now see section that looks like this:
> +
> + [remote "origin"]
> + url = ssh://master.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-2.6-nommu.git
> + fetch = +refs/heads/*:refs/remotes/origin/*
> + [branch "master"]
> + remote = origin
> + merge = refs/heads/master
> + [remote "linus"]
> + url = git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
> + fetch = +refs/heads/*:refs/remotes/linus/*
> +
> +
> + (5) You should now be able to update your development tree from the upstream
> + repository to make sure that works:
> +
> + git fetch -v %UPSTREAM
> +
> + In my case, that's:
> +
> + git fetch -v linus
> +
> + If you've just created the repository, it'll probably just say that things
> + are up to date:
> +
> + From git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
> + = [up to date] master -> linus/master
> +
> + [!] NOTE: I cannot determine a way of making "git pull linus" work without
> + setting branch.master.remote to 'linus'.
> +
> +
> + (6) And then you should be able to publish your development tree by pushing it
> + to your public tree, thus allowing the rest of the world to see your
> + changes.
> +
> + git push -v origin
> +
> +
> + (7) Finally you should be able to pull your published tree back into your
> + development tree, and it should just say that it's up to date:
> +
> + git pull -v
> +
> +
> +UPDATING YOUR DEVELOPMENT TREE
> +------------------------------
> +
> +Okay: so you've got your tree, and you've made changes to it, and now Linus has
> +gone and dumped five thousand patches into his tree, making the base for your
> +changes obsolete. You need to update your tree and fix up your changes.
> +
> +If you haven't yet committed your changes, you'll have to siphon them off into
> +a file:
> +
> + git diff >a.diff
> +
> +and deapply them:
> +
> + patch -p1 -R <a.diff
> +
> +You can then update your tree from the upstream tree with no fear of a conflict
> +(assuming you don't also have changes that you have committed). Once you've
> +updated your tree, you can reapply your changes:
> +
> + patch -p1 <a.diff
> +
> +And then fix up the rejects with your favourite editor and a few choice curses.
> +
> +
> +To actually update your tree, you can do the following:
> +
> + git fetch %UPSTREAM
> +
> +In my example, that'd be:
> +
> + git fetch linus
> +
> +If you have committed changes, this will attempt to merge them, but you may
> +still need to fix them up. If everything went smoothly this will automatically
> +commit a merge on top of the tree and set the HEAD pointer to that. This merge
> +will point at your last tree and the tree you just merged from upstream, and
> +will indicate that the resulting tree is a combination of both. Of course, you
> +shouldn't assume it will still compile, let alone still work...
> +
> +If you do need to fix them up, refer to the "Manually merging failed fetches"
> +section for guidance.
> +
> +You can view the merge that git pull committed by:
> +
> + git show
> +
> +And you can view the tree structure at that point with the gitk command.
> +
> +
> +PUBLISHING YOUR CHANGES
> +-----------------------
> +
> +Finally, you're in a position to make your changes available. Firstly, you
> +have to commit them to your development tree (as mentioned previously) and then
> +you have to make them available to the rest of the world. To do that, simply
> +run:
> +
> + git push
> +
> +which will apply the changes to your public tree. If you have web access to
> +your git tree, these will eventually become visible through there.
> +
> +You may then have to tell your upstream maintainer what you'd like them to pull
> +from your tree. The standard way to do this is to do:
> +
> + git request-pull %BASE_ID %MY_REPO >/tmp/request.txt
> +
> +where %BASE_ID is the head of the tree on which your changes are based, and
> +%MY_REPO is the public URL of your repository. If you have your development
> +git tree configured to know where the upstream remote repository is, then if
> +you've ever done 'git fetch' you should have a branch for it, named something
> +like "%UPSTREAM/%UPSTREAM_BRANCH" where %UPSTREAM is the name you gave to 'git
> +remote' and %UPSTREAM_BRANCH is the upstream branch on which you've based your
> +development (almost certainly 'master').
> +
> +This command will generate a list of all the patches between %BASE_ID and the
> +head of your tree that you are asking to be pulled.
> +
> +In my example, I can do:
> +
> + git request-pull linus/master \
> + git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-2.6-nommu.git \
> + >/tmp/request.txt
> +
> +You should then edit /tmp/request.txt to include a description of what you're
> +trying to achieve with these patches, and then mail the whole file to the
> +upstream maintainer.
> +
> +[!] NOTE: It may take some time for the git push to take full effect. Before
> + that time is up, git request-pull may give spurious warnings and the test
> + it produces may say that the branch is unverified.
> +
> +
> +===============================
> +MANUALLY MERGING FAILED FETCHES
> +===============================
> +
> +Occasionally, when you pull someone else's tree in to your repository, either
> +because the base needs updating or because you're incorporating stuff from a
> +contributor, the merge will fail due to conflicts between the changes you have
> +made in your tree, and the changes you're importing.
> +
> +GIT will try and automatically merge where possible, but it can't always manage
> +it. In such cases you have to unlimber your text editor and fix it manually.
> +
> +GIT will report the files that need merging during the git fetch/git pull:
> +
> + CONFLICT (content): Merge conflict in drivers/char/tty_audit.c
> +
> +and they can also be determined by looking in ".git/MERGE_MSG".
> +
> +GIT will interpolate markers into the affected files, along with both versions
> +of the code:
> +
> + <<<<<<< HEAD:drivers/char/tty_audit.c
> + tsk->pid, uid, loginuid, sessionid,
> + =======
> + tsk->pid, tsk->uid, loginuid, sessionid,
> + >>>>>>> b3985e2bf6ce51ae943208af4bd336287fb34ed6:drivers/char/tty_audit.c
> +
> +The first section (<<<<<<<< to =======) is the version from your tree, the
> +second section (======= to >>>>>>>) is the version from the tree being
> +imported. The markers must be removed, and the conflicting code resolved down
> +to the appropriate final version.
> +
> +
> +Once that is done, git add (or git rm) must be called on the changed files so
> +that git commit knows to include them in the new head. It works exactly like
> +changing files normally (as per the "Making changes" section), except that GIT
> +has stored extra data that will go into the merge commit when git commit
> +creates it.
> +
> +
> +=============
> +LOCATING BUGS
> +=============
> +
> +There will be times when the program you've built malfunctions. It happens now
> +and then even to the best of projects. Sometimes you can easily locate the bug
> +by looking at the symptoms and the debugging output and then eyeballing the
> +code, and sometimes you can't.
> +
> +For very big projects such as the Linux kernel, finding a bug that someone else
> +has inadvertently introduced can be very hard, but GIT allows you to take
> +advantage of the fact that the changes are introduced a bit at a time with
> +clear boundaries (commits) to make life a bit easier.
> +
> +
> +BISECTION
> +---------
> +
> +What you really want to be able to do is to isolate the commit that's causing
> +the malfunction, but with automation support so that you don't have to trace
> +the commit tree yourself. GIT has a tool to do this: git bisect.
> +
> +The way this works is to take two points in the tree: one at which you know the
> +program malfunctions, and one at which you know it doesn't, and then chop its
> +way through the tree to locate the failing commit.
> +
> +To illustrate this:
> +
> + (1) Assume that you're dealing with the kernel, and that you find that after
> + Linus's merge window, 2.6.25-rc1 does not boot for you, but you know that
> + 2.6.24 did prior to the window.
> +
> + Firstly you have to start your search and describe the bounds (the working
> + and non-working points). This is done with the following commands:
> +
> + git bisect start [%BAD_COMMIT [%GOOD_COMMIT]]
> + git bisect bad [%BAD_COMMIT]
> + git bisect good [%GOOD_COMMIT]
> +
> + where %BAD_COMMIT and %GOOD_COMMIT are optional commit object IDs or
> + symbolic representations thereof. The 'bad' command is unnecessary if
> + %BAD_COMMIT is given to 'start', and the 'good' command is not required if
> + %GOOD_COMMIT is given to 'start'.
> +
> + So, in the example we're looking at, you could do:
> +
> + git bisect start
> + git bisect bad v2.6.25-rc1
> + git bisect good v2.6.24
> +
> + or:
> +
> + git bisect start v2.6.25-rc1
> + git bisect good v2.6.24
> +
> + or:
> +
> + git bisect start v2.6.25-rc1 v2.6.24
> +
> + [!] NOTE: This is using a symbolic tag 'v2.6.24' to refer to the last
> + commit before 2.6.24 was declared.
> +
> +
> + However, if 2.6.25-rc1 is at currently at the head of your tree, you can
> + do:
> +
> + git bisect start
> + git bisect bad
> +
> + to indicate that this malfunctioned, or you could do this in a single
> + command:
> +
> + git bisect start HEAD
> +
> + to start bisection _and_ indicate that the HEAD revision is bad.
> +
> +
> + Alternatively, if you're at a point where the program _does_ work, you can
> + pass either HEAD or no parameter to the 'good' bisection command, or pass
> + HEAD as the %GOOD_COMMIT parameter to the 'start' bisection command.
> +
> +
> + (2) Now GIT will rumble through the commits between the two points you have
> + declared, and set the current HEAD of the repository to a point that
> + approximates midway between the two:
> +
> + warthog>git bisect start v2.6.25-rc1 v2.6.24
> + Bisecting: 4814 revisions left to test after this
> + [d2e626f45cc450c00f5f98a89b8b4c4ac3c9bf5f] x86: add PAGE_KERNEL_EXEC_NOCACHE
> +
> + and then it will check out the sources to reflect their state at this point.
> +
> +
> + (3) You should now attempt to compile this and test it. If the test succeeds,
> + you should run the command:
> +
> + git bisect good
> +
> + If the test fails, run the command:
> +
> + git bisect bad
> +
> + These will tell GIT to binary chop the commits between either the current
> + point and the good end or the current point and the bad end to find a new
> + commit to test:
> +
> + warthog>git bisect bad
> + Bisecting: 2406 revisions left to test after this
> + [fb46990dba94866462e90623e183d02ec591cf8f] [NETFILTER]: nf_queue: remove unnecessary hook existance check
> + warthog>git bisect good
> + Bisecting: 1203 revisions left to test after this
> + [936722922f6d2366378de606a40c14f96915474d] [IPV4] fib_trie: compute size when needed
> +
> + As for when bisection started, GIT will set the current HEAD pointer and
> + then check out the sources. You should repeat step (3).
> +
> + If the commit is broken for you and the compile fails, run the command:
> +
> + git bisect skip
> +
> + this will cause the bisection algorithm to move onto the next commit in
> + the hope that this one will be better:
> +
> + warthog>git bisect skip
> + Bisecting: 1203 revisions left to test after this
> + [1328042e268c936189f15eba5bd9a5a4605a8581] [IPV4] fib_trie: use hash list
> +
> + this will change the HEAD pointer and check out the sources. Repeat step
> + (3).
> +
> +
> + (4) Eventually, after you've tested a number of different commits, GIT will
> + tell you that it has narrowed the problem down to either a single commit,
> + or if there were compile errors that got in the way, a range of commits:
> +
> + warthog>git bisect bad
> + e3ac5298159c5286cef86f0865d4fa6a606bd391 is first bad commit
> + commit e3ac5298159c5286cef86f0865d4fa6a606bd391
> + Author: Patrick McHardy <kaber@trash.net>
> + Date: Wed Dec 5 01:23:57 2007 -0800
> +
> + [NETFILTER]: nf_queue: make queue_handler const
> +
> + Signed-off-by: Patrick McHardy <kaber@trash.net>
> + Signed-off-by: David S. Miller <davem@davemloft.net>
> + ...
> +
> +
> + (5) At any time during the bisection process, you can use:
> +
> + git show
> +
> + to examine the commit currently selected for testing, and:
> +
> + git bisect log
> +
> + to view the log of information provided by you through git bisect start,
> + good and bad, and:
> +
> + git bisect visualize
> +
> + to start up the gitk program to show you a graphical view of the current
> + good-to-bad range of commits as narrowed down by bisection.
> +
> +
> + (6) You should then end the bisection process by:
> +
> + git bisect reset
> +
> +
> +BLAME
> +-----
> +
> +Now imagine that rather than indulging in bisection you've found a bug by
> +simply looking at the code: who do you tell about it? You could look at the
> +banner comment at the top of the file to look for names and email addresses,
> +and you could also look in the kernel MAINTAINERS file or its equivalent, but
> +the person you really want to harangue is whoever made the change...
> +
> +There's a very useful GIT tool to help determine this:
> +
> + git blame <file>
> +
> +also known as:
> +
> + git annotate <file>
> +
> +which will give you a list of lines in a source file against who changed them
> +last and in what commit. You may find that your favourite editor has a
> +facility to run this for you (Emacs has vc-annotate, bound to C-x v g, for
> +example).
> +
> +Running git blame on the kernel's README file, for example, might show:
> +
> + warthog>git blame README
> + 620034c8 (Jesper Juhl 2006-12-07 00:45:58 +0100 1) Linux kernel release 2.6.xx <http://kernel.org/>
> + ^1da177e (Linus Torvalds 2005-04-16 15:20:36 -0700 2)
> + ^1da177e (Linus Torvalds 2005-04-16 15:20:36 -0700 3) These are the release notes for Linux version 2.6. Read them carefully,
> + ...
> +
> +The hex number that occurs first on the line is a truncated commit object ID,
> +and this can be passed to git-show (remove the '^' symbol first, if given).
> +
> + warthog>git show 1da177e
> + commit 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2
> + Author: Linus Torvalds <torvalds@ppc970.osdl.org>
> + Date: Sat Apr 16 15:20:36 2005 -0700
> + ...
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
^ permalink raw reply
* Re: [PATCH] git-branch: display sha1 on branch deletion
From: Jeff King @ 2008-12-12 19:43 UTC (permalink / raw)
To: Brandon Casey; +Cc: gitster, git
In-Reply-To: <uTIFnEi0iyLKcAungf7u1Gu2xl50j9i-AMiZaQp-QTs1q-ppgyHZoelGLgvK7BFKpYE03BLRHJ4@cipher.nrlssc.navy.mil>
On Fri, Dec 12, 2008 at 01:29:01PM -0600, Brandon Casey wrote:
> Make it easier to recover from a mistaken branch deletion by displaying the
> sha1 of the branch's tip commit.
I think this is reasonable behavior, but I have two comments:
> - printf("Deleted %sbranch %s.\n", remote, argv[i]);
> + printf("Deleted %sbranch %s (%s).\n", remote, argv[i],
> + sha1_to_hex(sha1));
1. Any reason not to use find_unique_abbrev(sha1, DEFAULT_ABBREV) here?
The full 40-character sha1 kind of dominates the line, especially if
you have short branch name. And this is not really for long-term
usage, but rather "oops, I didn't mean to have just deleted that".
2. I wonder if it is confusing to new users to simply say "Delete branch
$branch ($sha1)". We haven't deleted $sha1, just the branch pointer.
$sha1 is probably still in the HEAD reflog, if not in another branch.
Maybe something like "(was $sha1)" would be appropriate.
I don't know if '2' is a big deal. I haven't been a new user for a long
time, so I didn't personally find it confusing (especially with '1' so
that you actually notice the branch name rather than the gigantic sha1).
-Peff
^ permalink raw reply
* Re: [PATCH] Simplified GIT usage guide
From: Jakub Narebski @ 2008-12-12 19:39 UTC (permalink / raw)
To: David Howells; +Cc: torvalds, git, linux-kernel
In-Reply-To: <20081212182827.28408.40963.stgit@warthog.procyon.org.uk>
David Howells <dhowells@redhat.com> writes:
> Add a guide to using GIT's simpler features.
Wouldn't it be better to update either "Git User's Manual",
or http://book.git-scm.com?
See also: http://git.or.cz/gitwiki/GitDocumentation (including "Git
Magic", "Git in Nutshell", "Git for computer scientists" and "Git from
bottoms up"
--
Jakub Narebski
Poland
ShadeHawk on #git
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox