Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.jgit/src/org/eclipse/jgit/patch/HunkHeader.java')
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/patch/HunkHeader.java383
1 files changed, 383 insertions, 0 deletions
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/patch/HunkHeader.java b/org.eclipse.jgit/src/org/eclipse/jgit/patch/HunkHeader.java
new file mode 100644
index 0000000000..9d78d0b99f
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/patch/HunkHeader.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2008-2009, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.patch;
+
+import static org.eclipse.jgit.util.RawParseUtils.match;
+import static org.eclipse.jgit.util.RawParseUtils.nextLF;
+import static org.eclipse.jgit.util.RawParseUtils.parseBase10;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.eclipse.jgit.diff.Edit;
+import org.eclipse.jgit.diff.EditList;
+import org.eclipse.jgit.lib.AbbreviatedObjectId;
+import org.eclipse.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();
+ }
+
+ final FileHeader file;
+
+ /** Offset within {@link #file}.buf to the "@@ -" line. */
+ final int startOffset;
+
+ /** Position 1 past the end of this hunk within {@link #file}'s buf. */
+ int endOffset;
+
+ private final OldImage old;
+
+ /** First line number in the post-image file where the hunk starts */
+ int newStartLine;
+
+ /** Total number of post-image lines this hunk covers (context + inserted) */
+ int newLineCount;
+
+ /** Total number of lines of context appearing in this hunk */
+ int nContext;
+
+ 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 */
+ public FileHeader getFileHeader() {
+ return file;
+ }
+
+ /** @return the byte array holding this hunk's patch script. */
+ public byte[] getBuffer() {
+ return file.buf;
+ }
+
+ /** @return offset the start of this hunk in {@link #getBuffer()}. */
+ public int getStartOffset() {
+ return startOffset;
+ }
+
+ /** @return offset one past the end of the hunk in {@link #getBuffer()}. */
+ public int getEndOffset() {
+ return endOffset;
+ }
+
+ /** @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 */
+ public int getNewStartLine() {
+ return newStartLine;
+ }
+
+ /** @return Total number of post-image lines this hunk covers */
+ public int getNewLineCount() {
+ return newLineCount;
+ }
+
+ /** @return total number of lines of context appearing in this hunk */
+ public int getLinesContext() {
+ return nContext;
+ }
+
+ /** @return a list describing the content edits performed within the hunk. */
+ public EditList toEditList() {
+ final EditList r = new EditList();
+ final byte[] buf = file.buf;
+ int c = nextLF(buf, startOffset);
+ int oLine = old.startLine;
+ int nLine = newStartLine;
+ Edit in = null;
+
+ SCAN: for (; c < endOffset; c = nextLF(buf, c)) {
+ switch (buf[c]) {
+ case ' ':
+ case '\n':
+ in = null;
+ oLine++;
+ nLine++;
+ continue;
+
+ case '-':
+ if (in == null) {
+ in = new Edit(oLine - 1, nLine - 1);
+ r.add(in);
+ }
+ oLine++;
+ in.extendA();
+ continue;
+
+ case '+':
+ if (in == null) {
+ in = new Edit(oLine - 1, nLine - 1);
+ r.add(in);
+ }
+ nLine++;
+ in.extendB();
+ continue;
+
+ case '\\': // Matches "\ No newline at end of file"
+ continue;
+
+ default:
+ break SCAN;
+ }
+ }
+ return r;
+ }
+
+ void parseHeader() {
+ // Parse "@@ -236,9 +236,9 @@ protected boolean"
+ //
+ final byte[] buf = file.buf;
+ final MutableInteger ptr = new MutableInteger();
+ ptr.value = nextLF(buf, startOffset, ' ');
+ old.startLine = -parseBase10(buf, ptr.value, ptr);
+ if (buf[ptr.value] == ',')
+ old.lineCount = parseBase10(buf, ptr.value + 1, ptr);
+ else
+ old.lineCount = 1;
+
+ newStartLine = parseBase10(buf, ptr.value + 1, ptr);
+ if (buf[ptr.value] == ',')
+ newLineCount = parseBase10(buf, ptr.value + 1, ptr);
+ else
+ newLineCount = 1;
+ }
+
+ int parseBody(final Patch script, final int end) {
+ final byte[] buf = file.buf;
+ int c = nextLF(buf, startOffset), last = c;
+
+ old.nDeleted = 0;
+ old.nAdded = 0;
+
+ SCAN: for (; c < end; last = c, c = nextLF(buf, c)) {
+ switch (buf[c]) {
+ case ' ':
+ case '\n':
+ nContext++;
+ continue;
+
+ case '-':
+ old.nDeleted++;
+ continue;
+
+ case '+':
+ old.nAdded++;
+ continue;
+
+ case '\\': // Matches "\ No newline at end of file"
+ continue;
+
+ default:
+ break SCAN;
+ }
+ }
+
+ 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.
+ //
+ old.nDeleted--;
+ return last;
+ }
+
+ 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 + 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 + 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;
+ }
+
+ void extractFileLines(final OutputStream[] out) throws IOException {
+ final byte[] buf = file.buf;
+ int ptr = startOffset;
+ int eol = nextLF(buf, ptr);
+ if (endOffset <= eol)
+ return;
+
+ // Treat the hunk header as though it were from the ancestor,
+ // as it may have a function header appearing after it which
+ // was copied out of the ancestor file.
+ //
+ out[0].write(buf, ptr, eol - ptr);
+
+ SCAN: for (ptr = eol; ptr < endOffset; ptr = eol) {
+ eol = nextLF(buf, ptr);
+ switch (buf[ptr]) {
+ case ' ':
+ case '\n':
+ case '\\':
+ out[0].write(buf, ptr, eol - ptr);
+ out[1].write(buf, ptr, eol - ptr);
+ break;
+ case '-':
+ out[0].write(buf, ptr, eol - ptr);
+ break;
+ case '+':
+ out[1].write(buf, ptr, eol - ptr);
+ break;
+ default:
+ break SCAN;
+ }
+ }
+ }
+
+ void extractFileLines(final StringBuilder sb, final String[] text,
+ final int[] offsets) {
+ final byte[] buf = file.buf;
+ int ptr = startOffset;
+ int eol = nextLF(buf, ptr);
+ if (endOffset <= eol)
+ return;
+ copyLine(sb, text, offsets, 0);
+ SCAN: for (ptr = eol; ptr < endOffset; ptr = eol) {
+ eol = nextLF(buf, ptr);
+ switch (buf[ptr]) {
+ case ' ':
+ case '\n':
+ case '\\':
+ copyLine(sb, text, offsets, 0);
+ skipLine(text, offsets, 1);
+ break;
+ case '-':
+ copyLine(sb, text, offsets, 0);
+ break;
+ case '+':
+ copyLine(sb, text, offsets, 1);
+ break;
+ default:
+ break SCAN;
+ }
+ }
+ }
+
+ void copyLine(final StringBuilder sb, final String[] text,
+ final int[] offsets, final int fileIdx) {
+ final String s = text[fileIdx];
+ final int start = offsets[fileIdx];
+ int end = s.indexOf('\n', start);
+ if (end < 0)
+ end = s.length();
+ else
+ end++;
+ sb.append(s, start, end);
+ offsets[fileIdx] = end;
+ }
+
+ void skipLine(final String[] text, final int[] offsets,
+ final int fileIdx) {
+ final String s = text[fileIdx];
+ final int end = s.indexOf('\n', offsets[fileIdx]);
+ offsets[fileIdx] = end < 0 ? s.length() : end + 1;
+ }
+}

Back to the top