/* * Copyright (C) 2008, 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.lib.Constants.encodeASCII; import static org.eclipse.jgit.util.RawParseUtils.match; import static org.eclipse.jgit.util.RawParseUtils.nextLF; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.eclipse.jgit.lib.AbbreviatedObjectId; import org.eclipse.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 "); //$NON-NLS-1$ 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]; } @Override public String getScriptText(final Charset ocs, final Charset ncs) { final Charset[] cs = new Charset[getParentCount() + 1]; Arrays.fill(cs, ocs); cs[getParentCount()] = ncs; return getScriptText(cs); } /** * Convert the patch script for this file into a string. * * @param charsetGuess * optional array to suggest the character set to use when * decoding each file's line. If supplied the array must have a * length of <code>{@link #getParentCount()} + 1</code> * representing the old revision character sets and the new * revision character set. * @return the patch script, as a Unicode string. */ @Override public String getScriptText(final Charset[] charsetGuess) { return super.getScriptText(charsetGuess); } @Override 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; } }