Merge branch 'master' of git.labs.intellij.net:idea/community
authorirengrig <Irina.Chernushina@jetbrains.com>
Thu, 18 Nov 2010 08:47:15 +0000 (11:47 +0300)
committerirengrig <Irina.Chernushina@jetbrains.com>
Thu, 18 Nov 2010 08:47:15 +0000 (11:47 +0300)
1  2 
plugins/git4idea/src/META-INF/plugin.xml
plugins/git4idea/src/git4idea/history/GitHistoryUtils.java
plugins/git4idea/src/git4idea/history/GitLogParser.java
plugins/git4idea/src/git4idea/history/GitLogRecord.java
plugins/git4idea/tests/git4idea/tests/GitHistoryUtilsTestCase.java

index 7eee636a6994f9d92c9d501fb26c600c602e70f5,f032b29360a9280671c3b372225beaa91b1414de..4c972591620ac679cc252930387db7bb61c3ad9d
@@@ -23,8 -21,6 +23,7 @@@ import com.intellij.openapi.util.Getter
  import com.intellij.openapi.util.Key;
  import com.intellij.openapi.util.Pair;
  import com.intellij.openapi.vcs.FilePath;
- import com.intellij.openapi.vcs.FilePathImpl;
 +import com.intellij.openapi.vcs.FileStatus;
  import com.intellij.openapi.vcs.VcsException;
  import com.intellij.openapi.vcs.changes.Change;
  import com.intellij.openapi.vcs.changes.ChangeListManager;
@@@ -149,13 -141,24 +148,24 @@@ public class GitHistoryUtils 
  
      TODO: handle multiple repositories configuration: a file can be moved from one repo to another
     */
-   public static void history(final Project project, FilePath path, final Consumer<GitFileRevision> consumer,
-                              final Consumer<VcsException> exceptionConsumer) throws VcsException {
+   /**
+    * Retrieves the history of the file, including renames.
+    * @param project
+    * @param path              FilePath which history is queried.
+    * @param root              Git root - optional: if this is null, then git root will be detected automatically.
+    * @param consumer          This consumer is notified ({@link Consumer#consume(Object)} when new history records are retrieved.
+    * @param exceptionConsumer This consumer is notified in case of error while executing git command.
+    * @param parameters        Optional parameters which will be added to the git log command just before the path.
+    * @throws VcsException     In case of git native execution error.
+    */
+   public static void history(final Project project, FilePath path, @Nullable VirtualFile root, final Consumer<GitFileRevision> consumer,
+                              final Consumer<VcsException> exceptionConsumer, String... parameters) throws VcsException {
      // adjust path using change manager
      path = getLastCommitName(project, path);
-     final VirtualFile root = GitUtil.getGitRoot(path);
+     final VirtualFile finalRoot = (root == null ? GitUtil.getGitRoot(path) : root);
      final GitLogParser logParser = new GitLogParser(HASH, COMMIT_TIME, AUTHOR_NAME, AUTHOR_EMAIL, COMMITTER_NAME, COMMITTER_EMAIL, PARENTS, SUBJECT, BODY);
 -    logParser.setNameInOutput(false);
 +    logParser.parseStatusBeforeName(false);
  
      final AtomicReference<String> firstCommit = new AtomicReference<String>("HEAD");
      final AtomicReference<String> firstCommitParent = new AtomicReference<String>("HEAD");
      final GitLogParser parser = new GitLogParser(HASH);
      h.setNoSSH(true);
      h.setStdoutSuppressed(true);
-     h.addParameters("-M", "--follow", "--name-status", parser.getPretty(), "--encoding=UTF-8", commit);
+     h.addParameters("-M", "--name-status", parser.getPretty(), "--encoding=UTF-8", commit);
      h.endOptions();
-     h.addRelativePaths(filePath);
 -    parser.setNameInOutput(true);
 +    parser.parseStatusBeforeName(true);
      final String output = h.run();
-     final GitLogRecord record = parser.parseOneRecord(output);
-     final List<Change> changes = record.coolChangesParser(project, root);
+     final List<GitLogRecord> records = parser.parse(output);
+     // we have information about all changed files of the commit. Extracting information about the file we need.
+     GitLogRecord fileRecord = null;
+     for (GitLogRecord record : records) {
+       final List<String> paths = record.getPaths();
+       if (!paths.isEmpty()) {
+         String path = paths.get(paths.size()-1); // if the file is renamed, it has 2 paths - we are looking for the new name.
+         if (path.equals(GitUtil.relativePath(root, filePath))) {
+           fileRecord = record;
+           break;
+         }
+       }
+     }
  
-     final Change change = changes.get(0);
-     if (change.isMoved() || change.isRenamed()) {
-       final List<FilePath> paths = record.getFilePaths(root);
 -    if (fileRecord != null && fileRecord.getNameStatus() == 'R') {
 -      final List<FilePath> paths = fileRecord.getFilePaths(root);
--      final String message = "Rename commit should have 2 paths. Commit: " + commit;
--      if (!LOG.assertTrue(paths.size() == 2, message + " Output: [" + output + "]")) {
--        throw new VcsException(message);
++    if (fileRecord != null) {
++      final List<Change> changes = fileRecord.coolChangesParser(project, root);
++      final Change change = changes.get(0);
++      if (change.isMoved() || change.isRenamed()) {
++        final List<FilePath> paths = fileRecord.getFilePaths(root);
++        final String message = "Rename commit should have 2 paths. Commit: " + commit;
++        if (!LOG.assertTrue(paths.size() == 2, message + " Output: [" + output + "]")) {
++          throw new VcsException(message);
++        }
++        return paths.get(0);
        }
--      return paths.get(0);
      }
      return null;
    }
      path = getLastCommitName(project, path);
      final VirtualFile root = GitUtil.getGitRoot(path);
      GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.SHOW);
 -    GitLogParser parser = new GitLogParser(SHORT_HASH, HASH, COMMIT_TIME, AUTHOR_NAME, AUTHOR_EMAIL, COMMITTER_NAME, COMMITTER_EMAIL, SHORT_PARENTS, REF_NAMES, SUBJECT, BODY);
 +    GitLogParser parser = new GitLogParser(SHORT_HASH, HASH, COMMIT_TIME, AUTHOR_NAME, AUTHOR_TIME, AUTHOR_EMAIL, COMMITTER_NAME, COMMITTER_EMAIL, SHORT_PARENTS, REF_NAMES, SUBJECT, BODY);
      h.setNoSSH(true);
      h.setStdoutSuppressed(true);
-     parser.parseStatusBeforeName(true);      // todo ?
-     h.addParameters("--name-status", parser.getPretty(), "--encoding=UTF-8");
+     h.addParameters("--name-only", parser.getPretty(), "--encoding=UTF-8");
 -    parser.setNameInOutput(false);
++    parser.parseStatusBeforeName(false);
      h.addParameters(new ArrayList<String>(commitsIds));
  
      h.endOptions();
index 19b57f98beeef669dd1c45785ed8f0903b365199,ccec182eb4e18a86c9ed2644488290c87a0bfa52..15125a648621637c4e252331889d17e2ae7a367e
@@@ -46,20 -37,25 +46,20 @@@ import static git4idea.history.GitLogPa
  class GitLogRecord {
    private final Map<GitLogParser.GitLogOption, String> myOptions;
    private final List<String> myPaths;
 -  private final char myNameStatus;
 +  private final List<List<String>> myParts;
  
 -  GitLogRecord(@NotNull Map<GitLogParser.GitLogOption, String> options, @NotNull List<String> paths, char nameStatus) {
 +  GitLogRecord(Map<GitLogParser.GitLogOption, String> options, List<String> paths, List<List<String>> parts) {
      myOptions = options;
      myPaths = paths;
 -    myNameStatus = nameStatus;
 +    myParts = parts;
    }
  
-   Collection<String> getPaths() {
 -  /**
 -   * Status of the file requested by --name-status flag.
 -   * @return Character indicating the file status ('M', 'D', 'R' (for R100) or else) or 0 if no status was returned by git log
 -   * (which may happen, for example, if we didn't request it).
 -   */
 -  char getNameStatus() {
 -    return myNameStatus;
++  List<String> getPaths() {
 +    return myPaths;
    }
  
 -  @NotNull List<String> getPaths() {
 -    return myPaths;
 +  public List<List<String>> getParts() {
 +    return myParts;
    }
  
    @NotNull