import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.FileStatus;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.Change;
import git4idea.commands.GitCommand;
import git4idea.commands.GitSimpleHandler;
import git4idea.commands.StringScanner;
+import git4idea.history.browser.SHAHash;
import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.*;
}
}
+ @Nullable
+ public static SHAHash commitExists(final Project project, final VirtualFile root, final String anyReference) {
+ GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.LOG);
+ h.setNoSSH(true);
+ h.setSilent(true);
+ h.addParameters("--max-count=1", "--pretty=%H", "--encoding=UTF-8", "\"" + anyReference + "\"", "--");
+ try {
+ final String output = h.run().trim();
+ if (StringUtil.isEmptyOrSpaces(output)) return null;
+ return new SHAHash(output);
+ }
+ catch (VcsException e) {
+ return null;
+ }
+ }
+
+ @Nullable
+ public static SHAHash commitExistsByComment(final Project project, final VirtualFile root, final String anyReference) {
+ GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.LOG);
+ h.setNoSSH(true);
+ h.setSilent(true);
+ final String grepParam = "--grep=" + StringUtil.escapeQuotes(anyReference);
+ h.addParameters("--max-count=1", "--pretty=%H", "--all", "--encoding=UTF-8", grepParam, "--");
+ try {
+ final String output = h.run().trim();
+ if (StringUtil.isEmptyOrSpaces(output)) return null;
+ return new SHAHash(output);
+ }
+ catch (VcsException e) {
+ return null;
+ }
+ }
/**
* Parse changelist
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.VcsException;
return rc;
}
+ public static List<Pair<SHAHash, Date>> onlyHashesHistory(Project project, FilePath path, 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);
+ h.addParameters(parameters);
+ h.addParameters("--name-only", "--pretty=format:%x03%H%x00%ct%x00", "--encoding=UTF-8");
+ h.endOptions();
+ h.addRelativePaths(path);
+ String output = h.run();
+ final List<Pair<SHAHash, Date>> rc = new ArrayList<Pair<SHAHash, Date>>();
+ StringTokenizer tk = new StringTokenizer(output, "\u0003", false);
+
+ while (tk.hasMoreTokens()) {
+ final String line = tk.nextToken();
+ final StringTokenizer tk2 = new StringTokenizer(line, "\u0000\n", false);
+ final String hash = tk2.nextToken("\u0000\n");
+ final String dateString = tk2.nextToken("\u0000");
+ final Date date = GitUtil.parseTimestamp(dateString);
+ rc.add(new Pair<SHAHash, Date>(new SHAHash(hash), date));
+ }
+ 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);
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;
}
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Splitter;
+import com.intellij.openapi.ui.popup.*;
import com.intellij.openapi.util.*;
import com.intellij.openapi.vcs.ObjectsConvertor;
import com.intellij.openapi.vcs.changes.Change;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import java.awt.*;
+import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.text.DateFormat;
import java.util.*;
}.callMe();
}
- public void refreshView(@NotNull final List<GitCommit> commitsToShow, final TravelTicket ticket) {
+ public void refreshView(@NotNull final List<GitCommit> commitsToShow, final TravelTicket ticket, final SHAHash jumpTarget) {
new AbstractCalledLater(myProject, ModalityState.NON_MODAL) {
public void run() {
final boolean wasSelected = myCommitsList.getSelectedIndices().length == 0;
myCommitsList.setListData(ArrayUtil.toObjectArray(commitsToShow));
- if ((! commitsToShow.isEmpty()) && (wasSelected || (! Comparing.equal(myTicket, ticket)))) {
- myCommitsList.setSelectedIndex(0);
+ if (jumpTarget != null) {
+ for (int i = 0; i < commitsToShow.size(); i++) {
+ final GitCommit commit = commitsToShow.get(i);
+ if (commit.getHash().equals(jumpTarget)) {
+ myCommitsList.setSelectedIndex(i);
+ break;
+ }
+ }
+ if (myCommitsList.getSelectedIndices().length == 0) {
+ myCommitsList.setSelectedIndex(0);
+ }
+ } else {
+ if ((! commitsToShow.isEmpty()) && (wasSelected || (! Comparing.equal(myTicket, ticket)))) {
+ myCommitsList.setSelectedIndex(0);
+ }
}
myTicket = ticket;
myCommitsList.revalidate();
final MyCherryPick cp = new MyCherryPick();
group.add(cp);
cp.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_P, KeyEvent.ALT_DOWN_MASK | KeyEvent.CTRL_DOWN_MASK)), myPanel);
+ final MyGoto gotoCommit = new MyGoto();
+ group.add(gotoCommit);
+ gotoCommit.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_G, KeyEvent.ALT_DOWN_MASK | KeyEvent.CTRL_DOWN_MASK)), myPanel);
group.add(new MyRefreshAction());
final ActionManager am = ActionManager.getInstance();
}
}
+ // hash, reference name, or comment mask
+ private class MyGoto extends AnAction {
+ private MyGoto() {
+ super("Goto", "Type commit hash, or reference, or regexp for commit message", IconLoader.getIcon("/general/run.png"));
+ }
+
+ @Override
+ public void actionPerformed(final AnActionEvent e) {
+ final JTextField field = new JTextField(30);
+
+ final String[] gotoString = new String[1];
+ final JBPopup[] popup = new JBPopup[1];
+ field.addKeyListener(new KeyAdapter() {
+ @Override
+ public void keyPressed(KeyEvent e) {
+ if (KeyEvent.VK_ENTER == e.getKeyCode()) {
+ gotoString[0] = field.getText();
+ if (popup[0] != null) {
+ popup[0].cancel();
+ }
+ }
+ }
+ });
+ final JPanel panel = new JPanel(new BorderLayout());
+ panel.add(field, BorderLayout.CENTER);
+ final ComponentPopupBuilder builder = JBPopupFactory.getInstance().createComponentPopupBuilder(panel, field);
+ popup[0] = builder.setTitle("Goto")
+ .setResizable(true)
+ .setFocusable(true)
+ .setMovable(true)
+ .setModalContext(true)
+ .setAdText("Commit hash, or reference, or regexp for commit message")
+ .setDimensionServiceKey(myProject, "Git.Log.Tree.Goto", true)
+ .setCancelOnClickOutside(true).setCancelCallback(new Computable<Boolean>() {
+ public Boolean compute() {
+ if (gotoString[0] != null) {
+ tryFind(gotoString[0]);
+ }
+ return Boolean.TRUE;
+ }
+ })
+ .addListener(new JBPopupListener() {
+ public void beforeShown(LightweightWindowEvent event) {
+ IdeFocusManager.findInstanceByContext(e.getDataContext()).requestFocus(field, true);
+ }
+ public void onClosed(LightweightWindowEvent event) {
+ }
+ })
+ .createPopup();
+ UIUtil.installPopupMenuColorAndFonts(popup[0].getContent());
+ UIUtil.installPopupMenuBorder(popup[0].getContent());
+ popup[0].showInBestPositionFor(e.getDataContext());
+ }
+
+ private void tryFind(final String s) {
+ myController.navigateTo(s);
+ }
+ }
+
private class MyCherryPick extends AnAction {
private final Set<SHAHash> myIdsInProgress;
private Alarm myAlarm;
private Portion myFiltered;
+ private SHAHash myJumpTarget;
+
private final SLRUCache<SHAHash, CommittedChangeList> myListsCache = new SLRUCache<SHAHash, CommittedChangeList>(128, 64) {
@NotNull
@Override
}
}
};
+ private Runnable myRefresher;
GitTreeController(final Project project, final VirtualFile root, final GitTreeViewI treeView) {
myProject = project;
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() {
+ myRefresher = new Runnable() {
public void run() {
try {
if (myFilterHolder.isDirty()) {
+ final SHAHash target = myJumpTarget;
+ myJumpTarget = null;
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()));
+ myTreeView.refreshView(commitList, new TravelTicket(filtered.isStartFound(), filtered.getLast().getDate()), target);
myFilterHolder.setDirty(false);
}
myProject.getMessageBus().syncPublisher(GitProjectLogManager.CHECK_CURRENT_BRANCH).consume(myRoot);
}
}
- }, new Consumer<Runnable>() {
+ };
+ myFilterRequestsMerger = new RequestsMerger(myRefresher, new Consumer<Runnable>() {
public void consume(Runnable runnable) {
myTreeView.refreshStarted();
myAlarm.addRequest(runnable, 50);
}
}
+ private List<Pair<SHAHash, Date>> loadLine(final Collection<String> startingPoints, final Date beforePoint, final Date afterPoint,
+ final Collection<ChangesFilter.Filter> filtersIn, int maxCnt) {
+ 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));
+ }
+
+ try {
+ return myAccess.loadCommitHashes(startingPoints, Collections.<String>emptyList(), filters, maxCnt);
+ }
+ catch (VcsException e) {
+ myTreeView.acceptError(e.getMessage());
+ return null;
+ }
+ }
+
+ public SHAHash commitExists(String reference) {
+ return GitChangeUtils.commitExists(myProject, myRoot, reference);
+ }
+
private String getStatusMessage() {
// todo
return "Showing";
myFilterRequestsMerger.request();
}
+ public void navigateTo(@NotNull String reference) {
+ SHAHash hash = GitChangeUtils.commitExists(myProject, myRoot, reference);
+ if (hash == null) {
+ hash = GitChangeUtils.commitExistsByComment(myProject, myRoot, reference);
+ }
+ if (hash == null) {
+ ChangesViewBalloonProblemNotifier.showMe(myProject, "Nothing found for: \"" + reference + "\"", MessageType.WARNING);
+ } else {
+ final SHAHash finalHash = hash;
+ myAlarm.addRequest(new Runnable() {
+ public void run() {
+ // start from beginning
+ final List<Date> wayList = new LinkedList<Date>();
+
+ while (true) {
+ final Date startFrom = wayList.isEmpty() ? null : wayList.get(wayList.size() - 1);
+ final List<Pair<SHAHash, Date>> pairs =
+ loadLine(myFilterHolder.getStartingPoints(), startFrom, null, myFilterHolder.getFilters(), PageSizes.LOAD_SIZE);
+ if (pairs.isEmpty()) return;
+ for (Pair<SHAHash, Date> pair : pairs) {
+ if (finalHash.equals(pair.getFirst())) {
+ // select this page
+ while (myFilterHolder.getCurrentPoint() != null) {
+ myFilterHolder.popContinuationPoint();
+ }
+ for (Date date : wayList) {
+ myFilterHolder.addContinuationPoint(date);
+ }
+ myJumpTarget = finalHash;
+ myFilterHolder.setDirty(true);
+ myRefresher.run();
+ return;
+ }
+ }
+ wayList.add(pairs.get(pairs.size() - 1).getSecond());
+ }
+ }
+ }, 10);
+ }
+ }
+
public void refresh() {
myFilterHolder.setDirty(true);
myFilterRequestsMerger.request();
public interface GitTreeViewI {
void controllerReady();
- void refreshView(@NotNull final List<GitCommit> commitsToShow, final TravelTicket ticket);
+ void refreshView(@NotNull final List<GitCommit> commitsToShow, final TravelTicket ticket, SHAHash jumpTarget);
void showStatusMessage(@NotNull final String message);
void refreshStarted();
*/
package git4idea.history.browser;
+import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.util.Consumer;
+import org.jetbrains.annotations.NotNull;
import java.util.Collection;
+import java.util.Date;
import java.util.List;
public interface LowLevelAccess {
GitCommit getCommitByHash(final SHAHash hash);
- // todo define signature
+
+ List<Pair<SHAHash,Date>> loadCommitHashes(final @NotNull Collection<String> startingPoints,
+ @NotNull final Collection<String> endPoints,
+ @NotNull final Collection<ChangesFilter.Filter> filters,
+ int useMaxCnt) throws VcsException;
+
void loadCommits(final Collection<String> startingPoints, final Collection<String> endPoints, final Collection<ChangesFilter.Filter> filters,
final Consumer<GitCommit> consumer, final Collection<String> branches, int useMaxCnt) throws VcsException;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vcs.FilePathImpl;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.VirtualFile;
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;
+import java.util.*;
public class LowLevelAccessImpl implements LowLevelAccess {
private final static Logger LOG = Logger.getInstance("#git4idea.history.browser.LowLevelAccessImpl");
return null; //To change body of implemented methods use File | Settings | File Templates.
}
+ public List<Pair<SHAHash,Date>> loadCommitHashes(final @NotNull Collection<String> startingPoints,
+ @NotNull final Collection<String> endPoints,
+ @NotNull final Collection<ChangesFilter.Filter> filters,
+ 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);
+ }
+
+ return GitHistoryUtils.onlyHashesHistory(myProject, new FilePathImpl(myRoot), parameters.toArray(new String[parameters.size()]));
+ }
+
public void loadCommits(final @NotNull Collection<String> startingPoints, @NotNull final Collection<String> endPoints,
@NotNull final Collection<ChangesFilter.Filter> filters,
@NotNull final Consumer<GitCommit> consumer, final Collection<String> branches, int useMaxCnt)
*/
package git4idea.history.browser;
+import org.jetbrains.annotations.NotNull;
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 navigateTo(@NotNull final String reference);
void refresh();
List<String> getAllBranchesOrdered();
List<String> getKnownUsers();
void cherryPick(final Collection<SHAHash> hash);
+ // todo remove
+ SHAHash commitExists(String reference);
}