# HG changeset patch # Parent 236d21a95856d3f4634ce1b12f60ea929f2731ef # User Ondrej Vrabec #227348 - API allowing to compare between different branches - compare any two revisions - compare working tree to any revision diff -r 236d21a95856 libs.git/apichanges.xml --- a/libs.git/apichanges.xml Tue Mar 12 11:37:35 2013 +0100 +++ b/libs.git/apichanges.xml Tue Mar 12 11:42:29 2013 +0100 @@ -111,6 +111,25 @@ + Adding support comparing trees of arbitrary commits + + + + + +
    +
  • Adding a new method GitClient.getStatus(File[], String, ProgressMonitor) + allowing an API client to get statuses of local files not just against the HEAD but against + an arbitrary commit.
  • +
  • Adding a new method GitClient.getStatus(File[], String, String, ProgressMonitor) + allowing an API client to get modifications from the Git history. It is similar to + GitRevisionInfo.getModifications but returns file statuses between two arbitrary commits + and not just a commit and its parent.
  • +
+
+
+ + Adding support for rebase to the API diff -r 236d21a95856 libs.git/manifest.mf --- a/libs.git/manifest.mf Tue Mar 12 11:37:35 2013 +0100 +++ b/libs.git/manifest.mf Tue Mar 12 11:42:29 2013 +0100 @@ -1,4 +1,4 @@ Manifest-Version: 1.0 OpenIDE-Module: org.netbeans.libs.git/1 OpenIDE-Module-Localizing-Bundle: org/netbeans/libs/git/Bundle.properties -OpenIDE-Module-Specification-Version: 1.8 +OpenIDE-Module-Specification-Version: 1.9 diff -r 236d21a95856 libs.git/src/org/netbeans/libs/git/GitClassFactoryImpl.java --- a/libs.git/src/org/netbeans/libs/git/GitClassFactoryImpl.java Tue Mar 12 11:37:35 2013 +0100 +++ b/libs.git/src/org/netbeans/libs/git/GitClassFactoryImpl.java Tue Mar 12 11:42:29 2013 +0100 @@ -60,6 +60,7 @@ import org.eclipse.jgit.transport.URIish; import org.netbeans.libs.git.GitConflictDescriptor.Type; import org.netbeans.libs.git.GitRevertResult.Status; +import org.netbeans.libs.git.GitRevisionInfo.GitFileInfo; import org.netbeans.libs.git.jgit.GitClassFactory; /** @@ -92,6 +93,11 @@ } @Override + public GitFileInfo createFileInfo (File file, String oldPath, GitFileInfo.Status status, File originalFile, String originalPath) { + return new GitFileInfo(file, oldPath, status, originalFile, originalPath); + } + + @Override public GitMergeResult createMergeResult (MergeResult mergeResult, File workTree) { return new GitMergeResult(mergeResult, workTree); } diff -r 236d21a95856 libs.git/src/org/netbeans/libs/git/GitClient.java --- a/libs.git/src/org/netbeans/libs/git/GitClient.java Tue Mar 12 11:37:35 2013 +0100 +++ b/libs.git/src/org/netbeans/libs/git/GitClient.java Tue Mar 12 11:42:29 2013 +0100 @@ -49,9 +49,11 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.RepositoryState; +import org.netbeans.libs.git.GitRevisionInfo.GitFileInfo; import org.netbeans.libs.git.jgit.GitClassFactory; import org.netbeans.libs.git.jgit.JGitCredentialsProvider; import org.netbeans.libs.git.jgit.JGitRepository; @@ -62,6 +64,7 @@ import org.netbeans.libs.git.jgit.commands.CheckoutRevisionCommand; import org.netbeans.libs.git.jgit.commands.CleanCommand; import org.netbeans.libs.git.jgit.commands.CommitCommand; +import org.netbeans.libs.git.jgit.commands.CompareCommand; import org.netbeans.libs.git.jgit.commands.ConflictCommand; import org.netbeans.libs.git.jgit.commands.CopyCommand; import org.netbeans.libs.git.jgit.commands.CreateBranchCommand; @@ -603,19 +606,56 @@ } /** - * Returns an array of statuses for files under given roots + * Compares the working tree with the current HEAD and returns an array of + * statuses for files under given roots + * * @param roots root folders or files to search under * @return status array * @throws GitException an unexpected error occurs */ public Map getStatus (File[] roots, ProgressMonitor monitor) throws GitException { + return getStatus(roots, Constants.HEAD, monitor); + } + + /** + * Compares working tree with a given revision and returns an array of + * statuses for files under given roots + * + * @param roots root folders or files to search under + * @param revision revision to compare with the working tree. If set + * to null HEAD will be used instead. + * @return status array + * @throws GitException an unexpected error occurs + * @since 1.9 + */ + public Map getStatus (File[] roots, String revision, ProgressMonitor monitor) throws GitException { Repository repository = gitRepository.getRepository(); - StatusCommand cmd = new StatusCommand(repository, getClassFactory(), roots, monitor, delegateListener); + StatusCommand cmd = new StatusCommand(repository, revision == null ? Constants.HEAD : revision, + roots, getClassFactory(), monitor, delegateListener); cmd.execute(); return cmd.getStatuses(); } /** + * Compares two different commit trees and returns an array of file + * modifications between revisionFirst and revisionSecond + * + * @param roots root folders or files to search under + * @param revisionFirst first revision to compare + * @param revisionSecond second revision to compare + * @return status array + * @throws GitException an unexpected error occurs + * @since 1.9 + */ + public Map getStatus (File[] roots, String revisionFirst, String revisionSecond, ProgressMonitor monitor) throws GitException { + Repository repository = gitRepository.getRepository(); + CompareCommand cmd = new CompareCommand(repository, revisionFirst, revisionSecond, roots, + getClassFactory(), monitor); + cmd.execute(); + return cmd.getFileDifferences(); + } + + /** * Returns remote configuration set up for this repository identified by a given remoteName * @param remoteName name under which the remote is stored in repository's config file * @param monitor progress monitor diff -r 236d21a95856 libs.git/src/org/netbeans/libs/git/GitRevisionInfo.java --- a/libs.git/src/org/netbeans/libs/git/GitRevisionInfo.java Tue Mar 12 11:37:35 2013 +0100 +++ b/libs.git/src/org/netbeans/libs/git/GitRevisionInfo.java Tue Mar 12 11:42:29 2013 +0100 @@ -50,8 +50,6 @@ import java.util.List; import java.util.Map; import java.util.logging.Logger; -import org.eclipse.jgit.diff.DiffEntry; -import org.eclipse.jgit.diff.RenameDetector; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.Repository; @@ -62,6 +60,7 @@ import org.eclipse.jgit.treewalk.filter.AndTreeFilter; import org.eclipse.jgit.treewalk.filter.PathFilter; import org.eclipse.jgit.treewalk.filter.TreeFilter; +import org.netbeans.libs.git.jgit.Utils; /** * Provides information about a certain commit, usually is returned by @@ -164,7 +163,7 @@ RevWalk revWalk = new RevWalk(repository); TreeWalk walk = new TreeWalk(repository); try { - ArrayList result = new ArrayList(); + List result; walk.reset(); walk.setRecursive(true); RevCommit parentCommit = null; @@ -184,52 +183,9 @@ walk.addTree(revCommit.getTree().getId()); walk.setFilter(AndTreeFilter.create(TreeFilter.ANY_DIFF, PathFilter.ANY_DIFF)); if (parentCommit != null) { - List entries = DiffEntry.scan(walk); - RenameDetector rd = new RenameDetector(repository); - rd.addAll(entries); - entries = rd.compute(); - for (DiffEntry e : entries) { - GitFileInfo.Status status; - File oldFile = null; - String oldPath = null; - String path = e.getOldPath(); - if (path == null) { - path = e.getNewPath(); - } - switch (e.getChangeType()) { - case ADD: - status = GitFileInfo.Status.ADDED; - path = e.getNewPath(); - break; - case COPY: - status = GitFileInfo.Status.COPIED; - oldFile = new File(repository.getWorkTree(), e.getOldPath()); - oldPath = e.getOldPath(); - path = e.getNewPath(); - break; - case DELETE: - status = GitFileInfo.Status.REMOVED; - path = e.getOldPath(); - break; - case MODIFY: - status = GitFileInfo.Status.MODIFIED; - path = e.getOldPath(); - break; - case RENAME: - status = GitFileInfo.Status.RENAMED; - oldFile = new File(repository.getWorkTree(), e.getOldPath()); - oldPath = e.getOldPath(); - path = e.getNewPath(); - break; - default: - status = GitFileInfo.Status.UNKNOWN; - } - if (status == GitFileInfo.Status.RENAMED) { - result.add(new GitFileInfo(new File(repository.getWorkTree(), e.getOldPath()), e.getOldPath(), GitFileInfo.Status.REMOVED, null, null)); - } - result.add(new GitFileInfo(new File(repository.getWorkTree(), path), path, status, oldFile, oldPath)); - } + result = Utils.getDiffEntries(repository, walk, GitClassFactoryImpl.getInstance()); } else { + result = new ArrayList(); while (walk.next()) { result.add(new GitFileInfo(new File(repository.getWorkTree(), walk.getPathString()), walk.getPathString(), GitFileInfo.Status.ADDED, null, null)); } diff -r 236d21a95856 libs.git/src/org/netbeans/libs/git/jgit/GitClassFactory.java --- a/libs.git/src/org/netbeans/libs/git/jgit/GitClassFactory.java Tue Mar 12 11:37:35 2013 +0100 +++ b/libs.git/src/org/netbeans/libs/git/jgit/GitClassFactory.java Tue Mar 12 11:42:29 2013 +0100 @@ -69,6 +69,7 @@ import org.netbeans.libs.git.GitRemoteConfig; import org.netbeans.libs.git.GitRevertResult; import org.netbeans.libs.git.GitRevisionInfo; +import org.netbeans.libs.git.GitRevisionInfo.GitFileInfo; import org.netbeans.libs.git.GitStatus; import org.netbeans.libs.git.GitStatus.Status; import org.netbeans.libs.git.GitTag; @@ -86,6 +87,8 @@ public abstract GitBranch createBranch (String name, boolean remote, boolean active, ObjectId id); public abstract GitConflictDescriptor createConflictDescriptor (Type type); + + public abstract GitFileInfo createFileInfo (File file, String oldPath, GitFileInfo.Status status, File originalFile, String originalPath); public abstract GitMergeResult createMergeResult (MergeResult mergeResult, File workTree); diff -r 236d21a95856 libs.git/src/org/netbeans/libs/git/jgit/Utils.java --- a/libs.git/src/org/netbeans/libs/git/jgit/Utils.java Tue Mar 12 11:37:35 2013 +0100 +++ b/libs.git/src/org/netbeans/libs/git/jgit/Utils.java Tue Mar 12 11:42:29 2013 +0100 @@ -55,6 +55,8 @@ import java.util.ResourceBundle; import java.util.logging.Level; import java.util.logging.Logger; +import org.eclipse.jgit.diff.DiffEntry; +import org.eclipse.jgit.diff.RenameDetector; import org.eclipse.jgit.errors.AmbiguousObjectException; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.MissingObjectException; @@ -79,6 +81,8 @@ import org.netbeans.libs.git.GitBranch; import org.netbeans.libs.git.GitException; import org.netbeans.libs.git.GitObjectType; +import org.netbeans.libs.git.GitRevisionInfo; +import org.netbeans.libs.git.GitRevisionInfo.GitFileInfo; /** * @@ -119,6 +123,56 @@ return filter; } + public static List getDiffEntries (Repository repository, TreeWalk walk, GitClassFactory fac) throws IOException { + List result = new ArrayList(); + List entries = DiffEntry.scan(walk); + RenameDetector rd = new RenameDetector(repository); + rd.addAll(entries); + entries = rd.compute(); + for (DiffEntry e : entries) { + GitRevisionInfo.GitFileInfo.Status status; + File oldFile = null; + String oldPath = null; + String path = e.getOldPath(); + if (path == null) { + path = e.getNewPath(); + } + switch (e.getChangeType()) { + case ADD: + status = GitRevisionInfo.GitFileInfo.Status.ADDED; + path = e.getNewPath(); + break; + case COPY: + status = GitRevisionInfo.GitFileInfo.Status.COPIED; + oldFile = new File(repository.getWorkTree(), e.getOldPath()); + oldPath = e.getOldPath(); + path = e.getNewPath(); + break; + case DELETE: + status = GitRevisionInfo.GitFileInfo.Status.REMOVED; + path = e.getOldPath(); + break; + case MODIFY: + status = GitRevisionInfo.GitFileInfo.Status.MODIFIED; + path = e.getOldPath(); + break; + case RENAME: + status = GitRevisionInfo.GitFileInfo.Status.RENAMED; + oldFile = new File(repository.getWorkTree(), e.getOldPath()); + oldPath = e.getOldPath(); + path = e.getNewPath(); + break; + default: + status = GitRevisionInfo.GitFileInfo.Status.UNKNOWN; + } + if (status == GitRevisionInfo.GitFileInfo.Status.RENAMED) { + result.add(fac.createFileInfo(new File(repository.getWorkTree(), e.getOldPath()), e.getOldPath(), GitRevisionInfo.GitFileInfo.Status.REMOVED, null, null)); + } + result.add(fac.createFileInfo(new File(repository.getWorkTree(), path), path, status, oldFile, oldPath)); + } + return result; + } + private static Collection getPathFilters (Collection relativePaths) { Collection filters = new LinkedList(); for (String path : relativePaths) { diff -r 236d21a95856 libs.git/src/org/netbeans/libs/git/jgit/commands/CompareCommand.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs.git/src/org/netbeans/libs/git/jgit/commands/CompareCommand.java Tue Mar 12 11:42:29 2013 +0100 @@ -0,0 +1,130 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2010 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2010 Sun Microsystems, Inc. + */ + +package org.netbeans.libs.git.jgit.commands; + +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.treewalk.*; +import org.eclipse.jgit.treewalk.filter.AndTreeFilter; +import org.eclipse.jgit.treewalk.filter.PathFilter; +import org.eclipse.jgit.treewalk.filter.PathFilterGroup; +import org.eclipse.jgit.treewalk.filter.TreeFilter; +import org.netbeans.libs.git.GitException; +import org.netbeans.libs.git.GitRevisionInfo; +import org.netbeans.libs.git.jgit.GitClassFactory; +import org.netbeans.libs.git.jgit.Utils; +import org.netbeans.libs.git.progress.ProgressMonitor; + +/** + * + * @author ondra + */ +public class CompareCommand extends GitCommand { + private final LinkedHashMap statuses; + private final File[] roots; + private final String revisionFirst; + private final String revisionSecond; + + public CompareCommand (Repository repository, String revisionFirst, String revisionSecond, File[] roots, + GitClassFactory gitFactory, ProgressMonitor monitor) { + super(repository, gitFactory, monitor); + this.roots = roots; + this.revisionFirst = revisionFirst; + this.revisionSecond = revisionSecond; + statuses = new LinkedHashMap(); + } + + @Override + protected String getCommandDescription () { + StringBuilder sb = new StringBuilder("git diff --raw"); //NOI18N + sb.append(revisionFirst).append(' ').append(revisionSecond); + for (File root : roots) { + sb.append(" ").append(root.getAbsolutePath()); + } + return sb.toString(); + } + + @Override + protected boolean prepareCommand () throws GitException { + return getRepository().getDirectory().exists(); + } + + @Override + protected void run () throws GitException { + Repository repository = getRepository(); + TreeWalk walk = new TreeWalk(repository); + try { + walk.reset(); + walk.setRecursive(true); + walk.addTree(Utils.findCommit(repository, revisionFirst).getTree()); + walk.addTree(Utils.findCommit(repository, revisionSecond).getTree()); + Collection pathFilters = Utils.getPathFilters(repository.getWorkTree(), roots); + if (pathFilters.isEmpty()) { + walk.setFilter(AndTreeFilter.create(TreeFilter.ANY_DIFF, PathFilter.ANY_DIFF)); + } else { + walk.setFilter(AndTreeFilter.create(new TreeFilter[] { + TreeFilter.ANY_DIFF, + PathFilter.ANY_DIFF, + PathFilterGroup.create(pathFilters) + })); + } + List infos = Utils.getDiffEntries(repository, walk, getClassFactory()); + for (GitRevisionInfo.GitFileInfo info : infos) { + statuses.put(info.getFile(), info); + } + } catch (IOException ex) { + throw new GitException(ex); + } finally { + walk.release(); + } + } + + public Map getFileDifferences () { + return statuses; + } +} diff -r 236d21a95856 libs.git/src/org/netbeans/libs/git/jgit/commands/ConflictCommand.java --- a/libs.git/src/org/netbeans/libs/git/jgit/commands/ConflictCommand.java Tue Mar 12 11:37:35 2013 +0100 +++ b/libs.git/src/org/netbeans/libs/git/jgit/commands/ConflictCommand.java Tue Mar 12 11:42:29 2013 +0100 @@ -49,6 +49,7 @@ import org.eclipse.jgit.dircache.DirCacheEntry; import org.eclipse.jgit.dircache.DirCacheIterator; import org.eclipse.jgit.errors.CorruptObjectException; +import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.treewalk.filter.PathFilter; @@ -72,7 +73,7 @@ private final File[] roots; public ConflictCommand (Repository repository, GitClassFactory gitFactory, File[] roots, ProgressMonitor monitor, StatusListener listener) { - super(repository, gitFactory, roots, monitor, listener); + super(repository, Constants.HEAD, roots, gitFactory, monitor, listener); this.monitor = monitor; this.listener = listener; this.roots = roots; diff -r 236d21a95856 libs.git/src/org/netbeans/libs/git/jgit/commands/RebaseCommand.java --- a/libs.git/src/org/netbeans/libs/git/jgit/commands/RebaseCommand.java Tue Mar 12 11:37:35 2013 +0100 +++ b/libs.git/src/org/netbeans/libs/git/jgit/commands/RebaseCommand.java Tue Mar 12 11:42:29 2013 +0100 @@ -162,8 +162,8 @@ Repository repository = getRepository(); GitRevisionInfo info = getClassFactory().createRevisionInfo(currentCommit, repository); Map modifiedFiles = info.getModifiedFiles(); - StatusCommand cmd = new StatusCommand(repository, getClassFactory(), modifiedFiles.keySet().toArray( - new File[modifiedFiles.keySet().size()]), + StatusCommand cmd = new StatusCommand(repository, Constants.HEAD, modifiedFiles.keySet().toArray( + new File[modifiedFiles.keySet().size()]), getClassFactory(), new DelegatingGitProgressMonitor(monitor), new StatusListener() { @Override diff -r 236d21a95856 libs.git/src/org/netbeans/libs/git/jgit/commands/StatusCommand.java --- a/libs.git/src/org/netbeans/libs/git/jgit/commands/StatusCommand.java Tue Mar 12 11:37:35 2013 +0100 +++ b/libs.git/src/org/netbeans/libs/git/jgit/commands/StatusCommand.java Tue Mar 12 11:42:29 2013 +0100 @@ -95,19 +95,27 @@ private final File[] roots; private final ProgressMonitor monitor; private final StatusListener listener; + private final String revision; private static final String PROP_TRACK_SYMLINKS = "org.netbeans.libs.git.trackSymLinks"; //NOI18N - public StatusCommand (Repository repository, GitClassFactory gitFactory, File[] roots, ProgressMonitor monitor, StatusListener listener) { + public StatusCommand (Repository repository, String revision, File[] roots, GitClassFactory gitFactory, + ProgressMonitor monitor, StatusListener listener) { super(repository, gitFactory, monitor); this.roots = roots; this.monitor = monitor; this.listener = listener; + this.revision = revision; statuses = new LinkedHashMap(); } @Override protected String getCommandDescription () { - StringBuilder sb = new StringBuilder("git status"); //NOI18N + StringBuilder sb = new StringBuilder("git "); //NOI18N + if (Constants.HEAD.equals(revision)) { + sb.append("status"); //NOI18N + } else { + sb.append("diff --raw"); //NOI18N + } for (File root : roots) { sb.append(" ").append(root.getAbsolutePath()); } @@ -128,16 +136,16 @@ try { String workTreePath = repository.getWorkTree().getAbsolutePath(); Collection pathFilters = Utils.getPathFilters(repository.getWorkTree(), roots); - Map renames = detectRenames(repository, cache); + ObjectId commitId = Utils.parseObjectId(repository, revision); + Map renames = detectRenames(repository, cache, commitId); TreeWalk treeWalk = new TreeWalk(repository); if (!pathFilters.isEmpty()) { treeWalk.setFilter(PathFilterGroup.create(pathFilters)); } treeWalk.setRecursive(false); treeWalk.reset(); - ObjectId headId = repository.resolve(Constants.HEAD); - if (headId != null) { - treeWalk.addTree(new RevWalk(repository).parseTree(headId)); + if (commitId != null) { + treeWalk.addTree(new RevWalk(repository).parseTree(commitId)); } else { treeWalk.addTree(new EmptyTreeIterator()); } @@ -145,7 +153,7 @@ treeWalk.addTree(new DirCacheIterator(cache)); // Working directory treeWalk.addTree(new FileTreeIterator(repository)); - final int T_HEAD = 0; + final int T_COMMIT = 0; final int T_INDEX = 1; final int T_WORKSPACE = 2; String lastPath = null; @@ -166,7 +174,7 @@ } lastPath = path; File file = new File(workTreePath + File.separator + path); - int mHead = treeWalk.getRawMode(T_HEAD); + int mHead = treeWalk.getRawMode(T_COMMIT); int mIndex = treeWalk.getRawMode(T_INDEX); int mWorking = treeWalk.getRawMode(T_WORKSPACE); GitStatus.Status statusHeadIndex; @@ -177,7 +185,7 @@ statusHeadIndex = GitStatus.Status.STATUS_ADDED; } else if (mIndex == FileMode.MISSING.getBits() && mHead != FileMode.MISSING.getBits()) { statusHeadIndex = GitStatus.Status.STATUS_REMOVED; - } else if (mHead != mIndex || (mIndex != FileMode.TREE.getBits() && !treeWalk.idEqual(T_HEAD, T_INDEX))) { + } else if (mHead != mIndex || (mIndex != FileMode.TREE.getBits() && !treeWalk.idEqual(T_COMMIT, T_INDEX))) { statusHeadIndex = GitStatus.Status.STATUS_MODIFIED; } else { statusHeadIndex = GitStatus.Status.STATUS_NORMAL; @@ -234,7 +242,7 @@ && (indexEntry == null || !indexEntry.isAssumeValid()) //no update-index --assume-unchanged // head vs wt can be modified only when head vs index or index vs wt are modified, otherwise it's probably line-endings issue && (statusIndexWC != GitStatus.Status.STATUS_NORMAL || statusHeadIndex != GitStatus.Status.STATUS_NORMAL) - && !treeWalk.getObjectId(T_HEAD).equals(fti.getEntryObjectId())))) { + && !treeWalk.getObjectId(T_COMMIT).equals(fti.getEntryObjectId())))) { statusHeadWC = GitStatus.Status.STATUS_MODIFIED; } else { statusHeadWC = GitStatus.Status.STATUS_NORMAL; @@ -271,15 +279,14 @@ return statuses; } - private Map detectRenames (Repository repository, DirCache cache) { + private Map detectRenames (Repository repository, DirCache cache, ObjectId commitId) { List entries; TreeWalk treeWalk = new TreeWalk(repository); try { treeWalk.setRecursive(true); treeWalk.reset(); - ObjectId headId = repository.resolve(Constants.HEAD); - if (headId != null) { - treeWalk.addTree(new RevWalk(repository).parseTree(headId)); + if (commitId != null) { + treeWalk.addTree(new RevWalk(repository).parseTree(commitId)); } else { treeWalk.addTree(new EmptyTreeIterator()); } diff -r 236d21a95856 libs.git/test/unit/src/org/netbeans/libs/git/jgit/CommandsTestSuite.java --- a/libs.git/test/unit/src/org/netbeans/libs/git/jgit/CommandsTestSuite.java Tue Mar 12 11:37:35 2013 +0100 +++ b/libs.git/test/unit/src/org/netbeans/libs/git/jgit/CommandsTestSuite.java Tue Mar 12 11:42:29 2013 +0100 @@ -53,6 +53,7 @@ import org.netbeans.libs.git.jgit.commands.CheckoutTest; import org.netbeans.libs.git.jgit.commands.CleanTest; import org.netbeans.libs.git.jgit.commands.CommitTest; +import org.netbeans.libs.git.jgit.commands.CompareCommitTest; import org.netbeans.libs.git.jgit.commands.CopyTest; import org.netbeans.libs.git.jgit.commands.ExportCommitTest; import org.netbeans.libs.git.jgit.commands.ExportDiffTest; @@ -97,6 +98,7 @@ suite.addTestSuite(CheckoutTest.class); suite.addTestSuite(CleanTest.class); suite.addTestSuite(CommitTest.class); + suite.addTestSuite(CompareCommitTest.class); suite.addTestSuite(CopyTest.class); suite.addTestSuite(ExportCommitTest.class); suite.addTestSuite(ExportDiffTest.class); diff -r 236d21a95856 libs.git/test/unit/src/org/netbeans/libs/git/jgit/commands/CompareCommitTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs.git/test/unit/src/org/netbeans/libs/git/jgit/commands/CompareCommitTest.java Tue Mar 12 11:42:29 2013 +0100 @@ -0,0 +1,181 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2010 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2010 Sun Microsystems, Inc. + */ + +package org.netbeans.libs.git.jgit.commands; + +import java.io.File; +import java.io.IOException; +import java.util.Map; +import static junit.framework.Assert.assertEquals; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.Repository; +import org.netbeans.libs.git.GitClient; +import org.netbeans.libs.git.GitRevisionInfo; +import org.netbeans.libs.git.GitRevisionInfo.GitFileInfo.Status; +import org.netbeans.libs.git.jgit.AbstractGitTestCase; + +/** + * + * @author ondra + */ +public class CompareCommitTest extends AbstractGitTestCase { + private Repository repository; + private File workDir; + + public CompareCommitTest (String testName) throws IOException { + super(testName); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + workDir = getWorkingDirectory(); + repository = getRepository(getLocalGitRepository()); + } + + public void testCompareSimple () throws Exception { + File file = new File(workDir, "file"); + File[] files = new File[] { file }; + write(file, "init\n"); + add(files); + commit(files); + + GitClient client = getClient(workDir); + String revision1 = client.getBranches(false, NULL_PROGRESS_MONITOR).get(Constants.MASTER).getId(); + write(file, "modification\n"); + add(files); + GitRevisionInfo revision2 = client.commit(files, "my commit message", null, null, NULL_PROGRESS_MONITOR); + Map statuses = client.getStatus(files, Constants.HEAD, revision1, NULL_PROGRESS_MONITOR); + assertEquals(1, statuses.size()); + assertEquals(Status.MODIFIED, statuses.get(file).getStatus()); + + write(file, "modification 2\n"); + add(files); + GitRevisionInfo revision3 = client.commit(files, "my commit 3 message", null, null, NULL_PROGRESS_MONITOR); + statuses = client.getStatus(files, revision1, Constants.HEAD, NULL_PROGRESS_MONITOR); + assertEquals(1, statuses.size()); + assertEquals(Status.MODIFIED, statuses.get(file).getStatus()); + + statuses = client.getStatus(files, revision1, revision3.getRevision(), NULL_PROGRESS_MONITOR); + assertEquals(1, statuses.size()); + assertEquals(Status.MODIFIED, statuses.get(file).getStatus()); + + statuses = client.getStatus(files, revision2.getRevision(), revision3.getRevision(), NULL_PROGRESS_MONITOR); + assertEquals(1, statuses.size()); + assertEquals(Status.MODIFIED, statuses.get(file).getStatus()); + } + + public void testCompareRevertModification () throws Exception { + File file = new File(workDir, "file"); + File[] files = new File[] { file }; + write(file, "init\n"); + add(files); + commit(files); + + GitClient client = getClient(workDir); + String revision1 = client.getBranches(false, NULL_PROGRESS_MONITOR).get(Constants.MASTER).getId(); + write(file, "modification\n"); + add(files); + GitRevisionInfo revision2 = client.commit(files, "my commit message", null, null, NULL_PROGRESS_MONITOR); + + write(file, "init\n"); + add(files); + GitRevisionInfo revision3 = client.commit(files, "my commit 3 message", null, null, NULL_PROGRESS_MONITOR); + Map statuses = client.getStatus(files, revision1, Constants.HEAD, NULL_PROGRESS_MONITOR); + assertTrue(statuses.isEmpty()); + } + + public void testCompareSelection () throws Exception { + File file = new File(workDir, "file"); + File file2 = new File(workDir, "file2"); + File[] files = new File[] { file, file2 }; + write(file, "init\n"); + add(files); + commit(files); + + GitClient client = getClient(workDir); + String revision1 = client.getBranches(false, NULL_PROGRESS_MONITOR).get(Constants.MASTER).getId(); + write(file, "modification\n"); + add(files); + GitRevisionInfo revision2 = client.commit(files, "my commit message", null, null, NULL_PROGRESS_MONITOR); + + write(file2, "adding file 2\n"); + add(files); + GitRevisionInfo revision3 = client.commit(files, "adding file 2", null, null, NULL_PROGRESS_MONITOR); + Map statuses = client.getStatus(new File[] { file }, revision1, revision3.getRevision(), NULL_PROGRESS_MONITOR); + assertEquals(1, statuses.size()); + assertEquals(Status.MODIFIED, statuses.get(file).getStatus()); + + statuses = client.getStatus(new File[] { file2 }, revision1, revision3.getRevision(), NULL_PROGRESS_MONITOR); + assertEquals(1, statuses.size()); + assertEquals(Status.ADDED, statuses.get(file2).getStatus()); + + statuses = client.getStatus(files, revision1, revision3.getRevision(), NULL_PROGRESS_MONITOR); + assertEquals(2, statuses.size()); + assertEquals(Status.MODIFIED, statuses.get(file).getStatus()); + assertEquals(Status.ADDED, statuses.get(file2).getStatus()); + } + + public void testCompareRename () throws Exception { + File file = new File(workDir, "file"); + File file2 = new File(workDir, "file2"); + File[] files = new File[] { file, file2 }; + write(file, "files content\n"); + add(files); + commit(files); + + GitClient client = getClient(workDir); + String revision1 = client.getBranches(false, NULL_PROGRESS_MONITOR).get(Constants.MASTER).getId(); + remove(false, file); + + client.commit(files, "removing file", null, null, NULL_PROGRESS_MONITOR); + + write(file2, "files content\n"); + add(files); + GitRevisionInfo revision3 = client.commit(files, "adding file as file2", null, null, NULL_PROGRESS_MONITOR); + Map statuses = client.getStatus(files, revision1, revision3.getRevision(), NULL_PROGRESS_MONITOR); + assertEquals(2, statuses.size()); + assertEquals(Status.REMOVED, statuses.get(file).getStatus()); + assertEquals(Status.RENAMED, statuses.get(file2).getStatus()); + assertEquals(file, statuses.get(file2).getOriginalFile()); + } +} diff -r 236d21a95856 libs.git/test/unit/src/org/netbeans/libs/git/jgit/commands/StatusTest.java --- a/libs.git/test/unit/src/org/netbeans/libs/git/jgit/commands/StatusTest.java Tue Mar 12 11:37:35 2013 +0100 +++ b/libs.git/test/unit/src/org/netbeans/libs/git/jgit/commands/StatusTest.java Tue Mar 12 11:42:29 2013 +0100 @@ -45,11 +45,12 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; -import org.eclipse.jgit.api.Git; +import static junit.framework.Assert.assertFalse; import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.dircache.DirCacheBuilder; import org.eclipse.jgit.dircache.DirCacheEntry; import org.eclipse.jgit.lib.ConfigConstants; +import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.StoredConfig; import org.netbeans.libs.git.GitClient; @@ -166,6 +167,120 @@ // what about isIgnored() here? assertStatus(statuses, workDir, ignored, false, Status.STATUS_NORMAL, Status.STATUS_IGNORED, Status.STATUS_ADDED, false, listener); } + + // diff WT against a commit other than HEAD + public void testMiscStatusCommit () throws Exception { + write(new File(workDir, ".gitignore"), "ignored"); + File untracked = new File(workDir, "untracked"); + write(untracked, "untracked"); + File ignored = new File(workDir, "ignored"); + write(ignored, "ignored"); + File added_uptodate = new File(workDir, "added-uptodate"); + write(added_uptodate, "added-uptodate"); + File added_modified = new File(workDir, "added-modified"); + write(added_modified, "added_modified"); + File added_deleted = new File(workDir, "added-deleted"); + write(added_deleted, "added_deleted"); + + File uptodate_uptodate = new File(workDir, "uptodate-uptodate"); + write(uptodate_uptodate, "uptodate_uptodate"); + File uptodate_modified = new File(workDir, "uptodate-modified"); + write(uptodate_modified, "uptodate_modified"); + File uptodate_deleted = new File(workDir, "uptodate-deleted"); + write(uptodate_deleted, "uptodate_deleted"); + + File modified_uptodate = new File(workDir, "modified-uptodate"); + write(modified_uptodate, "modified_uptodate"); + File modified_modified = new File(workDir, "modified-modified"); + write(modified_modified, "modified_modified"); + File modified_reset = new File(workDir, "modified-reset"); + write(modified_reset, "modified_reset"); + File modified_deleted = new File(workDir, "modified-deleted"); + write(modified_deleted, "modified_deleted"); + + // we cannot + File deleted_uptodate = new File(workDir, "deleted-uptodate"); + write(deleted_uptodate, "deleted_uptodate"); + File deleted_untracked = new File(workDir, "deleted-untracked"); + write(deleted_untracked, "deleted_untracked"); + File deleted_modified = new File(workDir, "deleted-modified"); + write(deleted_modified, "deleted_modified"); + + add(uptodate_uptodate, uptodate_modified, uptodate_deleted, modified_uptodate, modified_modified, modified_reset, modified_deleted, deleted_uptodate, deleted_untracked, deleted_modified); + commit(workDir); + add(added_uptodate, added_modified, added_deleted); + write(modified_deleted, "modification modified_deleted"); + write(modified_modified, "modification modified_modified"); + write(modified_reset, "modification modified_reset"); + write(modified_uptodate, "modification modified_uptodate"); + add(modified_deleted, modified_modified, modified_reset, modified_uptodate); + deleted_uptodate.delete(); + deleted_untracked.delete(); + deleted_modified.delete(); + remove(true, deleted_uptodate, deleted_untracked, deleted_modified); + write(added_modified, "modification2 added_modified"); + write(uptodate_modified, "modification2 uptodate_modified"); + write(modified_modified, "modification2 modified_modified"); + write(modified_reset, "modified_reset"); + added_deleted.delete(); + modified_deleted.delete(); + uptodate_deleted.delete(); + write(deleted_untracked, "deleted_untracked"); + write(deleted_modified, "deleted_modified\nchange"); + + GitClient client = getClient(workDir); + String revId = client.getBranches(false, NULL_PROGRESS_MONITOR).get(Constants.MASTER).getId(); + + File someFile = new File(workDir, "fileforothercommit"); + write(someFile, "fileforothercommit"); + add(someFile); + commit(someFile); + + TestStatusListener listener = new TestStatusListener(); + client.addNotificationListener(listener); + Map statuses = client.getStatus(new File[] { workDir }, Constants.HEAD, NULL_PROGRESS_MONITOR); + assertFalse(statuses.isEmpty()); + assertStatus(statuses, workDir, untracked, false, Status.STATUS_NORMAL, Status.STATUS_ADDED, Status.STATUS_ADDED, false, listener); + assertStatus(statuses, workDir, added_uptodate, true, Status.STATUS_ADDED, Status.STATUS_NORMAL, Status.STATUS_ADDED, false, listener); + assertStatus(statuses, workDir, added_modified, true, Status.STATUS_ADDED, Status.STATUS_MODIFIED, Status.STATUS_ADDED, false, listener); + assertStatus(statuses, workDir, added_deleted, true, Status.STATUS_ADDED, Status.STATUS_REMOVED, Status.STATUS_NORMAL, false, listener); + assertStatus(statuses, workDir, uptodate_uptodate, true, Status.STATUS_NORMAL, Status.STATUS_NORMAL, Status.STATUS_NORMAL, false, listener); + assertStatus(statuses, workDir, uptodate_modified, true, Status.STATUS_NORMAL, Status.STATUS_MODIFIED, Status.STATUS_MODIFIED, false, listener); + assertStatus(statuses, workDir, uptodate_deleted, true, Status.STATUS_NORMAL, Status.STATUS_REMOVED, Status.STATUS_REMOVED, false, listener); + assertStatus(statuses, workDir, modified_uptodate, true, Status.STATUS_MODIFIED, Status.STATUS_NORMAL, Status.STATUS_MODIFIED, false, listener); + assertStatus(statuses, workDir, modified_modified, true, Status.STATUS_MODIFIED, Status.STATUS_MODIFIED, Status.STATUS_MODIFIED, false, listener); + assertStatus(statuses, workDir, modified_reset, true, Status.STATUS_MODIFIED, Status.STATUS_MODIFIED, Status.STATUS_NORMAL, false, listener); + assertStatus(statuses, workDir, modified_deleted, true, Status.STATUS_MODIFIED, Status.STATUS_REMOVED, Status.STATUS_REMOVED, false, listener); + assertStatus(statuses, workDir, deleted_uptodate, true, Status.STATUS_REMOVED, Status.STATUS_NORMAL, Status.STATUS_REMOVED, false, listener); + assertStatus(statuses, workDir, deleted_untracked, true, Status.STATUS_REMOVED, Status.STATUS_ADDED, Status.STATUS_NORMAL, false, listener); + assertStatus(statuses, workDir, deleted_modified, true, Status.STATUS_REMOVED, Status.STATUS_ADDED, Status.STATUS_MODIFIED, false, listener); + assertStatus(statuses, workDir, someFile, true, Status.STATUS_NORMAL, Status.STATUS_NORMAL, Status.STATUS_NORMAL, false, listener); + // what about isIgnored() here? + assertStatus(statuses, workDir, ignored, false, Status.STATUS_NORMAL, Status.STATUS_IGNORED, Status.STATUS_ADDED, false, listener); + + listener = new TestStatusListener(); + client.addNotificationListener(listener); + statuses = client.getStatus(new File[] { workDir }, revId, NULL_PROGRESS_MONITOR); + assertFalse(statuses.isEmpty()); + assertStatus(statuses, workDir, untracked, false, Status.STATUS_NORMAL, Status.STATUS_ADDED, Status.STATUS_ADDED, false, listener); + assertStatus(statuses, workDir, added_uptodate, true, Status.STATUS_ADDED, Status.STATUS_NORMAL, Status.STATUS_ADDED, false, listener); + assertStatus(statuses, workDir, added_modified, true, Status.STATUS_ADDED, Status.STATUS_MODIFIED, Status.STATUS_ADDED, false, listener); + assertStatus(statuses, workDir, added_deleted, true, Status.STATUS_ADDED, Status.STATUS_REMOVED, Status.STATUS_NORMAL, false, listener); + assertStatus(statuses, workDir, uptodate_uptodate, true, Status.STATUS_NORMAL, Status.STATUS_NORMAL, Status.STATUS_NORMAL, false, listener); + assertStatus(statuses, workDir, uptodate_modified, true, Status.STATUS_NORMAL, Status.STATUS_MODIFIED, Status.STATUS_MODIFIED, false, listener); + assertStatus(statuses, workDir, uptodate_deleted, true, Status.STATUS_NORMAL, Status.STATUS_REMOVED, Status.STATUS_REMOVED, false, listener); + assertStatus(statuses, workDir, modified_uptodate, true, Status.STATUS_MODIFIED, Status.STATUS_NORMAL, Status.STATUS_MODIFIED, false, listener); + assertStatus(statuses, workDir, modified_modified, true, Status.STATUS_MODIFIED, Status.STATUS_MODIFIED, Status.STATUS_MODIFIED, false, listener); + assertStatus(statuses, workDir, modified_reset, true, Status.STATUS_MODIFIED, Status.STATUS_MODIFIED, Status.STATUS_NORMAL, false, listener); + assertStatus(statuses, workDir, modified_deleted, true, Status.STATUS_MODIFIED, Status.STATUS_REMOVED, Status.STATUS_REMOVED, false, listener); + assertStatus(statuses, workDir, deleted_uptodate, true, Status.STATUS_REMOVED, Status.STATUS_NORMAL, Status.STATUS_REMOVED, false, listener); + assertStatus(statuses, workDir, deleted_untracked, true, Status.STATUS_REMOVED, Status.STATUS_ADDED, Status.STATUS_NORMAL, false, listener); + assertStatus(statuses, workDir, deleted_modified, true, Status.STATUS_REMOVED, Status.STATUS_ADDED, Status.STATUS_MODIFIED, false, listener); + // what about isIgnored() here? + assertStatus(statuses, workDir, ignored, false, Status.STATUS_NORMAL, Status.STATUS_IGNORED, Status.STATUS_ADDED, false, listener); + // file somefile was not known in that revision + assertStatus(statuses, workDir, someFile, true, Status.STATUS_ADDED, Status.STATUS_NORMAL, Status.STATUS_ADDED, false, listener); + } public void testStatusSingleFile () throws Exception { File untracked = new File(workDir, "untracked");