diff options
author | Matthias Sohn | 2012-05-10 14:41:22 +0000 |
---|---|---|
committer | Matthias Sohn | 2012-06-04 14:15:42 +0000 |
commit | 53ff64118482ecdc7221d9896c0717dbe9dab308 (patch) | |
tree | 2eea63a0c05a860a39f87f79f1036e570f0d025e | |
parent | e01d5bd9ca0c0fe8969a2ec883eadac3404f3d05 (diff) | |
download | egit-53ff64118482ecdc7221d9896c0717dbe9dab308.tar.gz egit-53ff64118482ecdc7221d9896c0717dbe9dab308.tar.xz egit-53ff64118482ecdc7221d9896c0717dbe9dab308.zip |
Load history incrementally
Loading history incrementally can drastically reduce memory consumption
and the time needed to render the part of the history the user is
interested in. Most often only the most recent part of the history is
needed. Hence load only one batch of 256 commits initially and keep the
GenerateHistoryJob in order to incrementally load the next batch when
the user has scrolled down half of the loading batch size (128 commits).
JGit-Dependency: Id3b97d88d3b4b2d67561b11f8810cb88fe040823
Change-Id: Ibc88d589ea2a5f233cd65c203bde9187c122a277
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
Signed-off-by: Stefan Lay <stefan.lay@sap.com>
5 files changed, 159 insertions, 73 deletions
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitGraphTable.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitGraphTable.java index 139cc9c5ed..ed684542a0 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitGraphTable.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitGraphTable.java @@ -3,9 +3,8 @@ * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com> * Copyright (C) 2008, Roger C. Soares <rogersoares@intelinet.com.br> * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> - * Copyright (C) 2011, Mathias Kinzler <mathias.kinzler@sap.com> - * Copyright (C) 2011, Matthias Sohn <matthias.sohn@sap.com> - * Copyright (C) 2012, Mathias Kinzler <mathias.kinzler@sap.com> + * Copyright (C) 2011-2012, Mathias Kinzler <mathias.kinzler@sap.com> + * Copyright (C) 2011-2012, Matthias Sohn <matthias.sohn@sap.com> * Copyright (C) 2012, Robin Stocker <robin@nibor.org> * * All rights reserved. This program and the accompanying materials @@ -39,6 +38,7 @@ import org.eclipse.egit.ui.UIPreferences; import org.eclipse.egit.ui.UIText; import org.eclipse.egit.ui.UIUtils; import org.eclipse.egit.ui.internal.history.command.HistoryViewCommands; +import org.eclipse.egit.ui.internal.trace.GitTraceLocation; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.MenuManager; @@ -136,6 +136,8 @@ class CommitGraphTable { private SWTCommitList allCommits; + private int allCommitsLength = 0; + // used for resolving PlotCommit objects by ids private HashMap<String, PlotCommit> commitsMap = null; @@ -151,15 +153,33 @@ class CommitGraphTable { private GraphLabelProvider graphLabelProvider; - CommitGraphTable(Composite parent) { + private final TableLoader tableLoader; + + private boolean trace = GitTraceLocation.HISTORYVIEW.isActive(); + + CommitGraphTable(Composite parent, final TableLoader loader) { nFont = UIUtils.getFont(UIPreferences.THEME_CommitGraphNormalFont); hFont = highlightFont(); + tableLoader = loader; - Table rawTable = new Table(parent, SWT.MULTI | SWT.H_SCROLL + final Table rawTable = new Table(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER | SWT.FULL_SELECTION | SWT.VIRTUAL); rawTable.setHeaderVisible(true); rawTable.setLinesVisible(false); rawTable.setFont(nFont); + rawTable.addListener(SWT.SetData, new Listener() { + public void handleEvent(Event event) { + if (tableLoader != null) { + TableItem item = (TableItem) event.item; + int index = rawTable.indexOf(item); + if (trace) + GitTraceLocation.getTrace().trace( + GitTraceLocation.HISTORYVIEW.getLocation(), + "Item " + index); //$NON-NLS-1$ + tableLoader.loadItem(index); + } + } + }); final TableLayout layout = new TableLayout(); rawTable.setLayout(layout); @@ -226,8 +246,8 @@ class CommitGraphTable { } CommitGraphTable(final Composite parent, final IPageSite site, - final MenuManager menuMgr) { - this(parent); + final MenuManager menuMgr, final TableLoader loader) { + this(parent, loader); final IAction selectAll = createStandardAction(ActionFactory.SELECT_ALL); getControl().addFocusListener(new FocusListener() { @@ -303,6 +323,8 @@ class CommitGraphTable { table.setSelection(new StructuredSelection(c), true); else if (commitsMap != null) { PlotCommit swtCommit = commitsMap.get(c.getId().name()); + if (swtCommit == null && tableLoader != null) + tableLoader.loadCommit(c); if (swtCommit != null) table.setSelection(new StructuredSelection(swtCommit), true); } @@ -350,21 +372,28 @@ class CommitGraphTable { } void setInput(final RevFlag hFlag, final SWTCommitList list, - final SWTCommit[] asArray, HistoryPageInput input) { + final SWTCommit[] asArray, HistoryPageInput input, boolean keepPosition) { + int topIndex = -1; + if (keepPosition) + topIndex = table.getTable().getTopIndex(); setHistoryPageInput(input); final SWTCommitList oldList = allCommits; if (oldList != null && oldList != list) oldList.dispose(); highlight = hFlag; allCommits = list; + int newAllCommitsLength = allCommits.size(); table.setInput(asArray); if (asArray != null && asArray.length > 0) { - if (oldList != list) + if (oldList != list || allCommitsLength < newAllCommitsLength) initCommitsMap(); } else table.getTable().deselectAll(); + allCommitsLength = newAllCommitsLength; if (commitToShow != null) selectCommit(commitToShow); + if (keepPosition) + table.getTable().setTopIndex(topIndex); } void setHistoryPageInput(HistoryPageInput input) { diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitSelectionDialog.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitSelectionDialog.java index eb2df25863..a025312203 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitSelectionDialog.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitSelectionDialog.java @@ -73,7 +73,7 @@ public class CommitSelectionDialog extends TitleAreaDialog { Composite main = new Composite(parent, SWT.NONE); main.setLayout(new GridLayout(1, false)); GridDataFactory.fillDefaults().grab(true, true).applyTo(main); - table = new CommitGraphTable(main); + table = new CommitGraphTable(main, null); table.getTableView().addSelectionChangedListener( new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { @@ -198,7 +198,7 @@ public class CommitSelectionDialog extends TitleAreaDialog { .toString())); setMessage(UIText.CommitSelectionDialog_DialogMessage); table.setInput(highlightFlag, allCommits, allCommits - .toArray(new SWTCommit[allCommits.size()]), null); + .toArray(new SWTCommit[allCommits.size()]), null, true); } private void markStartAllRefs(RevWalk currentWalk, String prefix) diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/GenerateHistoryJob.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/GenerateHistoryJob.java index 822624b913..8d95043898 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/GenerateHistoryJob.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/GenerateHistoryJob.java @@ -1,5 +1,6 @@ /******************************************************************************* * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> + * Copyright (C) 2012, Matthias Sohn <matthias.sohn@sap.com> * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 @@ -24,6 +25,7 @@ import org.eclipse.egit.ui.UIText; import org.eclipse.egit.ui.internal.trace.GitTraceLocation; import org.eclipse.jgit.revwalk.RevFlag; import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; @@ -31,39 +33,41 @@ import org.eclipse.swt.widgets.Display; class GenerateHistoryJob extends Job { private static final int BATCH_SIZE = 256; - private final int maxCommits = Activator.getDefault().getPreferenceStore() - .getInt(UIPreferences.HISTORY_MAX_NUM_COMMITS); - private final GitHistoryPage page; - private final SWTCommitList allCommits; + private final SWTCommitList loadedCommits; - private int lastUpdateCnt; + private int itemToLoad = 1; + + private RevCommit commitToLoad; + + private RevCommit commitToShow; - private long lastUpdateAt; + private int lastUpdateCnt; private boolean trace; private final RevWalk walk; + private RevFlag highlightFlag; + GenerateHistoryJob(final GitHistoryPage ghp, Control control, RevWalk walk) { super(NLS.bind(UIText.HistoryPage_refreshJob, Activator.getDefault() .getRepositoryUtil().getRepositoryName( ghp.getInputInternal().getRepository()))); page = ghp; this.walk = walk; - allCommits = new SWTCommitList(control); - allCommits.source(walk); + highlightFlag = walk.newFlag("highlight"); //$NON-NLS-1$ + loadedCommits = new SWTCommitList(control); + loadedCommits.source(walk); trace = GitTraceLocation.HISTORYVIEW.isActive(); } - public RevWalk getWalk() { - return walk; - } - @Override protected IStatus run(final IProgressMonitor monitor) { IStatus status = Status.OK_STATUS; + int maxCommits = Activator.getDefault().getPreferenceStore() + .getInt(UIPreferences.HISTORY_MAX_NUM_COMMITS); boolean incomplete = false; try { if (trace) @@ -71,40 +75,47 @@ class GenerateHistoryJob extends Job { GitTraceLocation.HISTORYVIEW.getLocation()); try { for (;;) { - final int oldsz = allCommits.size(); + int oldsz = loadedCommits.size(); if (trace) GitTraceLocation.getTrace().trace( GitTraceLocation.HISTORYVIEW.getLocation(), "Filling commit list"); //$NON-NLS-1$ // ensure that filling (here) and reading (CommitGraphTable) // the commit list is thread safe - synchronized (allCommits) { - allCommits.fillTo(oldsz + BATCH_SIZE - 1); + synchronized (loadedCommits) { + if (commitToLoad != null) { + loadedCommits.fillTo(commitToLoad, maxCommits); + commitToShow = commitToLoad; + commitToLoad = null; + } else + loadedCommits.fillTo(oldsz + BATCH_SIZE - 1); } if (monitor.isCanceled()) return Status.CANCEL_STATUS; - if (maxCommits > 0 && allCommits.size() > maxCommits) + final boolean loadIncrementally = !Activator.getDefault().getPreferenceStore() + .getBoolean(UIPreferences.RESOURCEHISTORY_SHOW_FINDTOOLBAR); + if (loadedCommits.size() > itemToLoad + (BATCH_SIZE / 2) + 1 && loadIncrementally) + break; + if (maxCommits > 0 && loadedCommits.size() > maxCommits) incomplete = true; - if (incomplete || oldsz == allCommits.size()) + if (incomplete || oldsz == loadedCommits.size()) break; - if (allCommits.size() != 1) + if (loadedCommits.size() != 1) monitor.setTaskName(MessageFormat .format(UIText.GenerateHistoryJob_taskFoundMultipleCommits, - Integer.valueOf(allCommits.size()))); + Integer.valueOf(loadedCommits.size()))); else monitor.setTaskName(UIText.GenerateHistoryJob_taskFoundSingleCommit); - - final long now = System.currentTimeMillis(); - if (now - lastUpdateAt < 2000 && lastUpdateCnt > 0) - continue; - updateUI(incomplete); - lastUpdateAt = now; } } catch (IOException e) { status = new Status(IStatus.ERROR, Activator.getPluginId(), UIText.GenerateHistoryJob_errorComputingHistory, e); } + if (trace) + GitTraceLocation.getTrace().trace( + GitTraceLocation.HISTORYVIEW.getLocation(), + "Loaded " + loadedCommits.size() + " commits"); //$NON-NLS-1$ //$NON-NLS-2$ updateUI(incomplete); } finally { monitor.done(); @@ -115,19 +126,19 @@ class GenerateHistoryJob extends Job { return status; } - void updateUI(boolean incomplete) { + private void updateUI(boolean incomplete) { if (trace) GitTraceLocation.getTrace().traceEntry( GitTraceLocation.HISTORYVIEW.getLocation()); try { - if (!incomplete && allCommits.size() == lastUpdateCnt) + if (!incomplete && loadedCommits.size() == lastUpdateCnt) return; - final SWTCommit[] asArray = new SWTCommit[allCommits.size()]; - allCommits.toArray(asArray); - RevFlag highlightFlag = walk.newFlag("highlight"); //$NON-NLS-1$ - page.showCommitList(this, allCommits, asArray, incomplete, highlightFlag); - lastUpdateCnt = allCommits.size(); + final SWTCommit[] asArray = new SWTCommit[loadedCommits.size()]; + loadedCommits.toArray(asArray); + page.showCommitList(this, loadedCommits, asArray, commitToShow, incomplete, highlightFlag); + commitToShow = null; + lastUpdateCnt = loadedCommits.size(); } finally { if (trace) GitTraceLocation.getTrace().traceExit( @@ -153,7 +164,7 @@ class GenerateHistoryJob extends Job { Display.getDefault().asyncExec(new Runnable() { public void run() { - allCommits.dispose(); + loadedCommits.dispose(); } }); } @@ -165,4 +176,15 @@ class GenerateHistoryJob extends Job { return super.belongsTo(family); } + void setLoadHint(final int index) { + itemToLoad = index; + } + + void setLoadHint(final RevCommit c) { + commitToLoad = c; + } + + int loadMoreItemsThreshold() { + return loadedCommits.size() - (BATCH_SIZE / 2); + } } diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/GitHistoryPage.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/GitHistoryPage.java index 06c086d9eb..f283a74485 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/GitHistoryPage.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/GitHistoryPage.java @@ -3,7 +3,7 @@ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> * Copyright (c) 2010, Stefan Lay <stefan.lay@sap.com> * Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com> - * Copyright (C) 2010-2011, Matthias Sohn <matthias.sohn@sap.com> + * Copyright (C) 2010-2012, Matthias Sohn <matthias.sohn@sap.com> * Copyright (C) 2012, Daniel megert <daniel_megert@ch.ibm.com> * * All rights reserved. This program and the accompanying materials @@ -24,10 +24,8 @@ import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; -import org.eclipse.core.runtime.jobs.IJobChangeEvent; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.core.runtime.jobs.Job; -import org.eclipse.core.runtime.jobs.JobChangeAdapter; import org.eclipse.egit.core.AdapterUtils; import org.eclipse.egit.core.project.RepositoryMapping; import org.eclipse.egit.ui.Activator; @@ -115,7 +113,9 @@ import org.eclipse.ui.progress.IWorkbenchSiteProgressService; /** Graphical commit history viewer. */ public class GitHistoryPage extends HistoryPage implements RefsChangedListener, - ISchedulingRule { + ISchedulingRule, TableLoader { + + private static final int INITIAL_ITEM = -1; /** actions used in GitHistoryPage **/ private static class GitHistoryPageActions { @@ -739,7 +739,7 @@ public class GitHistoryPage extends HistoryPage implements RefsChangedListener, graphDetailSplit = new SashForm(historyControl, SWT.VERTICAL); GridDataFactory.fillDefaults().grab(true, true).applyTo( graphDetailSplit); - graph = new CommitGraphTable(graphDetailSplit, getSite(), popupMgr); + graph = new CommitGraphTable(graphDetailSplit, getSite(), popupMgr, this); graph.setRelativeDate(isShowingRelativeDates()); graph.setShowEmailAddresses(isShowingEmailAddresses()); @@ -1431,7 +1431,7 @@ public class GitHistoryPage extends HistoryPage implements RefsChangedListener, } void showCommitList(final Job j, final SWTCommitList list, - final SWTCommit[] asArray, final boolean incomplete, final RevFlag highlightFlag) { + final SWTCommit[] asArray, final RevCommit toSelect, final boolean incomplete, final RevFlag highlightFlag) { if (trace) GitTraceLocation.getTrace().traceEntry( GitTraceLocation.HISTORYVIEW.getLocation(), @@ -1442,7 +1442,9 @@ public class GitHistoryPage extends HistoryPage implements RefsChangedListener, graph.getControl().getDisplay().asyncExec(new Runnable() { public void run() { if (!graph.getControl().isDisposed() && job == j) { - graph.setInput(highlightFlag, list, asArray, input); + graph.setInput(highlightFlag, list, asArray, input, true); + if (toSelect != null) + graph.selectCommit(toSelect); if (trace) GitTraceLocation.getTrace().trace( GitTraceLocation.HISTORYVIEW.getLocation(), @@ -1508,7 +1510,7 @@ public class GitHistoryPage extends HistoryPage implements RefsChangedListener, setupFileViewer(walk, db, paths); setupCommentViewer(db); - scheduleNewGenerateHistoryJob(walk); + loadHistory(INITIAL_ITEM, walk); } else // needed for context menu and double click graph.setHistoryPageInput(input); @@ -1779,31 +1781,38 @@ public class GitHistoryPage extends HistoryPage implements RefsChangedListener, return true; } - private void scheduleNewGenerateHistoryJob(RevWalk walk) { - final GenerateHistoryJob rj = new GenerateHistoryJob(this, - graph.getControl(), walk); - rj.setRule(this); - rj.addJobChangeListener(new JobChangeAdapter() { - @Override - public void done(final IJobChangeEvent event) { - rj.getWalk().release(); - final Control graphctl = graph.getControl(); - if (job != rj || graphctl.isDisposed()) - return; - graphctl.getDisplay().asyncExec(new Runnable() { - public void run() { - if (job == rj) - job = null; - } - }); - } - }); - job = rj; + public void loadItem(int item) { + if (job == null || job.loadMoreItemsThreshold() < item) + loadHistory(item, null); + } + + public void loadCommit(RevCommit c) { + if (job == null) + return; + job.setLoadHint(c); + if (trace) + GitTraceLocation.getTrace().trace( + GitTraceLocation.HISTORYVIEW.getLocation(), + "Scheduling GenerateHistoryJob"); //$NON-NLS-1$ + schedule(job); + } + + /** + * Load history items incrementally + * @param itemToLoad hint for index of item that should be loaded + * @param walk the revwalk, used only if itemToLoad == INITIAL_ITEM + */ + private void loadHistory(final int itemToLoad, RevWalk walk) { + if (itemToLoad == INITIAL_ITEM) { + job = new GenerateHistoryJob(this, graph.getControl(), walk); + job.setRule(this); + } + job.setLoadHint(itemToLoad); if (trace) GitTraceLocation.getTrace().trace( GitTraceLocation.HISTORYVIEW.getLocation(), "Scheduling GenerateHistoryJob"); //$NON-NLS-1$ - schedule(rj); + schedule(job); } private IWorkbenchPartSite getPartSite() { diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/TableLoader.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/TableLoader.java new file mode 100644 index 0000000000..1c0f377804 --- /dev/null +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/TableLoader.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (C) 2012, Matthias Sohn <matthias.sohn@sap.com> + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + *******************************************************************************/ +package org.eclipse.egit.ui.internal.history; + +import org.eclipse.jgit.revwalk.RevCommit; + +/** + * Callback interface for incrementally loading table items + */ +public interface TableLoader { + /** + * @param index hint for index of table item to be loaded + */ + void loadItem(int index); + + /** + * @param c commit to be loaded + */ + void loadCommit(RevCommit c); +} |