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 boolean showNameOnly;

  46.     private OutputStream out;

  47.     private int contextLines = -1;

  48.     private String sourcePrefix;

  49.     private String destinationPrefix;

  50.     private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;

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

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

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

  98.             diffFmt.setPathFilter(pathFilter);

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

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

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

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

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

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

  174.     /**
  175.      * Set whether to return only names of changed files
  176.      *
  177.      * @param showNameOnly
  178.      *            whether to return only names files
  179.      * @return this instance
  180.      * @since 6.4
  181.      */
  182.     public DiffCommand setShowNameOnly(boolean showNameOnly) {
  183.         this.showNameOnly = showNameOnly;
  184.         return this;
  185.     }

  186.     /**
  187.      * Set output stream
  188.      *
  189.      * @param out
  190.      *            the stream to write line data
  191.      * @return this instance
  192.      */
  193.     public DiffCommand setOutputStream(OutputStream out) {
  194.         this.out = out;
  195.         return this;
  196.     }

  197.     /**
  198.      * Set number of context lines instead of the usual three.
  199.      *
  200.      * @param contextLines
  201.      *            the number of context lines
  202.      * @return this instance
  203.      */
  204.     public DiffCommand setContextLines(int contextLines) {
  205.         this.contextLines = contextLines;
  206.         return this;
  207.     }

  208.     /**
  209.      * Set the given source prefix instead of "a/".
  210.      *
  211.      * @param sourcePrefix
  212.      *            the prefix
  213.      * @return this instance
  214.      */
  215.     public DiffCommand setSourcePrefix(String sourcePrefix) {
  216.         this.sourcePrefix = sourcePrefix;
  217.         return this;
  218.     }

  219.     /**
  220.      * Set the given destination prefix instead of "b/".
  221.      *
  222.      * @param destinationPrefix
  223.      *            the prefix
  224.      * @return this instance
  225.      */
  226.     public DiffCommand setDestinationPrefix(String destinationPrefix) {
  227.         this.destinationPrefix = destinationPrefix;
  228.         return this;
  229.     }

  230.     /**
  231.      * The progress monitor associated with the diff operation. By default, this
  232.      * is set to <code>NullProgressMonitor</code>
  233.      *
  234.      * @see NullProgressMonitor
  235.      * @param monitor
  236.      *            a progress monitor
  237.      * @return this instance
  238.      */
  239.     public DiffCommand setProgressMonitor(ProgressMonitor monitor) {
  240.         if (monitor == null) {
  241.             monitor = NullProgressMonitor.INSTANCE;
  242.         }
  243.         this.monitor = monitor;
  244.         return this;
  245.     }
  246. }