CombinedFileHeader.java

  1. /*
  2.  * Copyright (C) 2008, Google Inc. and others
  3.  *
  4.  * This program and the accompanying materials are made available under the
  5.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  6.  * https://www.eclipse.org/org/documents/edl-v10.php.
  7.  *
  8.  * SPDX-License-Identifier: BSD-3-Clause
  9.  */

  10. package org.eclipse.jgit.patch;

  11. import static org.eclipse.jgit.lib.Constants.encodeASCII;
  12. import static org.eclipse.jgit.util.RawParseUtils.match;
  13. import static org.eclipse.jgit.util.RawParseUtils.nextLF;

  14. import java.nio.charset.Charset;
  15. import java.util.ArrayList;
  16. import java.util.Arrays;
  17. import java.util.List;

  18. import org.eclipse.jgit.lib.AbbreviatedObjectId;
  19. import org.eclipse.jgit.lib.FileMode;

  20. /**
  21.  * A file in the Git "diff --cc" or "diff --combined" format.
  22.  * <p>
  23.  * A combined diff shows an n-way comparison between two or more ancestors and
  24.  * the final revision. Its primary function is to perform code reviews on a
  25.  * merge which introduces changes not in any ancestor.
  26.  */
  27. public class CombinedFileHeader extends FileHeader {
  28.     private static final byte[] MODE = encodeASCII("mode "); //$NON-NLS-1$

  29.     private AbbreviatedObjectId[] oldIds;

  30.     private FileMode[] oldModes;

  31.     CombinedFileHeader(byte[] b, int offset) {
  32.         super(b, offset);
  33.     }

  34.     /** {@inheritDoc} */
  35.     @Override
  36.     @SuppressWarnings("unchecked")
  37.     public List<? extends CombinedHunkHeader> getHunks() {
  38.         return (List<CombinedHunkHeader>) super.getHunks();
  39.     }

  40.     /**
  41.      * {@inheritDoc}
  42.      * <p>
  43.      *
  44.      * @return number of ancestor revisions mentioned in this diff.
  45.      */
  46.     @Override
  47.     public int getParentCount() {
  48.         return oldIds.length;
  49.     }

  50.     /**
  51.      * {@inheritDoc}
  52.      * <p>
  53.      * @return get the file mode of the first parent.
  54.      */
  55.     @Override
  56.     public FileMode getOldMode() {
  57.         return getOldMode(0);
  58.     }

  59.     /**
  60.      * Get the file mode of the nth ancestor
  61.      *
  62.      * @param nthParent
  63.      *            the ancestor to get the mode of
  64.      * @return the mode of the requested ancestor.
  65.      */
  66.     public FileMode getOldMode(int nthParent) {
  67.         return oldModes[nthParent];
  68.     }

  69.     /**
  70.      * {@inheritDoc}
  71.      * <p>
  72.      *
  73.      * @return get the object id of the first parent.
  74.      */
  75.     @Override
  76.     public AbbreviatedObjectId getOldId() {
  77.         return getOldId(0);
  78.     }

  79.     /**
  80.      * Get the ObjectId of the nth ancestor
  81.      *
  82.      * @param nthParent
  83.      *            the ancestor to get the object id of
  84.      * @return the id of the requested ancestor.
  85.      */
  86.     public AbbreviatedObjectId getOldId(int nthParent) {
  87.         return oldIds[nthParent];
  88.     }

  89.     /** {@inheritDoc} */
  90.     @Override
  91.     public String getScriptText(Charset ocs, Charset ncs) {
  92.         final Charset[] cs = new Charset[getParentCount() + 1];
  93.         Arrays.fill(cs, ocs);
  94.         cs[getParentCount()] = ncs;
  95.         return getScriptText(cs);
  96.     }

  97.     /**
  98.      * {@inheritDoc}
  99.      * <p>
  100.      * Convert the patch script for this file into a string.
  101.      */
  102.     @Override
  103.     public String getScriptText(Charset[] charsetGuess) {
  104.         return super.getScriptText(charsetGuess);
  105.     }

  106.     @Override
  107.     int parseGitHeaders(int ptr, int end) {
  108.         while (ptr < end) {
  109.             final int eol = nextLF(buf, ptr);
  110.             if (isHunkHdr(buf, ptr, end) >= 1) {
  111.                 // First hunk header; break out and parse them later.
  112.                 break;

  113.             } else if (match(buf, ptr, OLD_NAME) >= 0) {
  114.                 parseOldName(ptr, eol);

  115.             } else if (match(buf, ptr, NEW_NAME) >= 0) {
  116.                 parseNewName(ptr, eol);

  117.             } else if (match(buf, ptr, INDEX) >= 0) {
  118.                 parseIndexLine(ptr + INDEX.length, eol);

  119.             } else if (match(buf, ptr, MODE) >= 0) {
  120.                 parseModeLine(ptr + MODE.length, eol);

  121.             } else if (match(buf, ptr, NEW_FILE_MODE) >= 0) {
  122.                 parseNewFileMode(ptr, eol);

  123.             } else if (match(buf, ptr, DELETED_FILE_MODE) >= 0) {
  124.                 parseDeletedFileMode(ptr + DELETED_FILE_MODE.length, eol);

  125.             } else {
  126.                 // Probably an empty patch (stat dirty).
  127.                 break;
  128.             }

  129.             ptr = eol;
  130.         }
  131.         return ptr;
  132.     }

  133.     /** {@inheritDoc} */
  134.     @Override
  135.     protected void parseIndexLine(int ptr, int eol) {
  136.         // "index $asha1,$bsha1..$csha1"
  137.         //
  138.         final List<AbbreviatedObjectId> ids = new ArrayList<>();
  139.         while (ptr < eol) {
  140.             final int comma = nextLF(buf, ptr, ',');
  141.             if (eol <= comma)
  142.                 break;
  143.             ids.add(AbbreviatedObjectId.fromString(buf, ptr, comma - 1));
  144.             ptr = comma;
  145.         }

  146.         oldIds = new AbbreviatedObjectId[ids.size() + 1];
  147.         ids.toArray(oldIds);
  148.         final int dot2 = nextLF(buf, ptr, '.');
  149.         oldIds[ids.size()] = AbbreviatedObjectId.fromString(buf, ptr, dot2 - 1);
  150.         newId = AbbreviatedObjectId.fromString(buf, dot2 + 1, eol - 1);
  151.         oldModes = new FileMode[oldIds.length];
  152.     }

  153.     /** {@inheritDoc} */
  154.     @Override
  155.     protected void parseNewFileMode(int ptr, int eol) {
  156.         for (int i = 0; i < oldModes.length; i++)
  157.             oldModes[i] = FileMode.MISSING;
  158.         super.parseNewFileMode(ptr, eol);
  159.     }

  160.     @Override
  161.     HunkHeader newHunkHeader(int offset) {
  162.         return new CombinedHunkHeader(this, offset);
  163.     }

  164.     private void parseModeLine(int ptr, int eol) {
  165.         // "mode $amode,$bmode..$cmode"
  166.         //
  167.         int n = 0;
  168.         while (ptr < eol) {
  169.             final int comma = nextLF(buf, ptr, ',');
  170.             if (eol <= comma)
  171.                 break;
  172.             oldModes[n++] = parseFileMode(ptr, comma);
  173.             ptr = comma;
  174.         }
  175.         final int dot2 = nextLF(buf, ptr, '.');
  176.         oldModes[n] = parseFileMode(ptr, dot2);
  177.         newMode = parseFileMode(dot2 + 1, eol);
  178.     }

  179.     private void parseDeletedFileMode(int ptr, int eol) {
  180.         // "deleted file mode $amode,$bmode"
  181.         //
  182.         changeType = ChangeType.DELETE;
  183.         int n = 0;
  184.         while (ptr < eol) {
  185.             final int comma = nextLF(buf, ptr, ',');
  186.             if (eol <= comma)
  187.                 break;
  188.             oldModes[n++] = parseFileMode(ptr, comma);
  189.             ptr = comma;
  190.         }
  191.         oldModes[n] = parseFileMode(ptr, eol);
  192.         newMode = FileMode.MISSING;
  193.     }
  194. }