Git: history per repository. initial! version

This commit is contained in:
irengrig
2010-04-13 23:06:03 +04:00
parent 0a8207d565
commit ec5889c50b
48 changed files with 4021 additions and 138 deletions
@@ -18,6 +18,7 @@ package com.intellij.ui;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.util.containers.HashMap;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
@@ -25,6 +26,7 @@ import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;
import java.util.ArrayList;
import java.util.Map;
/**
* This is high performance Swing component which represents an icon
@@ -71,8 +73,7 @@ public class SimpleColoredComponent extends JComponent {
private int myMainTextLastIndex = -1;
private int myAlignIndex;
private int myAlignWidth;
private final Map<Integer, Integer> myAligns;
private boolean myIconOpaque = true;
@@ -84,6 +85,7 @@ public class SimpleColoredComponent extends JComponent {
myIpad = new Insets(1, 2, 1, 2);
myIconTextGap = 2;
myBorder = new MyBorder();
myAligns = new HashMap<Integer, Integer>(10);
setOpaque(true);
}
@@ -137,8 +139,8 @@ public class SimpleColoredComponent extends JComponent {
}
public synchronized void appendAlign(int alignWidth) {
myAlignIndex = myFragments.size()-1;
myAlignWidth = alignWidth;
final int alignIndex = myFragments.size()-1;
myAligns.put(alignIndex, alignWidth);
}
/**
@@ -153,8 +155,7 @@ public class SimpleColoredComponent extends JComponent {
myAttributes.clear();
myFragmentTags = null;
myMainTextLastIndex = -1;
myAlignIndex = -1;
myAlignWidth = -1;
myAligns.clear();
}
revalidateAndRepaint();
}
@@ -280,8 +281,10 @@ public class SimpleColoredComponent extends JComponent {
}
final FontMetrics metrics = getFontMetrics(font);
width += metrics.stringWidth(myFragments.get(i));
if (i == myAlignIndex && width < myAlignWidth) {
width = myAlignWidth;
final Integer fixedWidth = myAligns.get(i);
if (fixedWidth != null && width < fixedWidth.intValue()) {
width = fixedWidth.intValue();
}
if (mainTextOnly && myMainTextLastIndex >= 0 && i == myMainTextLastIndex) break;
@@ -330,8 +333,9 @@ public class SimpleColoredComponent extends JComponent {
return i;
}
curX += curWidth;
if (i == myAlignIndex && curX < myAlignWidth) {
curX = myAlignWidth;
final Integer fixedWidth = myAligns.get(i);
if (fixedWidth != null && curX < fixedWidth.intValue()) {
curX = fixedWidth.intValue();
}
}
return -1;
@@ -444,8 +448,9 @@ public class SimpleColoredComponent extends JComponent {
xOffset += fragmentWidth;
if (i == myAlignIndex && xOffset < myAlignWidth) {
xOffset = myAlignWidth;
final Integer fixedWidth = myAligns.get(i);
if (fixedWidth != null && xOffset < fixedWidth.intValue()) {
xOffset = fixedWidth.intValue();
}
}
@@ -0,0 +1,22 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.util;
public interface OnOff {
boolean isOn();
void on();
void off();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.idea.svn;
package com.intellij.openapi.vcs;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.SomeQueue;
@@ -39,7 +39,7 @@ import java.util.Map;
*/
@SomeQueue
public class RequestsMerger {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.RequestsMerger");
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.RequestsMerger");
private static final int ourDelay = 300;
private final MyWorker myWorker;
@@ -18,7 +18,6 @@ import com.intellij.openapi.keymap.KeymapManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Splitter;
import com.intellij.openapi.ui.SplitterProportionsData;
import com.intellij.openapi.ui.ThreeComponentsSplitter;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vcs.AbstractVcs;
@@ -67,8 +66,6 @@ import java.util.List;
* @author yole
*/
public class CommittedChangesTreeBrowser extends JPanel implements TypeSafeDataProvider, Disposable, DecoratorManager {
private static final Border LEFT_BORDER = BorderFactory.createMatteBorder(1, 0, 0, 1, UIUtil.getBorderSeparatorColor());
private static final Border MIDDLE_BORDER = BorderFactory.createMatteBorder(1, 1, 0, 1, UIUtil.getBorderSeparatorColor());
private static final Border RIGHT_BORDER = BorderFactory.createMatteBorder(1, 1, 0, 0, UIUtil.getBorderSeparatorColor());
private final Project myProject;
@@ -275,7 +272,7 @@ public class CommittedChangesTreeBrowser extends JPanel implements TypeSafeDataP
}
}
private static List<Change> collectChanges(final List<CommittedChangeList> selectedChangeLists, final boolean withMovedTrees) {
public static List<Change> collectChanges(final List<CommittedChangeList> selectedChangeLists, final boolean withMovedTrees) {
List<Change> result = new ArrayList<Change>();
Collections.sort(selectedChangeLists, new Comparator<CommittedChangeList>() {
public int compare(final CommittedChangeList o1, final CommittedChangeList o2) {
@@ -637,106 +634,6 @@ public class CommittedChangesTreeBrowser extends JPanel implements TypeSafeDataP
}
}
private static class WiseSplitter {
private final Runnable myRefresher;
private final Splitter myParentSplitter;
private final ThreeComponentsSplitter myInnerSplitter;
private final Map<String, Integer> myInnerSplitterContents;
private WiseSplitter(final Runnable refresher, final Splitter parentSplitter) {
myRefresher = refresher;
myParentSplitter = parentSplitter;
myInnerSplitter = new ThreeComponentsSplitter(false);
myInnerSplitter.setHonorComponentsMinimumSize(true);
myInnerSplitterContents = new HashMap<String, Integer>();
updateBorders();
}
public boolean canAdd() {
return myInnerSplitterContents.size() <= 3;
}
public void add(final String key, final JComponent comp) {
final int idx = myInnerSplitterContents.size();
myInnerSplitterContents.put(key, idx);
if (idx == 0) {
myParentSplitter.setFirstComponent(myInnerSplitter);
if (myParentSplitter.getProportion() < 0.05f) {
myParentSplitter.setProportion(0.25f);
}
myInnerSplitter.setFirstComponent(comp);
myInnerSplitter.setFirstSize((int) (myParentSplitter.getSize().getWidth() * myParentSplitter.getProportion()));
} else if (idx == 1) {
final Dimension dimension = myInnerSplitter.getSize();
final double width = dimension.getWidth() / 2;
myInnerSplitter.setInnerComponent(comp);
myInnerSplitter.setFirstSize((int) width);
} else {
final Dimension dimension = myInnerSplitter.getSize();
final double width = dimension.getWidth() / 3;
myInnerSplitter.setLastComponent(comp);
myInnerSplitter.setFirstSize((int) width);
myInnerSplitter.setLastSize((int) width);
}
updateBorders();
myRefresher.run();
}
private void updateBorders() {
boolean isEmpty = myInnerSplitterContents.size() == 0;
if (!isEmpty) {
setBorder(myInnerSplitter.getFirstComponent(), true);
setBorder(myInnerSplitter.getInnerComponent(), false);
}
setBorder(myParentSplitter.getSecondComponent(), isEmpty);
}
private void setBorder(JComponent c, boolean leftMost) {
if (c instanceof JScrollPane) c.setBorder(leftMost ? LEFT_BORDER : MIDDLE_BORDER);
}
public void remove(final String key) {
final Integer idx = myInnerSplitterContents.remove(key);
if (idx == null) {
return;
}
final Map<String, Integer> tmp = new HashMap<String, Integer>();
for (Map.Entry<String, Integer> entry : myInnerSplitterContents.entrySet()) {
if (entry.getValue() < idx) {
tmp.put(entry.getKey(), entry.getValue());
} else {
tmp.put(entry.getKey(), entry.getValue() - 1);
}
}
myInnerSplitterContents.clear();
myInnerSplitterContents.putAll(tmp);
if (idx == 0) {
final JComponent inner = myInnerSplitter.getInnerComponent();
myInnerSplitter.setInnerComponent(null);
myInnerSplitter.setFirstComponent(inner);
lastToInner();
} else if (idx == 1) {
lastToInner();
} else {
myInnerSplitter.setLastComponent(null);
}
updateBorders();
myRefresher.run();
}
private void lastToInner() {
final JComponent last = myInnerSplitter.getLastComponent();
myInnerSplitter.setLastComponent(null);
myInnerSplitter.setInnerComponent(last);
}
}
public interface CommittedChangesReloadListener {
void itemsReloaded();
void emptyRefresh();
@@ -0,0 +1,129 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.vcs.changes.committed;
import com.intellij.openapi.ui.Splitter;
import com.intellij.openapi.ui.ThreeComponentsSplitter;
import com.intellij.util.ui.UIUtil;
import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;
import java.util.HashMap;
import java.util.Map;
public class WiseSplitter {
private static final Border LEFT_BORDER = BorderFactory.createMatteBorder(1, 0, 0, 1, UIUtil.getBorderSeparatorColor());
private static final Border MIDDLE_BORDER = BorderFactory.createMatteBorder(1, 1, 0, 1, UIUtil.getBorderSeparatorColor());
private final Runnable myRefresher;
private final Splitter myParentSplitter;
private final ThreeComponentsSplitter myInnerSplitter;
private final Map<String, Integer> myInnerSplitterContents;
public WiseSplitter(final Runnable refresher, final Splitter parentSplitter) {
myRefresher = refresher;
myParentSplitter = parentSplitter;
myInnerSplitter = new ThreeComponentsSplitter(false);
myInnerSplitter.setHonorComponentsMinimumSize(true);
myInnerSplitterContents = new HashMap<String, Integer>();
updateBorders();
}
public boolean canAdd() {
return myInnerSplitterContents.size() <= 3;
}
public void add(final String key, final JComponent comp) {
final int idx = myInnerSplitterContents.size();
myInnerSplitterContents.put(key, idx);
if (idx == 0) {
myParentSplitter.setFirstComponent(myInnerSplitter);
if (myParentSplitter.getProportion() < 0.05f) {
myParentSplitter.setProportion(0.25f);
}
myInnerSplitter.setFirstComponent(comp);
myInnerSplitter.setFirstSize((int) (myParentSplitter.getSize().getWidth() * myParentSplitter.getProportion()));
} else if (idx == 1) {
final Dimension dimension = myInnerSplitter.getSize();
final double width = dimension.getWidth() / 2;
myInnerSplitter.setInnerComponent(comp);
myInnerSplitter.setFirstSize((int) width);
} else {
final Dimension dimension = myInnerSplitter.getSize();
final double width = dimension.getWidth() / 3;
myInnerSplitter.setLastComponent(comp);
myInnerSplitter.setFirstSize((int) width);
myInnerSplitter.setLastSize((int) width);
}
updateBorders();
myRefresher.run();
}
private void updateBorders() {
boolean isEmpty = myInnerSplitterContents.size() == 0;
if (!isEmpty) {
setBorder(myInnerSplitter.getFirstComponent(), true);
setBorder(myInnerSplitter.getInnerComponent(), false);
}
setBorder(myParentSplitter.getSecondComponent(), isEmpty);
}
private void setBorder(JComponent c, boolean leftMost) {
if (c instanceof JScrollPane) c.setBorder(leftMost ? LEFT_BORDER : MIDDLE_BORDER);
}
public void remove(final String key) {
final Integer idx = myInnerSplitterContents.remove(key);
if (idx == null) {
return;
}
final Map<String, Integer> tmp = new HashMap<String, Integer>();
for (Map.Entry<String, Integer> entry : myInnerSplitterContents.entrySet()) {
if (entry.getValue() < idx) {
tmp.put(entry.getKey(), entry.getValue());
} else {
tmp.put(entry.getKey(), entry.getValue() - 1);
}
}
myInnerSplitterContents.clear();
myInnerSplitterContents.putAll(tmp);
if (idx == 0) {
final JComponent inner = myInnerSplitter.getInnerComponent();
myInnerSplitter.setInnerComponent(null);
myInnerSplitter.setFirstComponent(inner);
lastToInner();
} else if (idx == 1) {
lastToInner();
} else {
myInnerSplitter.setLastComponent(null);
}
updateBorders();
myRefresher.run();
}
private void lastToInner() {
final JComponent last = myInnerSplitter.getLastComponent();
myInnerSplitter.setLastComponent(null);
myInnerSplitter.setInnerComponent(last);
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 775 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 570 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 816 B

+6 -1
View File
@@ -108,4 +108,9 @@
<fileTypeFactory implementation="git4idea.vfs.GitFileTypeFactory"/>
</extensions>
</idea-plugin>
<project-components>
<component>
<implementation-class>git4idea.history.browser.GitProjectLogManager</implementation-class>
</component>
</project-components>
</idea-plugin>
+8 -3
View File
@@ -119,13 +119,14 @@ public class GitBranch extends GitReference {
* @param remote if true remote branches are listed
* @param local if true local branches are listed
* @param branches the collection used to store branches
* @param containingCommit
* @throws VcsException if there is a problem with running git
*/
public static void listAsStrings(final Project project,
final VirtualFile root,
final boolean remote,
final boolean local,
final Collection<String> branches) throws VcsException {
final Collection<String> branches, @Nullable final String containingCommit) throws VcsException {
if (!local && !remote) {
// no need to run handler
return;
@@ -140,6 +141,10 @@ public class GitBranch extends GitReference {
else if (remote) {
handler.addParameters("-r");
}
if (containingCommit != null) {
handler.addParameters("--contains");
handler.addParameters(containingCommit);
}
StringScanner s = new StringScanner(handler.run());
while (s.hasMoreData()) {
String line = s.line();
@@ -208,14 +213,14 @@ public class GitBranch extends GitReference {
final Collection<GitBranch> branches) throws VcsException {
ArrayList<String> temp = new ArrayList<String>();
if (local) {
listAsStrings(project, root, false, true, temp);
listAsStrings(project, root, false, true, temp, null);
for (String b : temp) {
branches.add(new GitBranch(b, false, false));
}
temp.clear();
}
if (remote) {
listAsStrings(project, root, true, false, temp);
listAsStrings(project, root, true, false, temp, null);
for (String b : temp) {
branches.add(new GitBranch(b, false, true));
}
+9 -2
View File
@@ -22,6 +22,7 @@ import git4idea.commands.GitCommand;
import git4idea.commands.GitSimpleHandler;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
@@ -58,13 +59,19 @@ public class GitTag extends GitReference {
* @param project the context
* @param root the git root
* @param tags the tag list
* @param containingCommit
* @throws VcsException if there is a problem with running git
*/
public static void listAsStrings(final Project project, final VirtualFile root, final Collection<String> tags) throws VcsException {
public static void listAsStrings(final Project project, final VirtualFile root, final Collection<String> tags,
@Nullable final String containingCommit) throws VcsException {
GitSimpleHandler handler = new GitSimpleHandler(project, root, GitCommand.TAG);
handler.setNoSSH(true);
handler.setSilent(true);
handler.addParameters("-l");
if (containingCommit != null) {
handler.addParameters("--contains");
handler.addParameters(containingCommit);
}
for (String line : handler.run().split("\n")) {
if (line.length() == 0) {
continue;
@@ -83,7 +90,7 @@ public class GitTag extends GitReference {
*/
public static void list(final Project project, final VirtualFile root, final Collection<? super GitTag> tags) throws VcsException {
ArrayList<String> temp = new ArrayList<String>();
listAsStrings(project, root, temp);
listAsStrings(project, root, temp, null);
for (String t : temp) {
tags.add(new GitTag(t));
}
@@ -674,8 +674,6 @@ public class GitUtil {
final VirtualFile root,
final Consumer<GitSimpleHandler> parametersSpecifier,
final Consumer<CommittedChangeList> consumer) throws VcsException {
final List<CommittedChangeList> rc = new ArrayList<CommittedChangeList>();
GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.LOG);
h.setSilent(true);
h.setNoSSH(true);
@@ -314,7 +314,7 @@ public class GitChangeUtils {
}
GitRevisionNumber thisRevision = new GitRevisionNumber(revisionNumber, commitDate);
GitRevisionNumber parentRevision = parents.length > 0 ? loadRevision(project, root, parents[0]) : null;
long number = Long.parseLong(revisionNumber.substring(0, 15), 16) << 4 + Integer.parseInt(revisionNumber.substring(15, 16), 16);
long number = longForSHAHash(revisionNumber);
if (parents.length <= 1) {
// This is the first or normal commit with the single parent.
// Just parse changes in this commit as returned by the show command.
@@ -350,4 +350,8 @@ public class GitChangeUtils {
return new CommittedChangeListImpl(commentSubject + "(" + revisionNumber + ")", fullComment, committerName, number, commitDate,
changes);
}
public static long longForSHAHash(String revisionNumber) {
return Long.parseLong(revisionNumber.substring(0, 15), 16) << 4 + Integer.parseInt(revisionNumber.substring(15, 16), 16);
}
}
@@ -369,8 +369,8 @@ public class GitPushDialog extends DialogWrapper {
myBranchNames.clear();
myTagNames.clear();
try {
GitBranch.listAsStrings(myProject, getGitRoot(), false, true, myBranchNames);
GitTag.listAsStrings(myProject, getGitRoot(), myTagNames);
GitBranch.listAsStrings(myProject, getGitRoot(), false, true, myBranchNames, null);
GitTag.listAsStrings(myProject, getGitRoot(), myTagNames, null);
}
catch (VcsException ex) {
LOG.warn("Exception in branch list: \n" + StringUtil.getThrowableText(ex));
@@ -240,14 +240,14 @@ public class GitCheckoutDialog extends DialogWrapper {
try {
List<String> branchesAndTags = new ArrayList<String>();
// get branches
GitBranch.listAsStrings(myProject, gitRoot(), true, true, branchesAndTags);
GitBranch.listAsStrings(myProject, gitRoot(), true, true, branchesAndTags, null);
existingBranches.clear();
existingBranches.addAll(branchesAndTags);
Collections.sort(branchesAndTags);
// get tags
if (myIncludeTagsCheckBox.isSelected()) {
int mark = branchesAndTags.size();
GitTag.listAsStrings(myProject, gitRoot(), branchesAndTags);
GitTag.listAsStrings(myProject, gitRoot(), branchesAndTags, null);
Collections.sort(branchesAndTags.subList(mark, branchesAndTags.size()));
}
myBranchToCkeckout.removeAllItems();
@@ -18,6 +18,7 @@ package git4idea.history;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.Change;
@@ -26,6 +27,7 @@ import com.intellij.openapi.vcs.diff.ItemLatestState;
import com.intellij.openapi.vcs.history.VcsFileRevision;
import com.intellij.openapi.vcs.history.VcsRevisionNumber;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Consumer;
import com.intellij.util.concurrency.Semaphore;
import com.intellij.util.text.StringTokenizer;
@@ -35,11 +37,11 @@ import git4idea.commands.GitCommand;
import git4idea.commands.GitLineHandler;
import git4idea.commands.GitLineHandlerAdapter;
import git4idea.commands.GitSimpleHandler;
import git4idea.history.browser.GitCommit;
import git4idea.history.browser.SHAHash;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.*;
/**
* History utilities for Git
@@ -277,6 +279,129 @@ public class GitHistoryUtils {
return rc;
}
public static List<GitCommit> historyWithLinks(Project project, FilePath path, final Set<String> allBranchesSet, final String... parameters) throws VcsException {
// adjust path using change manager
path = getLastCommitName(project, path);
final VirtualFile root = GitUtil.getGitRoot(path);
GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.LOG);
h.setNoSSH(true);
h.setStdoutSuppressed(true);
// todo think of name+email linkage in a single field
h.addParameters(parameters);
/*h.addParameters("-M", "--follow", "--name-only",
"--pretty=format:%x03%H%x00%ct%x00%an%x00%ae%x00%cn%x00%ce%x00[%P]%x00[%d]%x00%s%n%n%b%x00", "--encoding=UTF-8");*/
h.addParameters("--name-only",
"--pretty=format:%x03%H%x00%ct%x00%an%x00%ae%x00%cn%x00%ce%x00[%P]%x00[%d]%x00%s%n%n%b%x00", "--encoding=UTF-8");
h.endOptions();
h.addRelativePaths(path);
String output = h.run();
final List<GitCommit> rc = new ArrayList<GitCommit>();
StringTokenizer tk = new StringTokenizer(output, "\u0003", false);
final String prefix = root.getPath() + "/";
while (tk.hasMoreTokens()) {
final String line = tk.nextToken();
final StringTokenizer tk2 = new StringTokenizer(line, "\u0000\n", false);
//while (tk2.hasMoreTokens()) {
final String hash = tk2.nextToken("\u0000\n");
final String dateString = tk2.nextToken("\u0000");
final Date date = GitUtil.parseTimestamp(dateString);
final String authorName = tk2.nextToken("\u0000");
final String authorEmail = tk2.nextToken("\u0000");
final String committerName = tk2.nextToken("\u0000");
final String committerEmail = tk2.nextToken("\u0000");
// parent hashes
final String parents = removeSquareBraces(tk2.nextToken("\u0000"));
final Set<SHAHash> parentsHashes;
if (! StringUtil.isEmptyOrSpaces(parents)) {
final String[] parentsSplit = parents.split(" "); // todo if parent = 000000
parentsHashes = new HashSet<SHAHash>();
for (String s : parentsSplit) {
parentsHashes.add(new SHAHash(s));
}
} else {
parentsHashes = Collections.emptySet();
}
// decorate
final String decorate = tk2.nextToken("\u0000");
final String[] refNames = parseRefNames(decorate);
final List<String> tags = refNames.length > 0 ? new LinkedList<String>() : Collections.<String>emptyList();
final List<String> branches = refNames.length > 0 ? new LinkedList<String>() : Collections.<String>emptyList();
for (String refName : refNames) {
if (allBranchesSet.contains(refName)) {
branches.add(refName);
} else {
tags.add(refName);
}
}
final String message = tk2.nextToken("\u0000").trim();
final List<FilePath> pathsList = new LinkedList<FilePath>();
tk2.reset("\u0000\n");
while (tk2.hasMoreTokens()) {
final FilePath revisionPath = VcsUtil.getFilePathForDeletedFile(prefix + GitUtil.unescapePath(tk2.nextToken()), false);
pathsList.add(revisionPath);
}
// todo parse revisions... patches?
rc.add(new GitCommit(new SHAHash(hash), authorName, committerName, date, message, parentsHashes, pathsList, authorEmail,
committerEmail, tags, branches));
//}
}
/*StringTokenizer tk2 = new StringTokenizer(output, "\u0000\n", false);
String prefix = root.getPath() + "/";
while (tk.hasMoreTokens()) {
final String hash = tk.nextToken("\u0000\n");
final Date date = GitUtil.parseTimestamp(tk.nextToken("\u0000"));
final String authorName = tk.nextToken("\u0000");
final String committerName = tk.nextToken("\u0000");
// parent hashes
final String parents = tk.nextToken("\u0000");
final String[] parentsSplit = parents.split(" "); // todo if parent = 000000
final Set<SHAHash> parentsHashes = new HashSet<SHAHash>();
for (String s : parentsSplit) {
parentsHashes.add(new SHAHash(s));
}
// decorate
final String decorate = tk.nextToken("\u0000");
final int startSymb = decorate.indexOf("(");
int idxFrom = startSymb == -1 ? 0 : startSymb;
final int endSymb = decorate.indexOf(")");
int idxTo = endSymb == -1 ? 0 : endSymb;
final String refs = decorate.substring(idxFrom, idxTo);
final String[] refNames = refs.split(", ");
final String message = tk.nextToken("\u0000").trim();
tk.nextToken("\u0000\u0001\u0000");
//final FilePath revisionPath = VcsUtil.getFilePathForDeletedFile(prefix + GitUtil.unescapePath(tk.nextToken("\u0000\n")), false);
rc.add(new GitCommit(new SHAHash(hash), authorName, committerName, date, message, parentsHashes, Arrays.asList(refNames)));
}*/
return rc;
}
@Nullable
private static String removeSquareBraces(final String s) {
final int startSquare = s.indexOf("[");
final int endSquare = s.indexOf("]");
if ((startSquare == -1) || (endSquare == -1)) return null;
return s.substring(startSquare + 1, endSquare);
}
private static String[] parseRefNames(final String decorate) {
if (removeSquareBraces(decorate) == null) return ArrayUtil.EMPTY_STRING_ARRAY;
/*final int startSquare = decorate.indexOf("[");
final int endSquare = decorate.indexOf("]");
if ((startSquare == -1) || (endSquare == -1)) return ArrayUtil.EMPTY_STRING_ARRAY;*/
final int startParentheses = decorate.indexOf("(");
final int endParentheses = decorate.indexOf(")");
if ((startParentheses == -1) || (endParentheses == -1)) return ArrayUtil.EMPTY_STRING_ARRAY;
final String refs = decorate.substring(startParentheses + 1, endParentheses);
return refs.split(", ");
}
/**
* Get name of the file in the last commit. If file was renamed, returns the previous name.
*
@@ -0,0 +1,71 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.history.browser;
import com.intellij.util.ui.UIUtil;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.Rectangle2D;
public class CaptionIcon implements Icon {
private final Color myBgrnd;
private final Font myFont;
private final String myText;
private final int myHeight;
private final int myWidth;
public CaptionIcon(Color bgrnd, Font font, String text, final Component someComponent) {
myBgrnd = bgrnd;
myFont = font;
myText = text;
final FontMetrics fm = someComponent.getFontMetrics(myFont);
final Rectangle2D bounds = fm.getStringBounds(text, someComponent.getGraphics());
final double height = (text.equals(text.toLowerCase())) ? (bounds.getHeight() - fm.getMaxDescent()) : bounds.getHeight();
myHeight = (int) height + 4;
myWidth = (int) bounds.getWidth() + 4;
}
public int getIconHeight() {
return myHeight;
}
public void paintIcon(Component c, Graphics g, int x, int y) {
final Graphics2D graphics2D = (Graphics2D)g;
final Paint oldPaint = graphics2D.getPaint();
final Stroke oldStroke = graphics2D.getStroke();
graphics2D.setPaint(myBgrnd);
g.fillRoundRect(x + 2, y, myWidth, myHeight, 2, 2);
graphics2D.setPaint(UIUtil.getTextAreaForeground());
final BasicStroke stroke = new BasicStroke(1);
graphics2D.setStroke(stroke);
g.drawRoundRect(x + 2, y, myWidth, myHeight - 1, 2, 2);
g.setColor(UIUtil.getTextAreaForeground());
g.drawString(myText, x + 4, y + myHeight - 3); // -2
graphics2D.setPaint(oldPaint);
graphics2D.setStroke(oldStroke);
}
public int getIconWidth() {
return myWidth + 4;
}
}
@@ -0,0 +1,227 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.history.browser;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.regex.Pattern;
public class ChangesFilter {
public abstract static class Merger {
private final Collection<MemoryFilter> myFilters;
private MemoryFilter myResult;
protected Merger() {
myFilters = new LinkedList<MemoryFilter>();
}
protected abstract boolean acceptImpl(MemoryFilter filter);
protected abstract MemoryFilter merge(final Collection<MemoryFilter> filters);
public boolean accept(final MemoryFilter filter) {
if (acceptImpl(filter)) {
myFilters.add(filter);
return true;
}
return false;
}
@Nullable
public MemoryFilter getResult() {
if (myFilters.isEmpty()) return null;
return merge(myFilters);
}
}
public static class UsersMerger extends Merger {
@Override
protected boolean acceptImpl(MemoryFilter filter) {
return filter instanceof Author || filter instanceof Committer;
}
@Override
protected MemoryFilter merge(Collection<MemoryFilter> filters) {
final CompositeUserMemoryFilter result = new CompositeUserMemoryFilter();
for (MemoryFilter filter : filters) {
if (filter instanceof Author) {
result.addUser(((Author) filter).myRegexp);
} else if (filter instanceof Committer) {
result.addUser(((Committer) filter).myRegexp);
}
}
return result;
}
}
public static List<MemoryFilter> combineFilters(final Collection<Filter> filters) {
final Merger[] mergers = {new UsersMerger()};
if (filters.isEmpty()) return Collections.emptyList();
final List<MemoryFilter> result = new LinkedList<MemoryFilter>();
for (Filter filter : filters) {
boolean taken = false;
for (Merger combiner : mergers) {
if (combiner.accept(filter)) {
taken = true;
break;
}
}
if (! taken) {
result.add(filter);
}
}
for (Merger combiner : mergers) {
final MemoryFilter combined = combiner.getResult();
if (combined != null) {
result.add(combined);
}
}
return result;
}
private static class CompositeUserMemoryFilter implements MemoryFilter {
private final Set<String> myUsers;
private CompositeUserMemoryFilter() {
myUsers = new HashSet<String>();
}
public void addUser(final String name) {
myUsers.add(name);
}
public boolean applyInMemory(GitCommit commit) {
return myUsers.contains(commit.getCommitter()) || myUsers.contains(commit.getAuthor());
}
}
public interface MemoryFilter {
boolean applyInMemory(final GitCommit commit);
}
public interface Filter extends MemoryFilter {
void applyToCommandLine(final List<String> sink);
}
public static class Branch implements Filter {
// todo decode into hash
private Collection<String> myCommitNames;
public boolean applyInMemory(final GitCommit commit) {
for (String commitName : myCommitNames) {
//if (commit.getHash().startsWith(commitName) || )
}
return false;
}
public void applyToCommandLine(List<String> sink) {
//To change body of implemented methods use File | Settings | File Templates.
}
}
public static class Author implements Filter {
private final String myRegexp;
private Pattern myPattern;
public Author(String regexp) {
myRegexp = regexp;
myPattern = Pattern.compile(myRegexp);
}
public boolean applyInMemory(GitCommit commit) {
return myPattern.matcher(commit.getAuthor()).matches();
}
public void applyToCommandLine(List<String> sink) {
sink.add("--author=" + myRegexp);
}
}
public static class Committer implements Filter {
private final String myRegexp;
private Pattern myPattern;
public Committer(String regexp) {
myRegexp = regexp;
myPattern = Pattern.compile(myRegexp);
}
public boolean applyInMemory(GitCommit commit) {
return myPattern.matcher(commit.getCommitter()).matches();
}
public void applyToCommandLine(List<String> sink) {
sink.add("--committer=" + myRegexp);
}
}
public static class BeforeDate implements Filter {
private final Date myDate;
public BeforeDate(final Date date) {
myDate = date;
}
public boolean applyInMemory(GitCommit commit) {
return commit.getDate().before(myDate);
}
public void applyToCommandLine(List<String> sink) {
sink.add("--before=" + formatDate(myDate));
}
}
public static class AfterDate implements Filter {
private final Date myDate;
public AfterDate(final Date date) {
myDate = date;
}
public boolean applyInMemory(final GitCommit commit) {
return commit.getDate().after(myDate);
}
public void applyToCommandLine(final List<String> sink) {
sink.add("--after=" + formatDate(myDate));
}
}
private static String formatDate(final Date date) {
return date.toString();
}
public static class Comment implements Filter {
private final String myRegexp;
private Pattern myPattern;
public Comment(final String regexp) {
myRegexp = regexp;
myPattern = Pattern.compile(myRegexp);
}
public boolean applyInMemory(final GitCommit commit) {
return myPattern.matcher(commit.getDescription()).matches() || myPattern.matcher(commit.getCommitter()).matches() ||
myPattern.matcher(commit.getAuthor()).matches();
}
public void applyToCommandLine(final List<String> sink) {
sink.add("--grep=" + myRegexp);
}
}
}
@@ -0,0 +1,22 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.history.browser;
public class ErrorsHolder {
public void register(String s) {
//To change body of created methods use File | Settings | File Templates.
}
}
@@ -0,0 +1,171 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.history.browser;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Trinity;
import com.intellij.openapi.vcs.changes.committed.CommittedChangesTreeBrowser;
import com.intellij.ui.ColoredListCellRenderer;
import com.intellij.ui.SimpleTextAttributes;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
public abstract class FictiveTableCellRenderer extends ColoredListCellRenderer {
// for cases when our renderer is encapsulated
protected abstract int getParentWidth(JList list);
@Nullable
protected abstract Color getBgrndColor(JList list, Object value, int index, boolean selected, boolean hasFocus);
protected abstract Description getDescription(Object value);
protected abstract Trinity<String, SimpleTextAttributes, Object> getMoreTag();
protected abstract boolean willRender(Object value);
protected abstract static class Description {
private final java.util.List<Pair<String, SimpleTextAttributes>> myPieces;
private final int myMiddleIdx;
public Description(int middleIdx, java.util.List<Pair<String, SimpleTextAttributes>> pieces) {
myMiddleIdx = middleIdx;
myPieces = pieces;
}
public int getMiddleIdx() {
return myMiddleIdx;
}
public java.util.List<Pair<String, SimpleTextAttributes>> getPieces() {
return myPieces;
}
public abstract String getMaxString(final int idx);
}
@Override
protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) {
if (! willRender(value)) return;
final int parentWidth = getParentWidth(list);
final Color bgrndColor = getBgrndColor(list, value, index, selected, hasFocus);
if (bgrndColor != null) {
setBackground(bgrndColor);
}
final Description description = getDescription(value);
final MyFontHelper helper = new MyFontHelper(list);
int fixedWidth = 0;
int fixedWidthBefore = 0;
int changeableWidth = 0;
final java.util.List<Pair<String,SimpleTextAttributes>> pieces = description.getPieces();
final int middleIdx = description.getMiddleIdx();
for (int i = 0; i < pieces.size(); i++) {
final Pair<String, SimpleTextAttributes> pair = pieces.get(i);
if (middleIdx == i) {
changeableWidth = helper.getWidth(pair);
} else {
final int width = helper.getWidth(pair);
if (i < middleIdx) {
fixedWidthBefore += width;
append(pair.getFirst(), pair.getSecond());
final String adj = description.getMaxString(i);
if (adj != null) {
final int adjWidth = helper.getWidth(new Pair<String, SimpleTextAttributes>(adj, pair.getSecond()));
appendAlign(adjWidth);
fixedWidth += adjWidth;
} else {
fixedWidth += width;
}
} else {
fixedWidth += width;
}
}
}
final Pair<String, SimpleTextAttributes> changeable = pieces.get(middleIdx);
final int difference = parentWidth - fixedWidth;
if ((fixedWidth + changeableWidth) > parentWidth) {
// need zipping
if (fixedWidth < parentWidth) {
// todo this should be rewised
final Trinity<String, SimpleTextAttributes, Object> tag = getMoreTag();
final int more = helper.getWidth(new Pair<String, SimpleTextAttributes>(tag.getFirst(), tag.getSecond()));
if (more < difference) {
final String truncated = CommittedChangesTreeBrowser.CommittedChangeListRenderer
.truncateDescription(changeable.getFirst(), helper.getFontMetrics(changeable.getSecond()), difference - more);
final int truncatedWidth = helper.getWidth(new Pair<String, SimpleTextAttributes>(truncated, changeable.getSecond()));
append(truncated, changeable.getSecond());
append(tag.getFirst(), tag.getSecond(), tag.getThird());
if (truncatedWidth > 0) {
appendAlign(difference - truncatedWidth);
}
} else {
appendAlign(difference);
}
}
} else {
append(changeable.getFirst(), changeable.getSecond());
appendAlign(parentWidth - fixedWidth);
}
for (int i = middleIdx + 1; i < pieces.size(); i++) {
final Pair<String, SimpleTextAttributes> pair = pieces.get(i);
append(pair.getFirst(), pair.getSecond());
}
}
private static class MyFontHelper {
private final FontMetrics myFontMetrics;
private final FontMetrics myBoldMetrics;
private final FontMetrics myItalicsMetrics;
private final FontMetrics myBoldItalicsMetrics;
private MyFontHelper(final JList list) {
final Font font = list.getFont();
myFontMetrics = list.getFontMetrics(font);
myBoldMetrics = list.getFontMetrics(font.deriveFont(Font.BOLD));
myItalicsMetrics = list.getFontMetrics(font.deriveFont(Font.ITALIC));
myBoldItalicsMetrics = list.getFontMetrics(font.deriveFont(Font.ITALIC | Font.BOLD));
}
public int getWidth(final Pair<String, SimpleTextAttributes> pair) {
final SimpleTextAttributes ta = pair.getSecond();
FontMetrics fm;
fm = getFontMetrics(ta);
return fm.stringWidth(pair.getFirst());
}
public FontMetrics getFontMetrics(SimpleTextAttributes ta) {
FontMetrics fm;
if ((ta.getStyle() & SimpleTextAttributes.STYLE_BOLD) == SimpleTextAttributes.STYLE_BOLD) {
if ((ta.getStyle() & SimpleTextAttributes.STYLE_ITALIC) == SimpleTextAttributes.STYLE_ITALIC) {
fm = myBoldItalicsMetrics;
} else {
fm = myBoldMetrics;
}
} else {
if ((ta.getStyle() & SimpleTextAttributes.STYLE_ITALIC) == SimpleTextAttributes.STYLE_ITALIC) {
fm = myItalicsMetrics;
} else {
fm = myFontMetrics;
}
}
return fm;
}
}
}
@@ -0,0 +1,143 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.history.browser;
import com.intellij.openapi.vcs.FilePath;
import org.jetbrains.annotations.NotNull;
import java.util.*;
public class GitCommit {
@NotNull
private final SHAHash myHash;
private final String myAuthor;
private final String myCommitter;
private final String myDescription;
private final Date myDate;
private final String myAuthorEmail;
private final String myComitterEmail;
private final List<String> myTags;
private final List<String> myBranches;
private final Set<SHAHash> myParentsHashes;
private final Set<GitCommit> myParentsLinks;
private final List<FilePath> myPathsList;
public GitCommit(@NotNull final SHAHash hash, final String author, final String committer, final Date date, final String description,
final Set<SHAHash> parentsHashes, final List<FilePath> pathsList,
final String authorEmail, final String comitterEmail, List<String> tags, List<String> branches) {
myAuthor = author;
myCommitter = committer;
myDate = date;
myDescription = description;
myHash = hash;
myParentsHashes = parentsHashes;
myPathsList = pathsList;
myAuthorEmail = authorEmail;
myComitterEmail = comitterEmail;
myTags = tags;
myBranches = branches;
myParentsLinks = new HashSet<GitCommit>();
}
public void addParentLink(final GitCommit commit) {
myParentsLinks.add(commit);
}
public String getAuthor() {
return myAuthor;
}
public String getCommitter() {
return myCommitter;
}
public Date getDate() {
return myDate;
}
public String getDescription() {
return myDescription;
}
@NotNull
public SHAHash getHash() {
return myHash;
}
// todo think of interface
public Set<SHAHash> getParentsHashes() {
return myParentsHashes;
}
// todo think of interface
public Set<GitCommit> getParentsLinks() {
return myParentsLinks;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GitCommit gitCommit = (GitCommit)o;
if (!myHash.equals(gitCommit.myHash)) return false;
return true;
}
@Override
public int hashCode() {
return myHash.hashCode();
}
public List<String> getBranches() {
return myBranches;
}
public List<String> getTags() {
return myTags;
}
public void orderTags(final Comparator<String> comparator) {
Collections.sort(myTags, comparator);
}
public void orderBranches(final Comparator<String> comparator) {
Collections.sort(myBranches, comparator);
}
public String getAuthorEmail() {
return myAuthorEmail;
}
public String getComitterEmail() {
return myComitterEmail;
}
public List<FilePath> getPathsList() {
return myPathsList;
}
@Override
public String toString() {
return myHash.getValue();
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,150 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.history.browser;
import com.intellij.openapi.components.ProjectComponent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.DumbAwareRunnable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.startup.StartupManager;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.VcsListener;
import com.intellij.openapi.vcs.changes.ui.ChangesViewContentManager;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentFactory;
import com.intellij.util.containers.HashMap;
import git4idea.GitBranchesSearcher;
import git4idea.GitVcs;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
public class GitProjectLogManager implements ProjectComponent {
private final static Logger LOG = Logger.getInstance("#git4idea.history.browser.GitProjectLogManager");
private final Project myProject;
private final ProjectLevelVcsManager myVcsManager;
private final AtomicReference<Map<VirtualFile, Content>> myComponentsMap;
private VcsListener myListener;
public GitProjectLogManager(final Project project, final ProjectLevelVcsManager vcsManager) {
myProject = project;
myVcsManager = vcsManager;
myComponentsMap = new AtomicReference<Map<VirtualFile, Content>>(new HashMap<VirtualFile, Content>());
myListener = new VcsListener() {
public void directoryMappingChanged() {
recalculateWindows();
}
};
}
public static GitProjectLogManager getInstance(final Project project) {
return project.getComponent(GitProjectLogManager.class);
}
public void projectClosed() {
myVcsManager.removeVcsListener(myListener);
}
public void projectOpened() {
StartupManager.getInstance(myProject).registerPostStartupActivity(new DumbAwareRunnable() {
public void run() {
myVcsManager.addVcsListener(myListener);
recalculateWindows();
}
});
}
private void recalculateWindows() {
final GitVcs vcs = GitVcs.getInstance(myProject);
final VirtualFile[] roots = myVcsManager.getRootsUnderVcs(vcs);
final Map<VirtualFile, Content> currentState = myComponentsMap.get();
final Set<VirtualFile> currentKeys = new HashSet<VirtualFile>(currentState.keySet());
currentKeys.removeAll(Arrays.asList(roots));
final Map<VirtualFile, Content> newKeys = new HashMap<VirtualFile, Content>(currentState);
final ChangesViewContentManager cvcm = ChangesViewContentManager.getInstance(myProject);
final VirtualFile baseDir = myProject.getBaseDir();
final ContentFactory contentFactory = ContentFactory.SERVICE.getInstance();
for (VirtualFile root : roots) {
if (! currentState.containsKey(root)) {
final GitLogTree tree = new GitLogTree(myProject, root);
tree.setParentDisposable(myProject);
tree.initView();
final Content content = contentFactory.createContent(tree.getComponent(), getCaption(baseDir, root), false);
content.setCloseable(false);
cvcm.addContent(content);
newKeys.put(root, content);
}
}
for (VirtualFile currentKey : currentKeys) {
final Content content = newKeys.remove(currentKey);
cvcm.removeContent(content);
}
myComponentsMap.set(newKeys);
}
private String getCaption(@Nullable VirtualFile baseDir, final VirtualFile root) {
String result = root.getPresentableUrl();
if (baseDir != null) {
if (baseDir.equals(root)) {
result = "<Project root>";
} else {
final String variant = VfsUtil.getRelativePath(baseDir, root, '/');
if ((variant != null) && (variant.length() < result.length())) {
result = variant;
}
}
}
GitBranchesSearcher searcher = null;
try {
searcher = new GitBranchesSearcher(myProject, root, false);
}
catch (VcsException e) {
LOG.info(e);
}
if (searcher != null) {
result += " (" + searcher.getLocal().getName() + ")";
}
return "Log for: " + result;
}
@NotNull
public String getComponentName() {
return "git4idea.history.browser.GitProjectLogManager";
}
public void initComponent() {
}
public void disposeComponent() {
}
}
@@ -0,0 +1,547 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.history.browser;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vcs.RequestsMerger;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
import com.intellij.openapi.vcs.versionBrowser.CommittedChangeListImpl;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Alarm;
import com.intellij.util.Consumer;
import com.intellij.util.Processor;
import com.intellij.util.containers.SLRUCache;
import git4idea.changes.GitChangeUtils;
import git4idea.config.GitConfigUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
class GitTreeController implements ManageGitTreeView {
private final Project myProject;
private final VirtualFile myRoot;
private final GitTreeViewI myTreeView;
private final LowLevelAccess myAccess;
private volatile boolean myInitialized;
// guarded by lock
private final AtomicReference<List<String>> myTags;
private final AtomicReference<List<String>> myBranches;
private final AtomicReference<List<String>> myUsers;
private final MyFiltersStateHolder myFilterHolder;
private final MyFiltersStateHolder myHighlightingHolder;
private final RequestsMerger myFilterRequestsMerger;
//private final RequestsMerger myHighlightingRequestsMerger;
private final MyUpdateStateInterceptor myFiltering;
private final MyUpdateStateInterceptor myHighlighting;
private Alarm myAlarm;
private Portion myFiltered;
private final SLRUCache<SHAHash, CommittedChangeList> myListsCache = new SLRUCache<SHAHash, CommittedChangeList>(16, 64) {
@NotNull
@Override
public CommittedChangeList createValue(SHAHash key) {
try {
return GitChangeUtils.getRevisionChanges(myProject, myRoot, key.getValue());
}
catch (VcsException e) {
return new CommittedChangeListImpl(e.getMessage(), "", "", -1, null, Collections.<Change>emptyList());
}
}
};
GitTreeController(final Project project, final VirtualFile root, final GitTreeViewI treeView) {
myProject = project;
myRoot = root;
myTreeView = treeView;
myAccess = new LowLevelAccessImpl(project, root);
myFilterHolder = new MyFiltersStateHolder();
myHighlightingHolder = new MyFiltersStateHolder();
myTags = new AtomicReference<List<String>>(Collections.<String>emptyList());
myBranches = new AtomicReference<List<String>>(Collections.<String>emptyList());
myUsers = new AtomicReference<List<String>>(Collections.<String>emptyList());
// todo !!!!! dispose
myAlarm = new Alarm(Alarm.ThreadToUse.OWN_THREAD, project);
myFilterRequestsMerger = new RequestsMerger(new Runnable() {
public void run() {
if (myFilterHolder.isDirty()) {
final Portion filtered = loadPortion(myFilterHolder.getStartingPoints(), myFilterHolder.getCurrentPoint(), null,
myFilterHolder.getFilters(), PageSizes.LOAD_SIZE);
if (filtered == null) return;
final List<GitCommit> commitList = filtered.getXFrom(0, PageSizes.VISIBLE_PAGE_SIZE);
myFiltered = filtered;
myTreeView.refreshView(commitList, new TravelTicket(filtered.isStartFound(), filtered.getLast().getDate()));
myFilterHolder.setDirty(false);
}
// highlighting
if (myHighlightingHolder.isNothingSelected()) {
myTreeView.clearHighlighted();
return;
}
myTreeView.acceptHighlighted(loadIdsToHighlight());
myTreeView.refreshFinished();
}
}, new Consumer<Runnable>() {
public void consume(Runnable runnable) {
myTreeView.refreshStarted();
myAlarm.addRequest(runnable, 50);
}
});
/*myHighlightingRequestsMerger = new RequestsMerger(new Runnable() {
public void run() {
if (myHighlightingHolder.isNothingSelected()) {
myTreeView.clearHighlighted();
return;
}
myTreeView.acceptHighlighted(loadIdsToHighlight());
}
}, new Consumer<Runnable>() {
public void consume(Runnable runnable) {
myTreeView.refreshStarted();
application.executeOnPooledThread(runnable);
myTreeView.refreshFinished();
}
});*/
myFiltering = new MyUpdateStateInterceptor(myFilterRequestsMerger, myFilterHolder, null);
myHighlighting = new MyUpdateStateInterceptor(myFilterRequestsMerger, myHighlightingHolder, null);
myFilterHolder.setDirty(true);
}
private Set<SHAHash> loadIdsToHighlight() {
final Portion highlighted;
if (myFiltered == null) {
// todo rewrite when dates are introduced
highlighted = loadPortion(myHighlightingHolder.getStartingPoints(), myFilterHolder.getCurrentPoint(),
null, Collections.<ChangesFilter.Filter>emptyList(), -1);
} else {
highlighted = loadPortion(myHighlightingHolder.getStartingPoints(), myFilterHolder.getCurrentPoint(),
myFiltered.getLast().getDate(), Collections.<ChangesFilter.Filter>emptyList(), -1);
}
final Collection<ChangesFilter.Filter> filters = myHighlightingHolder.getFilters();
final List<ChangesFilter.MemoryFilter> combined = ChangesFilter.combineFilters(filters);
final Set<SHAHash> highlightedIds = new HashSet<SHAHash>();
highlighted.iterateFrom(0, new Processor<GitCommit>() {
public boolean process(GitCommit gitCommit) {
for (ChangesFilter.MemoryFilter filter : combined) {
if (! filter.applyInMemory(gitCommit)) {
return false;
}
}
highlightedIds.add(gitCommit.getHash());
return false;
}
});
return highlightedIds;
}
// !!!! after point is included! (should be)
@Nullable
private Portion loadPortion(final List<String> startingPoints, final Date beforePoint, final Date afterPoint,
final Collection<ChangesFilter.Filter> filtersIn, int maxCnt) {
try {
final Collection<ChangesFilter.Filter> filters = new LinkedList<ChangesFilter.Filter>(filtersIn);
if (beforePoint != null) {
filters.add(new ChangesFilter.BeforeDate(new Date(beforePoint.getTime() - 1)));
}
if (afterPoint != null) {
filters.add(new ChangesFilter.AfterDate(afterPoint));
}
final Portion portion = new Portion(null);
myAccess.loadCommits(startingPoints, Collections.<String>emptyList(), filters, portion, myBranches.get(), maxCnt);
return portion;
}
catch (VcsException e) {
myTreeView.acceptError(e.getMessage());
return null;
}
}
// todo not-so-good
/*@Nullable
private Portion loadPortion(@Nullable final ChangesFilter.Filter filter, final boolean useMaxCnt, final MyFiltersStateHolder... holders) {
try {
final Collection<ChangesFilter.Filter> filters = new LinkedList<ChangesFilter.Filter>();
final List<String> startingPoints = new LinkedList<String>();
// continuation condition
final Date point = myFilterHolder.getCurrentPoint();
if (point != null) {
filters.add(new ChangesFilter.BeforeDate(new Date(point.getTime() - 1)));
}
if (filter != null) {
filters.add(filter);
}
for (MyFiltersStateHolder holder : holders) {
startingPoints.addAll(holder.getStartingPoints());
filters.addAll(holder.getFilters());
}
final Portion portion = new Portion(null);
myAccess.loadCommits(startingPoints, Collections.<String>emptyList(), filters, portion, myBranches.get(), useMaxCnt);
return portion;
}
catch (VcsException e) {
myTreeView.acceptError(e.getMessage());
return null;
}
}*/
private String getStatusMessage() {
// todo
return "Showing";
}
private void loadTagsNBranches(final boolean loadBranches, final boolean loadTags, final boolean loadUsers) {
final List<String> branches = new LinkedList<String>();
final List<String> tags = new LinkedList<String>();
try {
if (loadBranches) {
myAccess.loadAllBranches(branches);
Collections.sort(branches);
myBranches.set(branches);
}
if (loadTags) {
myAccess.loadAllTags(tags);
Collections.sort(tags);
myTags.set(tags);
}
if (loadUsers) {
final List<Pair<String,String>> value = GitConfigUtil.getAllValues(myProject, myRoot, "user.name");
final String username = value.size() == 1 ? value.get(0).getSecond() : null;
final Portion p = loadPortion(Collections.<String>emptyList(), null, null, Collections.<ChangesFilter.Filter>emptyList(), 500);
final Set<String> users = new HashSet<String>();
p.iterateFrom(0, new Processor<GitCommit>() {
public boolean process(GitCommit gitCommit) {
users.add(gitCommit.getAuthor());
users.add(gitCommit.getCommitter());
return false;
}
});
final ArrayList<String> usersList = new ArrayList<String>(users);
Collections.sort(usersList);
if (username != null) {
usersList.remove(username);
usersList.add(0, username);
}
myUsers.set(usersList);
}
}
catch (VcsException e) {
myTreeView.acceptError(e.getMessage());
}
}
// no filter - or saved filter? -> then pass
public void init() {
assert ! myInitialized;
myAlarm.addRequest(new Runnable() {
public void run() {
myFilterRequestsMerger.request();
loadTagsNBranches(true, true, false);
myInitialized = true;
myTreeView.controllerReady();
myAlarm.addRequest(new Runnable() {
public void run() {
loadTagsNBranches(false, false, true);
}
}, 100);
}
}, 100);
}
public boolean hasNext(final TravelTicket ticket) {
return (ticket == null) || (! ticket.isIsBottomReached());
}
public boolean hasPrevious(final TravelTicket ticket) {
return myFilterHolder.getCurrentPoint() != null;
}
public void next(final TravelTicket ticket) {
myFilterHolder.addContinuationPoint(ticket.getLatestDate());
myFilterRequestsMerger.request();
}
public void previous(final TravelTicket ticket) {
myFilterHolder.popContinuationPoint();
myFilterRequestsMerger.request();
}
public void refresh() {
myFilterRequestsMerger.request();
myAlarm.addRequest(new Runnable() {
public void run() {
// todo USERS ARE NOT REFRESHED now! to be fixed after loading them faster
loadTagsNBranches(true, true, false);
}
}, 100);
}
public void getDetails(final Collection<SHAHash> hashes) {
final Application application = ApplicationManager.getApplication();
myAlarm.addRequest(new Runnable() {
public void run() {
final List<CommittedChangeList> loaded = new LinkedList<CommittedChangeList>();
final Set<Long> requested = new HashSet<Long>(hashes.size());
for (SHAHash hash : hashes) {
requested.add(GitChangeUtils.longForSHAHash(hash.getValue()));
final CommittedChangeList changeList = myListsCache.get(hash);
if (requested.contains(changeList.getNumber())) {
loaded.add(changeList);
}
}
if (! loaded.isEmpty()) {
application.invokeLater(new Runnable() {
public void run() {
myTreeView.acceptDetails(loaded);
}
});
}
}
}, 30);
}
public GitTreeFiltering getFiltering() {
return myFiltering;
}
public GitTreeFiltering getHighlighting() {
return myHighlighting;
}
public List<String> getKnownUsers() {
return new ArrayList<String>(myUsers.get());
}
public List<String> getAllBranchesOrdered() {
return new ArrayList<String>(myBranches.get());
}
public List<String> getAllTagsOrdered() {
return new ArrayList<String>(myTags.get());
}
public boolean isInitialized() {
return myInitialized;
}
private static class MyUpdateStateInterceptor implements GitTreeFiltering {
private final MyFiltersStateHolder myState;
@Nullable private final RequestsMerger mySecond;
private final RequestsMerger myRequestsMerger;
protected MyUpdateStateInterceptor(RequestsMerger requestsMerger, MyFiltersStateHolder state, final @Nullable RequestsMerger mySecond) {
myRequestsMerger = requestsMerger;
myState = state;
this.mySecond = mySecond;
}
private void requestRefresh() {
myRequestsMerger.request();
if (mySecond != null) {
mySecond.request();
}
}
public void addFilter(ChangesFilter.Filter filter) {
myState.addFilter(filter);
requestRefresh();
}
public void removeFilter(ChangesFilter.Filter filter) {
myState.removeFilter(filter);
requestRefresh();
}
public void addStartingPoint(String ref) {
myState.addStartingPoint(ref);
requestRefresh();
}
public void removeStartingPoint(String ref) {
myState.removeStartingPoint(ref);
requestRefresh();
}
public void updateExcludePoints(List<String> points) {
myState.updateExcludePoints(points);
requestRefresh();
}
public List<String> getStartingPoints() {
return myState.getStartingPoints();
}
public List<String> getExcludePoints() {
return myState.getExcludePoints();
}
public Collection<ChangesFilter.Filter> getFilters() {
return myState.getFilters();
}
}
private static class MyFiltersStateHolder implements GitTreeFiltering {
private final Object myLock;
private final List<String> myStartingPoints;
private boolean myDirty;
private final List<Date> myContinuationPoints;
@Nullable
private List<String> myExcludePoints;
private final Collection<ChangesFilter.Filter> myFilters;
private MyFiltersStateHolder() {
myLock = new Object();
myStartingPoints = new LinkedList<String>();
myFilters = new LinkedList<ChangesFilter.Filter>();
myContinuationPoints = new LinkedList<Date>();
}
public boolean isDirty() {
synchronized (myLock) {
return myDirty;
}
}
public void setDirty(boolean dirty) {
synchronized (myLock) {
myDirty = dirty;
}
}
@Nullable
public List<String> getExcludePoints() {
synchronized (myLock) {
return myExcludePoints;
}
}
// page starts
public void addContinuationPoint(final Date point) {
synchronized (myLock) {
if ((! myContinuationPoints.isEmpty()) && myContinuationPoints.get(myContinuationPoints.size() - 1).equals(point)) return;
myDirty = true;
myContinuationPoints.add(point);
}
}
@Nullable
public Date popContinuationPoint() {
synchronized (myLock) {
myDirty = true;
return myContinuationPoints.isEmpty() ? null : myContinuationPoints.remove(myContinuationPoints.size() - 1);
}
}
public Date getCurrentPoint() {
synchronized (myLock) {
return myContinuationPoints.isEmpty() ? null : myContinuationPoints.get(myContinuationPoints.size() - 1);
}
}
public void addFilter(ChangesFilter.Filter filter) {
synchronized (myLock) {
myDirty = true;
myFilters.add(filter);
myContinuationPoints.clear();
}
}
public void removeFilter(ChangesFilter.Filter filter) {
synchronized (myLock) {
myDirty = true;
myFilters.remove(filter);
myContinuationPoints.clear();
}
}
public void addStartingPoint(String ref) {
synchronized (myLock) {
myDirty = true;
myStartingPoints.add(ref);
myContinuationPoints.clear();
}
}
public void removeStartingPoint(String ref) {
synchronized (myLock) {
myDirty = true;
myStartingPoints.remove(ref);
myContinuationPoints.clear();
}
}
public void updateExcludePoints(List<String> points) {
synchronized (myLock) {
myDirty = true;
myExcludePoints = points;
myContinuationPoints.clear();
}
}
@Nullable
public List<String> getStartingPoints() {
synchronized (myLock) {
return myStartingPoints;
}
}
public Collection<ChangesFilter.Filter> getFilters() {
synchronized (myLock) {
return myFilters;
}
}
public boolean isNothingSelected() {
synchronized (myLock) {
return ((myExcludePoints == null) || (myExcludePoints.isEmpty())) && myFilters.isEmpty() && myStartingPoints.isEmpty();
}
}
}
}
@@ -0,0 +1,42 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.history.browser;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
public interface GitTreeFiltering {
// void updateFilter(final Collection<ChangesFilter.Filter> filters);
// void updateStartingPoints(final List<String> points);
void addFilter(final ChangesFilter.Filter filter);
void removeFilter(final ChangesFilter.Filter filter);
void addStartingPoint(final String ref);
void removeStartingPoint(final String ref);
void updateExcludePoints(final List<String> points);
// todo COPIES!
@Nullable
List<String> getStartingPoints();
@Nullable
List<String> getExcludePoints();
Collection<ChangesFilter.Filter> getFilters();
}
@@ -0,0 +1,38 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.history.browser;
import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Set;
public interface GitTreeViewI {
void controllerReady();
void refreshView(@NotNull final List<GitCommit> commitsToShow, final TravelTicket ticket);
void showStatusMessage(@NotNull final String message);
void refreshStarted();
void refreshFinished();
// todo details?
void acceptError(String text);
void acceptHighlighted(final Set<SHAHash> ids);
void clearHighlighted();
void acceptDetails(List<CommittedChangeList> changeList);
}
@@ -0,0 +1,20 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.history.browser;
public interface LazyWorker {
void accept(final Runnable work, final Runnable afterWork);
}
@@ -0,0 +1,35 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.history.browser;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.util.Consumer;
import java.util.Collection;
import java.util.List;
public interface LowLevelAccess {
GitCommit getCommitByHash(final SHAHash hash);
// todo define signature
void loadCommits(final List<String> startingPoints, final List<String> endPoints, final Collection<ChangesFilter.Filter> filters,
final Consumer<GitCommit> consumer, final Collection<String> branches, int useMaxCnt) throws VcsException;
Collection<String> getBranchesWithCommit(final SHAHash hash) throws VcsException;
Collection<String> getTagsWithCommit(final SHAHash hash) throws VcsException;
void loadAllBranches(final List<String> sink) throws VcsException;
void loadAllTags(final List<String> sink) throws VcsException;
}
@@ -0,0 +1,101 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.history.browser;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.FilePathImpl;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Consumer;
import git4idea.GitBranch;
import git4idea.GitTag;
import git4idea.history.GitHistoryUtils;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
public class LowLevelAccessImpl implements LowLevelAccess {
private final Project myProject;
private final VirtualFile myRoot;
public LowLevelAccessImpl(final Project project, final VirtualFile root) {
myProject = project;
myRoot = root;
}
public GitCommit getCommitByHash(SHAHash hash) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
public void loadCommits(final @NotNull List<String> startingPoints, @NotNull final List<String> endPoints,
@NotNull final Collection<ChangesFilter.Filter> filters,
@NotNull final Consumer<GitCommit> consumer, final Collection<String> branches, int useMaxCnt)
throws VcsException {
final List<String> parameters = new LinkedList<String>();
if (useMaxCnt > 0) {
parameters.add("--max-count=" + useMaxCnt);
}
for (ChangesFilter.Filter filter : filters) {
filter.applyToCommandLine(parameters);
}
if (! startingPoints.isEmpty()) {
for (String startingPoint : startingPoints) {
parameters.add(startingPoint);
}
} else {
parameters.add("--all");
}
for (String endPoint : endPoints) {
parameters.add("^" + endPoint);
}
// todo can easily be done step-by-step
final List<GitCommit> commits =
GitHistoryUtils.historyWithLinks(myProject, new FilePathImpl(myRoot), new HashSet<String>(branches), parameters.toArray(new String[parameters.size()]));
for (GitCommit commit : commits) {
consumer.consume(commit);
}
}
public Collection<String> getBranchesWithCommit(final SHAHash hash) throws VcsException {
final List<String> result = new LinkedList<String>();
GitBranch.listAsStrings(myProject, myRoot, false, true, result, hash.getValue());
GitBranch.listAsStrings(myProject, myRoot, true, false, result, hash.getValue());
return result;
}
public Collection<String> getTagsWithCommit(final SHAHash hash) throws VcsException {
final List<String> result = new LinkedList<String>();
GitTag.listAsStrings(myProject, myRoot, result, hash.getValue());
return result;
}
public void loadAllBranches(List<String> sink) throws VcsException {
GitBranch.listAsStrings(myProject, myRoot, true, false, sink, null);
GitBranch.listAsStrings(myProject, myRoot, false, true, sink, null);
}
public void loadAllTags(List<String> sink) throws VcsException {
GitTag.listAsStrings(myProject, myRoot, sink, null);
}
}
@@ -0,0 +1,37 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.history.browser;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
interface ManageGitTreeView extends RepositoryCommonData {
void init();
boolean hasNext(@Nullable final TravelTicket ticket);
boolean hasPrevious(@Nullable final TravelTicket ticket);
void next(@Nullable final TravelTicket ticket);
void previous(@Nullable final TravelTicket ticket);
void refresh();
void getDetails(final Collection<SHAHash> hashes);
GitTreeFiltering getFiltering();
GitTreeFiltering getHighlighting();
boolean isInitialized();
}
@@ -0,0 +1,22 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.history.browser;
import java.util.Collection;
public interface Mediator {
Collection<GitCommit> getOrLoadParents(final GitCommit commit);
}
@@ -0,0 +1,21 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.history.browser;
public interface PageSizes {
int LOAD_SIZE = 50;
int VISIBLE_PAGE_SIZE = 50;
}
@@ -0,0 +1,151 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.history.browser;
import com.intellij.util.AsynchConsumer;
import com.intellij.util.Processor;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.Nullable;
import java.util.*;
// todo introduce synchronization here?
public class Portion implements AsynchConsumer<GitCommit> {
private final Map<String, SHAHash> myNameToHash;
private final Map<SHAHash, Integer> myHolder;
private boolean myStartFound;
// ordered
private List<GitCommit> myOrdered;
private final boolean myChildrenWasSet;
private final MultiMap<SHAHash, GitCommit> myOrphanMap;
private final Set<String> myUsers;
// parents, w/out loaded commits; theoretically, those in myOrphan map todo check
private final List<GitCommit> myRoots;
// what was passed into log command
private final List<GitCommit> myLeafs;
@Nullable private final List<SHAHash> myStartingPoints;
public Portion(@Nullable final List<SHAHash> startingPoints) {
myStartingPoints = startingPoints;
myChildrenWasSet = startingPoints == null || startingPoints.isEmpty();
myNameToHash = new HashMap<String, SHAHash>();
myHolder = new HashMap<SHAHash, Integer>();
myOrdered = new LinkedList<GitCommit>();
myRoots = new LinkedList<GitCommit>();
myLeafs = new LinkedList<GitCommit>();
myOrphanMap = new MultiMap<SHAHash, GitCommit>();
myUsers = new HashSet<String>();
}
public void finished() {
//To change body of implemented methods use File | Settings | File Templates.
}
public Set<String> getUsers() {
return myUsers;
}
public void consume(final GitCommit commit) {
myUsers.add(commit.getAuthor());
myUsers.add(commit.getCommitter());
myOrdered.add(commit);
myHolder.put(commit.getHash(), myOrdered.size() - 1);
final Collection<GitCommit> orphans = myOrphanMap.get(commit.getHash());
for (GitCommit orphan : orphans) {
orphan.addParentLink(commit);
}
final List<String> referencies = commit.getBranches();
if (! referencies.isEmpty()) {
final SHAHash hash = commit.getHash();
for (String reference : referencies) {
myNameToHash.put(reference, hash);
}
}
final List<String> tags = commit.getTags();
if (! tags.isEmpty()) {
final SHAHash hash = commit.getHash();
for (String reference : tags) {
myNameToHash.put(reference, hash);
}
}
final Set<SHAHash> parentHashes = commit.getParentsHashes();
if (parentHashes.isEmpty()) {
myStartFound = true;
} else {
for (SHAHash parentHash : parentHashes) {
final Integer idx = myHolder.get(parentHash);
if (idx != null) {
final GitCommit parent = myOrdered.get(idx);
commit.addParentLink(parent);
} else {
myOrphanMap.putValue(parentHash, commit);
}
}
}
}
public boolean isStartFound() {
return myStartFound;
}
@Nullable
public GitCommit getLast() {
return myOrdered.isEmpty() ? null : myOrdered.get(myOrdered.size() - 1);
}
// todo make simplier
public List<GitCommit> getXFrom(final int idx, final int num) {
final List<GitCommit> result = new ArrayList<GitCommit>();
iterateFrom(idx, new Processor<GitCommit>() {
public boolean process(GitCommit gitCommit) {
result.add(gitCommit);
return result.size() == num;
}
});
return result;
}
public void iterateFrom(final int idx, final Processor<GitCommit> processor) {
if ((idx < 0) || (idx > (myOrdered.size() - 1))) return;
for (int i = idx; i < myOrdered.size(); i++) {
final GitCommit commit = myOrdered.get(i);
if (processor.process(commit)) return;
}
}
public SHAHash getHashForReference(final String reference) {
return myNameToHash.get(reference);
}
@Nullable
public GitCommit getByHash(final String hash) {
final SHAHash shaHash = new SHAHash(hash);
final Integer idx = myHolder.get(shaHash);
return idx == null ? null : myOrdered.get(idx);
}
}
@@ -0,0 +1,25 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.history.browser;
// this will be passed to build UI
public interface RecentLogsHolder {
//boolean isReachable(final String childHash, final String parentHash);
// todo move out
boolean isReachable(final GitCommit commit, SHAHash parentHash);
SHAHash getHashByName(final String name);
GitCommit getByHash(final SHAHash hash);
}
@@ -0,0 +1,24 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.history.browser;
import java.util.List;
public interface RepositoryCommonData {
List<String> getAllTagsOrdered();
List<String> getAllBranchesOrdered();
List<String> getKnownUsers();
}
@@ -0,0 +1,51 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.history.browser;
public class SHAHash {
private final String myValue;
public SHAHash(final String value) {
myValue = value;
assert myValue.length() == 40 : myValue;
}
public String getValue() {
return myValue;
}
@Override
public String toString() {
return myValue;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SHAHash shaHash = (SHAHash)o;
if (!myValue.equals(shaHash.myValue)) return false;
return true;
}
@Override
public int hashCode() {
return myValue.hashCode();
}
}
@@ -0,0 +1,67 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.history.browser;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.ui.ColoredListCellRenderer;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Consumer;
import javax.swing.*;
import java.awt.*;
import java.util.List;
public class SelectGitBranchesPopup {
private SelectGitBranchesPopup() {
}
public static void showMe(final Consumer<Object[]> selectedBranchConsumer,
final List<String> branchesAndTags,
boolean tags,
DataContext dc) {
final JList list = new JList();
list.setCellRenderer(new BranchListCellRenderer(tags));
list.setListData(ArrayUtil.toObjectArray(branchesAndTags));
JBPopupFactory.getInstance().createListPopupBuilder(list)
.setTitle("Select " + (tags ? "tags" : "branches"))
.setResizable(true)
.setDimensionServiceKey("Git.Select branches/tags")
.setItemChoosenCallback(new Runnable() {
public void run() {
selectedBranchConsumer.consume(list.getSelectedValues());
}
})
.createPopup().showInBestPositionFor(dc);
}
private static class BranchListCellRenderer extends ColoredListCellRenderer {
private final boolean myTags;
private BranchListCellRenderer(boolean tags) {
myTags = tags;
}
@Override
protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) {
if (value instanceof String) {
final Color color = (myTags) ? Color.blue : Color.green;
append((String) value, new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, color));
}
}
}
}
@@ -0,0 +1,62 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.history.browser;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.ui.IdeBorderFactory;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class SelfRemovingLabel {
public static JPanel createComponent(final String text, final Icon icon, final JComponent parent, final Color textColor) {
final JLabel textLabel = new JLabel(text);
final JPanel panel = new JPanel(new BorderLayout());
final JLabel label = new JLabel(icon);
label.setOpaque(true);
label.setBackground(textLabel.getBackground());
textLabel.setOpaque(true);
textLabel.setForeground(textColor);
panel.add(label, BorderLayout.WEST);
panel.add(textLabel, BorderLayout.EAST);
//textLabel.setToolTipText(myDescription);
//label.setToolTipText(myDescription);
final Border originalBorder;
if (SystemInfo.isMac) {
originalBorder = BorderFactory.createLoweredBevelBorder();
}
else {
originalBorder = textLabel.getBorder();
}
panel.setBorder(new CompoundBorder(IdeBorderFactory.createEmptyBorder(4, 0, 4, 0), originalBorder));
textLabel.setOpaque(true);
textLabel.setBorder(IdeBorderFactory.createEmptyBorder(0, 5, 0, 5));
label.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
parent.remove(panel);
parent.repaint();
}
});
return panel;
}
}
@@ -0,0 +1,60 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.history.browser;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.util.containers.MultiMap;
import java.util.Collection;
import java.util.Set;
// when you're not at first page, or when your filter moved tags out, you might still want to filter by them
public class TagBridge {
final Set<SHAHash> myParentsInterestedIn;
private final LowLevelAccess myAccess;
final MultiMap<SHAHash, String> myTagsForHashes;
final MultiMap<String, SHAHash> myHashesForTags;
public TagBridge(final Set<SHAHash> parentsInterestedIn, final LowLevelAccess access) {
myParentsInterestedIn = parentsInterestedIn;
myAccess = access;
myTagsForHashes = new MultiMap<SHAHash, String>();
myHashesForTags = new MultiMap<String, SHAHash>();
}
public void load() throws VcsException {
for (SHAHash hash : myParentsInterestedIn) {
final Collection<String> refs = myAccess.getBranchesWithCommit(hash);
refs.addAll(myAccess.getTagsWithCommit(hash));
myTagsForHashes.put(hash, refs);
for (String ref : refs) {
myHashesForTags.putValue(ref, hash);
}
}
}
// todo +-
public MultiMap<String, SHAHash> getHashesForTags() {
return myHashesForTags;
}
// todo +-
public MultiMap<SHAHash, String> getTagsForHashes() {
return myTagsForHashes;
}
}
@@ -0,0 +1,53 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.history.browser;
import java.util.Date;
public class TravelTicket {
private final boolean myIsBottomReached;
private final Date myLatestDate;
public TravelTicket(boolean isBottomReached, Date latestDate) {
myIsBottomReached = isBottomReached;
myLatestDate = latestDate;
}
public boolean isIsBottomReached() {
return myIsBottomReached;
}
public Date getLatestDate() {
return myLatestDate;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TravelTicket that = (TravelTicket)o;
if (!myLatestDate.equals(that.myLatestDate)) return false;
return true;
}
@Override
public int hashCode() {
return myLatestDate.hashCode();
}
}
@@ -0,0 +1,50 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.history.browser;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.ui.ListSpeedSearch;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Consumer;
import javax.swing.*;
import java.util.List;
public class UsersPopup {
private UsersPopup() {
}
public static void showUsersPopup(final List<String> knownUsers, final Consumer<String> continuation, DataContext dc) {
// todo pattern to come
final JList list = new JList();
list.setListData(ArrayUtil.toObjectArray(knownUsers));
new ListSpeedSearch(list);
JBPopupFactory.getInstance().createListPopupBuilder(list)
.setTitle("Select author or committer")
.setResizable(true)
.setDimensionServiceKey("Git.Select user")
.setItemChoosenCallback(new Runnable() {
public void run() {
if (list.getSelectedIndices().length > 0) {
continuation.consume((String) list.getSelectedValue());
}
}
})
.createPopup().showInBestPositionFor(dc);
}
}
@@ -304,7 +304,7 @@ public class GitUnstashDialog extends DialogWrapper {
}
myBranches.clear();
try {
GitBranch.listAsStrings(myProject, getGitRoot(), false, true, myBranches);
GitBranch.listAsStrings(myProject, getGitRoot(), false, true, myBranches, null);
}
catch (VcsException e) {
// ignore error
@@ -22,6 +22,7 @@ import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.RequestsMerger;
import com.intellij.util.Consumer;
import com.intellij.util.concurrency.Semaphore;
@@ -87,7 +87,7 @@
<text value="Label"/>
</properties>
</component>
<grid id="cfd61" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="cfd61" layout-manager="GridLayoutManager" row-count="2" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="3" indent="0" use-parent-layout="false">
@@ -96,7 +96,27 @@
</constraints>
<properties/>
<border type="none"/>
<children/>
<children>
<grid id="22415" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children/>
</grid>
<hspacer id="2042">
<constraints>
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
</hspacer>
<vspacer id="1062b">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
</children>
</grid>
</children>
</grid>