DiffCommand.java

  1. /*
  2.  * Copyright (C) 2011, Tomasz Zarna <Tomasz.Zarna@pl.ibm.com> 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.api;

  11. import static org.eclipse.jgit.lib.Constants.HEAD;

  12. import java.io.BufferedOutputStream;
  13. import java.io.IOException;
  14. import java.io.OutputStream;
  15. import java.util.List;

  16. import org.eclipse.jgit.api.errors.GitAPIException;
  17. import org.eclipse.jgit.api.errors.JGitInternalException;
  18. import org.eclipse.jgit.api.errors.NoHeadException;
  19. import org.eclipse.jgit.diff.DiffEntry;
  20. import org.eclipse.jgit.diff.DiffFormatter;
  21. import org.eclipse.jgit.dircache.DirCacheIterator;
  22. import org.eclipse.jgit.internal.JGitText;
  23. import org.eclipse.jgit.lib.NullProgressMonitor;
  24. import org.eclipse.jgit.lib.ObjectId;
  25. import org.eclipse.jgit.lib.ObjectReader;
  26. import org.eclipse.jgit.lib.ProgressMonitor;
  27. import org.eclipse.jgit.lib.Repository;
  28. import org.eclipse.jgit.treewalk.AbstractTreeIterator;
  29. import org.eclipse.jgit.treewalk.CanonicalTreeParser;
  30. import org.eclipse.jgit.treewalk.FileTreeIterator;
  31. import org.eclipse.jgit.treewalk.filter.TreeFilter;
  32. import org.eclipse.jgit.util.io.NullOutputStream;

  33. /**
  34.  * Show changes between commits, commit and working tree, etc.
  35.  *
  36.  * @see <a href="http://www.kernel.org/pub/software/scm/git/docs/git-diff.html"
  37.  *      >Git documentation about diff</a>
  38.  */
  39. public class DiffCommand extends GitCommand<List<DiffEntry>> {
  40.     private AbstractTreeIterator oldTree;

  41.     private AbstractTreeIterator newTree;

  42.     private boolean cached;

  43.     private TreeFilter pathFilter = TreeFilter.ALL;

  44.     private boolean showNameAndStatusOnly;

  45.     private OutputStream out;

  46.     private int contextLines = -1;

  47.     private String sourcePrefix;

  48.     private String destinationPrefix;

  49.     private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;

  50.     /**
  51.      * Constructor for DiffCommand
  52.      *
  53.      * @param repo
  54.      *            a {@link org.eclipse.jgit.lib.Repository} object.
  55.      */
  56.     protected DiffCommand(Repository repo) {
  57.         super(repo);
  58.     }

  59.     private DiffFormatter getDiffFormatter() {
  60.         return out != null && !showNameAndStatusOnly
  61.                 ? new DiffFormatter(new BufferedOutputStream(out))
  62.                 : new DiffFormatter(NullOutputStream.INSTANCE);
  63.     }

  64.     /**
  65.      * {@inheritDoc}
  66.      * <p>
  67.      * Executes the {@code Diff} command with all the options and parameters
  68.      * collected by the setter methods (e.g. {@link #setCached(boolean)} of this
  69.      * class. Each instance of this class should only be used for one invocation
  70.      * of the command. Don't call this method twice on an instance.
  71.      */
  72.     @Override
  73.     public List<DiffEntry> call() throws GitAPIException {
  74.         try (DiffFormatter diffFmt = getDiffFormatter()) {
  75.             diffFmt.setRepository(repo);
  76.             diffFmt.setProgressMonitor(monitor);
  77.             if (cached) {
  78.                 if (oldTree == null) {
  79.                     ObjectId head = repo.resolve(HEAD + "^{tree}"); //$NON-NLS-1$
  80.                     if (head == null)
  81.                         throw new NoHeadException(JGitText.get().cannotReadTree);
  82.                     CanonicalTreeParser p = new CanonicalTreeParser();
  83.                     try (ObjectReader reader = repo.newObjectReader()) {
  84.                         p.reset(reader, head);
  85.                     }
  86.                     oldTree = p;
  87.                 }
  88.                 newTree = new DirCacheIterator(repo.readDirCache());
  89.             } else {
  90.                 if (oldTree == null) {
  91.                     oldTree = new DirCacheIterator(repo.readDirCache());
  92.                 }
  93.                 if (newTree == null) {
  94.                     newTree = new FileTreeIterator(repo);
  95.                 }
  96.             }

  97.             diffFmt.setPathFilter(pathFilter);

  98.             List<DiffEntry> result = diffFmt.scan(oldTree, newTree);
  99.             if (showNameAndStatusOnly) {
  100.                 return result;
  101.             }
  102.             if (contextLines >= 0) {
  103.                 diffFmt.setContext(contextLines);
  104.             }
  105.             if (destinationPrefix != null) {
  106.                 diffFmt.setNewPrefix(destinationPrefix);
  107.             }
  108.             if (sourcePrefix != null) {
  109.                 diffFmt.setOldPrefix(sourcePrefix);
  110.             }
  111.             diffFmt.format(result);
  112.             diffFmt.flush();
  113.             return result;
  114.         } catch (IOException e) {
  115.             throw new JGitInternalException(e.getMessage(), e);
  116.         }
  117.     }

  118.     /**
  119.      * Whether to view the changes staged for the next commit
  120.      *
  121.      * @param cached
  122.      *            whether to view the changes staged for the next commit
  123.      * @return this instance
  124.      */
  125.     public DiffCommand setCached(boolean cached) {
  126.         this.cached = cached;
  127.         return this;
  128.     }

  129.     /**
  130.      * Set path filter
  131.      *
  132.      * @param pathFilter
  133.      *            parameter, used to limit the diff to the named path
  134.      * @return this instance
  135.      */
  136.     public DiffCommand setPathFilter(TreeFilter pathFilter) {
  137.         this.pathFilter = pathFilter;
  138.         return this;
  139.     }

  140.     /**
  141.      * Set old tree
  142.      *
  143.      * @param oldTree
  144.      *            the previous state
  145.      * @return this instance
  146.      */
  147.     public DiffCommand setOldTree(AbstractTreeIterator oldTree) {
  148.         this.oldTree = oldTree;
  149.         return this;
  150.     }

  151.     /**
  152.      * Set new tree
  153.      *
  154.      * @param newTree
  155.      *            the updated state
  156.      * @return this instance
  157.      */
  158.     public DiffCommand setNewTree(AbstractTreeIterator newTree) {
  159.         this.newTree = newTree;
  160.         return this;
  161.     }

  162.     /**
  163.      * Set whether to return only names and status of changed files
  164.      *
  165.      * @param showNameAndStatusOnly
  166.      *            whether to return only names and status of changed files
  167.      * @return this instance
  168.      */
  169.     public DiffCommand setShowNameAndStatusOnly(boolean showNameAndStatusOnly) {
  170.         this.showNameAndStatusOnly = showNameAndStatusOnly;
  171.         return this;
  172.     }

  173.     /**
  174.      * Set output stream
  175.      *
  176.      * @param out
  177.      *            the stream to write line data
  178.      * @return this instance
  179.      */
  180.     public DiffCommand setOutputStream(OutputStream out) {
  181.         this.out = out;
  182.         return this;
  183.     }

  184.     /**
  185.      * Set number of context lines instead of the usual three.
  186.      *
  187.      * @param contextLines
  188.      *            the number of context lines
  189.      * @return this instance
  190.      */
  191.     public DiffCommand setContextLines(int contextLines) {
  192.         this.contextLines = contextLines;
  193.         return this;
  194.     }

  195.     /**
  196.      * Set the given source prefix instead of "a/".
  197.      *
  198.      * @param sourcePrefix
  199.      *            the prefix
  200.      * @return this instance
  201.      */
  202.     public DiffCommand setSourcePrefix(String sourcePrefix) {
  203.         this.sourcePrefix = sourcePrefix;
  204.         return this;
  205.     }

  206.     /**
  207.      * Set the given destination prefix instead of "b/".
  208.      *
  209.      * @param destinationPrefix
  210.      *            the prefix
  211.      * @return this instance
  212.      */
  213.     public DiffCommand setDestinationPrefix(String destinationPrefix) {
  214.         this.destinationPrefix = destinationPrefix;
  215.         return this;
  216.     }

  217.     /**
  218.      * The progress monitor associated with the diff operation. By default, this
  219.      * is set to <code>NullProgressMonitor</code>
  220.      *
  221.      * @see NullProgressMonitor
  222.      * @param monitor
  223.      *            a progress monitor
  224.      * @return this instance
  225.      */
  226.     public DiffCommand setProgressMonitor(ProgressMonitor monitor) {
  227.         if (monitor == null) {
  228.             monitor = NullProgressMonitor.INSTANCE;
  229.         }
  230.         this.monitor = monitor;
  231.         return this;
  232.     }
  233. }