{"thread":{"id":"12798","subject":"[EGIT PATCH 3/4] Show all branches and children in the revision detail viewer.","startedAt":"2008-03-21T22:08:09Z","lastAt":"2008-03-21T22:08:09Z","messageCount":1,"participants":["Roger C. Soares"],"isPatch":true,"patchVersion":1,"patchTotal":4},"messages":[{"id":"72622","messageId":"1206137289-4076-1-git-send-email-rogersoares@intelinet.com.br","threadId":"12798","inReplyTo":null,"subject":"[EGIT PATCH 3/4] Show all branches and children in the revision detail viewer.","fromName":"Roger C. Soares","fromEmail":"rogersoares@intelinet.com.br","sentAt":"2008-03-21T22:08:09Z","receivedAt":"2008-03-21T22:08:09Z","isPatch":true,"sender":{"key":"rogersoares@intelinet.com.br","avatar":null},"body":"Shows all branches containg the selected commit along with its children in\nthe revision detail viewer. The lines where reordered to match the same\nas in gitk.\n\nThe revision info code has moved to it's own class and uses a background\nthread similiar to the find toolbar to display data that may take a while\nto be retrieved.\n\nSigned-off-by: Roger C. Soares <rogersoares@intelinet.com.br>\n---\n .../src/org/spearce/egit/ui/GitHistoryPage.java    |  248 ++-----------------\n .../src/org/spearce/egit/ui/RevisionInfo.java      |  269 ++++++++++++++++++++\n .../org/spearce/egit/ui/RevisionInfoThread.java    |  212 +++++++++++++++\n 3 files changed, 500 insertions(+), 229 deletions(-)\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/RevisionInfo.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/RevisionInfoThread.java\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/GitHistoryPage.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/GitHistoryPage.java\nindex ae1dfbf..5da095b 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/GitHistoryPage.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/GitHistoryPage.java\n@@ -46,8 +46,6 @@ import org.eclipse.jface.action.IMenuManager;\n import org.eclipse.jface.action.MenuManager;\n import org.eclipse.jface.action.Separator;\n import org.eclipse.jface.resource.JFaceResources;\n-import org.eclipse.jface.text.Document;\n-import org.eclipse.jface.text.TextViewer;\n import org.eclipse.jface.viewers.ColumnWeightData;\n import org.eclipse.jface.viewers.ILazyContentProvider;\n import org.eclipse.jface.viewers.StructuredSelection;\n@@ -83,7 +81,6 @@ import org.eclipse.team.ui.history.HistoryPage;\n import org.eclipse.team.ui.history.IHistoryCompareAdapter;\n import org.eclipse.team.ui.history.IHistoryPageSite;\n import org.eclipse.ui.IActionBars;\n-import org.eclipse.ui.part.IPageSite;\n import org.spearce.egit.core.GitProvider;\n import org.spearce.egit.core.internal.mapping.GitCommitFileRevision;\n import org.spearce.egit.core.internal.mapping.GitFileHistory;\n@@ -91,9 +88,7 @@ import org.spearce.egit.core.internal.mapping.GitFileHistoryProvider;\n import org.spearce.egit.core.internal.mapping.GitFileRevision;\n import org.spearce.egit.core.project.RepositoryMapping;\n import org.spearce.egit.ui.internal.actions.GitCompareRevisionAction;\n-import org.spearce.jgit.lib.Commit;\n import org.spearce.jgit.lib.ObjectId;\n-import org.spearce.jgit.lib.PersonIdent;\n import org.spearce.jgit.lib.Tag;\n import org.spearce.jgit.lib.TopologicalSorter;\n import org.spearce.jgit.lib.Repository.StGitPatch;\n@@ -118,14 +113,12 @@ public class GitHistoryPage extends HistoryPage implements IAdaptable,\n \n \tprivate Composite parentComposite;\n \tprivate SashForm localComposite;\n-\tprivate SashForm revisionInfoComposite;\n+\tprivate RevisionInfo revisionInfoComposite;\n \t/* private */FindToolbar findToolbar;\n \n \t/* private */Preferences prefs = Activator.getDefault().getPluginPreferences();\n \n \t/* private */TableViewer viewer;\n-\t/* private */TextViewer revDetailTextViewer;\n-\t/* private */TextViewer revCommentTextViewer;\n \t/* private */IAction toggleCommentWrapAction;\n \t/* private */IAction toggleRevDetailAction;\n \t/* private */IAction toggleRevCommentAction;\n@@ -164,6 +157,10 @@ public class GitHistoryPage extends HistoryPage implements IAdaptable,\n \tpublic boolean inputSet() {\n \t\tif (viewer != null)\n \t\t\tviewer.setInput(getInput());\n+\t\tif (revisionInfoComposite != null) {\n+\t\t\tIProject project = ((IResource) getInput()).getProject();\n+\t\t\trevisionInfoComposite.setRepositoryMapping(RepositoryMapping.getMapping(project));\n+\t\t}\n \t\t// TODO Auto-generated method stub\n \t\treturn true;\n \t}\n@@ -189,20 +186,20 @@ public class GitHistoryPage extends HistoryPage implements IAdaptable,\n \t\tfindToolbar = new FindToolbar(parentComposite);\n \t\tfindToolbar.addSelectionListener(new Listener() {\n \t\t\tpublic void handleEvent(Event event) {\n-\t\t\t\tcleanRevisionInfoTextViewers();\n+\t\t\t\trevisionInfoComposite.cleanRevisionInfoTextViewers();\n \t\t\t\tint ix = table.getSelectionIndex();\n \t\t\t\tIFileRevision revision = fileRevisions.get(ix);\n \t\t\t\tif(revision instanceof GitCommitFileRevision) {\n-\t\t\t\t\tsetRevisionInfoTextViewers((GitCommitFileRevision) revision);\n+\t\t\t\t\trevisionInfoComposite.setRevisionInfoTextViewers((GitCommitFileRevision) revision);\n \t\t\t\t}\n \t\t\t}\n \t\t});\n \n \t\tcreateTable(localComposite);\n \n-\t\trevisionInfoComposite = new SashForm(localComposite, SWT.HORIZONTAL);\n-\t\tcreateRevDetailTextViewer(revisionInfoComposite);\n-\t\tcreateRevCommentTextViewer(revisionInfoComposite);\n+\t\tIProject project = ((IResource) getInput()).getProject();\n+\t\tRepositoryMapping repositoryMapping = RepositoryMapping.getMapping(project);\n+\t\trevisionInfoComposite = new RevisionInfo(localComposite, getHistoryPageSite().getWorkbenchPageSite(), repositoryMapping);\n \n \t\tIHistoryPageSite parentSite = getHistoryPageSite();\n \t\tif (parentSite != null && parentSite instanceof DialogHistoryPageSite)\n@@ -245,7 +242,7 @@ public class GitHistoryPage extends HistoryPage implements IAdaptable,\n \t\t\t\t}\n \n \t\t\t\t// Updates the revision info text viewers\n-\t\t\t\tcleanRevisionInfoTextViewers();\n+\t\t\t\trevisionInfoComposite.cleanRevisionInfoTextViewers();\n \t\t\t\tif(selection2.length == 1) {\n \t\t\t\t\t// if the table item is not visible in the UI and it's selected via keyboard\n \t\t\t\t\t// this listener is called before the listener that sets the item data.\n@@ -255,7 +252,7 @@ public class GitHistoryPage extends HistoryPage implements IAdaptable,\n \t\t\t\t\t\trevision = fileRevisions.get(ix);\n \t\t\t\t\t\tselection2[0] = revision;\n \t\t\t\t\t}\n-\t\t\t\t\tsetRevisionInfoTextViewers((GitCommitFileRevision)revision);\n+\t\t\t\t\trevisionInfoComposite.setRevisionInfoTextViewers((GitCommitFileRevision)revision);\n \t\t\t\t}\n \n \t\t\t\tcompareAction.setCurrentFileRevision(fileRevisions.get(0));\n@@ -419,7 +416,7 @@ public class GitHistoryPage extends HistoryPage implements IAdaptable,\n \t\tshowHideFindToolbar();\n \n \t\tlocalComposite.setWeights(new int[] {65, 35});\n-\t\trevisionInfoComposite.setWeights(new int[] {40, 60});\n+\t\trevisionInfoComposite.setWeights(new int[] {50, 50});\n \t}\n \n \t/* private */void updateResourceHistoryComposites() {\n@@ -427,22 +424,14 @@ public class GitHistoryPage extends HistoryPage implements IAdaptable,\n \t\tboolean showRevDetail = toggleRevDetailAction.isChecked();\n \t\tboolean showRevComment = toggleRevCommentAction.isChecked();\n \n-\t\trevCommentTextViewer.getTextWidget().setWordWrap(commentWrap);\n+\t\trevisionInfoComposite.setCommentWordWrap(commentWrap);\n \n-\t\tif(showRevDetail && showRevComment) {\n-\t\t\tlocalComposite.setMaximizedControl(null);\n-\t\t\trevisionInfoComposite.setMaximizedControl(null);\n+\t\tif(!(showRevDetail || showRevComment)) {\n+\t\t\tlocalComposite.setMaximizedControl(table);\n \t\t} else {\n-\t\t\tif(!(showRevDetail || showRevComment)) {\n-\t\t\t\tlocalComposite.setMaximizedControl(table);\n-\t\t\t} else if(showRevDetail) {\n-\t\t\t\tlocalComposite.setMaximizedControl(null);\n-\t\t\t\trevisionInfoComposite.setMaximizedControl(revDetailTextViewer.getControl());\n-\t\t\t} else {\n-\t\t\t\tlocalComposite.setMaximizedControl(null);\n-\t\t\t\trevisionInfoComposite.setMaximizedControl(revCommentTextViewer.getControl());\n-\t\t\t}\n+\t\t\tlocalComposite.setMaximizedControl(null);\n \t\t}\n+\t\trevisionInfoComposite.setShowViewers(showRevDetail, showRevComment);\n \t}\n \n \t/* private */void updateShowTooltips() {\n@@ -600,205 +589,6 @@ public class GitHistoryPage extends HistoryPage implements IAdaptable,\n \t\t}\n \t}\n \n-\tprivate void createRevDetailTextViewer(SashForm composite) {\n-\t\trevDetailTextViewer = new TextViewer(composite, SWT.H_SCROLL | SWT.V_SCROLL | SWT.READ_ONLY);\n-\t}\n-\n-\tprivate void createRevCommentTextViewer(SashForm composite) {\n-\t\trevCommentTextViewer = new TextViewer(composite, SWT.H_SCROLL | SWT.V_SCROLL | SWT.READ_ONLY);\n-\t}\n-\n-\t/* private */void setRevisionInfoTextViewers(GitCommitFileRevision rev) {\n-\t\tStringBuilder revisionInfo = new StringBuilder();\n-\t\tif (appliedPatches != null) {\n-\t\t\tString id = rev.getContentIdentifier();\n-\t\t\tif (!id.equals(\"Workspace\") && !id.equals(\"Index\")) {\n-\t\t\t\tStGitPatch patch = (StGitPatch) appliedPatches\n-\t\t\t\t\t\t.get(new ObjectId(id));\n-\t\t\t\tif (patch != null) {\n-\t\t\t\t\trevisionInfo.append(\"Patch: \");\n-\t\t\t\t\trevisionInfo.append(patch.getName());\n-\t\t\t\t}\n-\t\t\t} else {\n-\t\t\t\trevisionInfo.append(id.toString());\n-\t\t\t}\n-\t\t}\n-\t\tif (revisionInfo.length() == 0) {\n-\t\t\trevisionInfo.append(\"Commit: \");\n-\t\t\trevisionInfo.append(rev.getContentIdentifier());\n-\t\t}\n-\n-\t\t// Gets the tags for this revision.\n-\t\t// TODO: Understand this better and see if there's a simpler/shared way to do it.\n-\t\tIProject project = ((IResource) getInput()).getProject();\n-\t\tRepositoryMapping repositoryMapping = RepositoryMapping.getMapping(project);\n-\t\tMap<ObjectId,Tag[]> newtags = new HashMap<ObjectId,Tag[]>();\n-\t\ttry {\n-\t\t\tfor (String name : repositoryMapping.getRepository().getTags()) {\n-\t\t\t\tTag t = repositoryMapping.getRepository().mapTag(name);\n-\t\t\t\tTag[] samecommit = newtags.get(t.getObjId());\n-\t\t\t\tif (samecommit==null) {\n-\t\t\t\t\tsamecommit = new Tag[] { t };\n-\t\t\t\t} else {\n-\t\t\t\t\tTag[] n=new Tag[samecommit.length+1];\n-\t\t\t\t\tfor (int j=0; j<samecommit.length; ++j)\n-\t\t\t\t\t\tn[j] = samecommit[j];\n-\t\t\t\t\tn[n.length-1] = t;\n-\t\t\t\t\tsamecommit = n;\n-\t\t\t\t}\n-\t\t\t\tnewtags.put(t.getObjId(), samecommit);\n-\t\t\t}\n-\n-\t\t\tString id = rev.getContentIdentifier();\n-\t\t\tif (!id.equals(\"Workspace\") && !id.equals(\"Index\")) {\n-\t\t\t\tObjectId oid = new ObjectId(id);\n-\t\t\t\tStringBuilder b=new StringBuilder();\n-\t\t\t\tif (tags != null) {\n-\t\t\t\t\tTag[] matching = tags.get(oid);\n-\t\t\t\t\tif (matching != null) {\n-\t\t\t\t\t\tfor (Tag t : matching) {\n-\t\t\t\t\t\t\tif (b.length() > 0)\n-\t\t\t\t\t\t\t\tb.append(\", \");\n-\t\t\t\t\t\t\tString tag = t.getTag();\n-\t\t\t\t\t\t\tb.append(tag);\n-\t\t\t\t\t\t}\n-\t\t\t\t\t}\n-\t\t\t\t}\n-\t\t\t\tif(b.length() > 0) {\n-\t\t\t\t\trevisionInfo.append(\"\\nTags: \");\n-\t\t\t\t\trevisionInfo.append(b);\n-\t\t\t\t}\n-\t\t\t}\n-\t\t} catch (IOException e) {\n-\t\t\t// TODO: better error handling.\n-\t\t\te.printStackTrace();\n-\t\t}\n-\n-\t\t// Gets the branches for this revision.\n-\t\t// TODO: Branches should always show all the branches from a commit.\n-\t\tMap<ObjectId, String[]> newBranches = new HashMap<ObjectId, String[]>();\n-\t\ttry {\n-\t\t\tfor (String branch : repositoryMapping.getRepository().getBranches()) {\n-\t\t\t\tObjectId id = repositoryMapping.getRepository().resolve(branch);\n-\t\t\t\tString[] samecommit = newBranches.get(id);\n-\t\t\t\tif (samecommit == null) {\n-\t\t\t\t\tsamecommit = new String[] { branch };\n-\t\t\t\t} else {\n-\t\t\t\t\tString[] n=new String[samecommit.length + 1];\n-\t\t\t\t\tfor (int j=0; j<samecommit.length; ++j)\n-\t\t\t\t\t\tn[j] = samecommit[j];\n-\t\t\t\t\tn[n.length-1] = branch;\n-\t\t\t\t\tsamecommit = n;\n-\t\t\t\t}\n-\t\t\t\tnewBranches.put(id, samecommit);\n-\t\t\t}\n-\t\t\tString[] samecommit = newBranches.get(currentHead);\n-\t\t\tif (samecommit == null) {\n-\t\t\t\tsamecommit = new String[] { \"HEAD\" };\n-\t\t\t} else {\n-\t\t\t\tString[] n=new String[samecommit.length + 1];\n-\t\t\t\tfor (int j=0; j<samecommit.length; ++j)\n-\t\t\t\t\tn[j] = samecommit[j];\n-\t\t\t\tn[n.length-1] = \"HEAD\";\n-\t\t\t\tsamecommit = n;\n-\t\t\t}\n-\t\t\tnewBranches.put(currentHead, samecommit);\n-\t\t\tbranches = newBranches;\n-\n-\t\t\tString id = rev.getContentIdentifier();\n-\t\t\tif (branches != null) {\n-\t\t\t\tStringBuilder b=new StringBuilder();\n-\t\t\t\tObjectId oid = new ObjectId(id);\n-\t\t\t\tString[] matching = branches.get(oid);\n-\t\t\t\tif (matching != null) {\n-\t\t\t\t\tfor (String t : matching) {\n-\t\t\t\t\t\tif (b.length() > 0)\n-\t\t\t\t\t\t\tb.append(\", \");\n-\t\t\t\t\t\tif (t.startsWith(\"refs/heads/\"))\n-\t\t\t\t\t\t\tt = t.substring(11);\n-\t\t\t\t\t\tb.append(t);\n-\t\t\t\t\t}\n-\t\t\t\t}\n-\t\t\t\tif(b.length() > 0) {\n-\t\t\t\t\trevisionInfo.append(\"\\nBranches: \");\n-\t\t\t\t\trevisionInfo.append(b);\n-\t\t\t\t}\n-\t\t\t}\n-\t\t} catch (IOException e) {\n-\t\t\te.printStackTrace();\n-\t\t}\n-\n-\t\tCommit commit = rev.getCommit();\n-\t\trevisionInfo.append(\"\\nAuthor: \");\n-\t\trevisionInfo.append(formatPersonIdentForRevInfo(commit.getAuthor()));\n-\t\trevisionInfo.append(\"\\nCommitter: \");\n-\t\trevisionInfo.append(formatPersonIdentForRevInfo(commit.getCommitter()));\n-\t\tif(commit.getParentIds() != null) {\n-\t\t\tfor(ObjectId pid : commit.getParentIds()) {\n-\t\t\t\trevisionInfo.append(\"\\nParent: \");\n-\t\t\t\trevisionInfo.append(pid.toString());\n-\t\t\t\ttry {\n-\t\t\t\t\tCommit pc = repositoryMapping.getRepository().mapCommit(pid);\n-\t\t\t\t\trevisionInfo.append(\" (\");\n-\t\t\t\t\tString cmesg = pc.getMessage();\n-\t\t\t\t\tint enterIndex = cmesg.indexOf(\"\\n\");\n-\t\t\t\t\tif(enterIndex > 0) {\n-\t\t\t\t\t\tcmesg = cmesg.substring(0, enterIndex);\n-\t\t\t\t\t}\n-\t\t\t\t\trevisionInfo.append(cmesg);\n-\t\t\t\t\trevisionInfo.append(\" )\");\n-\t\t\t\t} catch (IOException e) {\n-\t\t\t\t\t// TODO: better error handling.\n-\t\t\t\t\te.printStackTrace();\n-\t\t\t\t}\n-\t\t\t}\n-\t\t}\n-\n-\t\tString comment = rev.getComment();\n-\t\trevDetailTextViewer.setDocument(new Document(revisionInfo.toString()));\n-\t\trevCommentTextViewer.setDocument(new Document(comment));\n-\n-\t\t// Adds the first comment line in the status line.\n-\t\tint enterIndex = comment.indexOf(\"\\n\");\n-\t\tif(enterIndex > 0) {\n-\t\t\tcomment = comment.substring(0, enterIndex);\n-\t\t}\n-\t\tIPageSite workbenchPageSite = getHistoryPageSite().getWorkbenchPageSite();\n-\t\tif (workbenchPageSite != null) {\n-\t\t\tworkbenchPageSite.getActionBars().getStatusLineManager().setMessage(comment);\n-\t\t}\n-\n-\t}\n-\n-\tprivate String formatPersonIdentForRevInfo(PersonIdent p) {\n-\t\tStringBuilder sb = new StringBuilder();\n-\t\tsb.append(p.getName());\n-\t\tif(p.getEmailAddress() != null) {\n-\t\t\tsb.append(\" <\");\n-\t\t\tsb.append(p.getEmailAddress());\n-\t\t\tsb.append(\">\");\n-\t\t}\n-\t\tif(p.getWhen() != null) {\n-\t\t\tsb.append(\" \");\n-\t\t\tsb.append(DATETIMETZ_FORMAT.format(p.getWhen()));\n-\t\t}\n-\n-\t\treturn sb.toString();\n-\t}\n-\n-\tvoid cleanRevisionInfoTextViewers() {\n-\t\tif(revDetailTextViewer != null && revCommentTextViewer != null) {\n-\t\t\trevDetailTextViewer.setDocument(new Document(\"\"));\n-\t\t\trevCommentTextViewer.setDocument(new Document(\"\"));\n-\n-\t\t\t// Cleans the status line\n-\t\t\tIPageSite workbenchPageSite = getHistoryPageSite().getWorkbenchPageSite();\n-\t\t\tif (workbenchPageSite != null) {\n-\t\t\t\tworkbenchPageSite.getActionBars().getStatusLineManager().setMessage(null);\n-\t\t\t}\n-\t\t}\n-\t}\n-\n \tprivate void createTable(Composite composite) {\n \t\ttable = new Table(composite, SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI\n \t\t\t\t| SWT.FULL_SELECTION | SWT.VIRTUAL);\n@@ -1067,7 +857,7 @@ public class GitHistoryPage extends HistoryPage implements IAdaptable,\n \t\t\t\ttable.getDisplay().asyncExec(new Runnable() {\n \t\t\t\t\n \t\t\t\t\tpublic void run() {\n-\t\t\t\t\t\tcleanRevisionInfoTextViewers();\n+\t\t\t\t\t\trevisionInfoComposite.cleanRevisionInfoTextViewers();\n \n \t\t\t\t\t\ttable.removeAll();\n \t\t\t\t\t\ttable.setItemCount(fileRevisions.size());\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/RevisionInfo.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/RevisionInfo.java\nnew file mode 100644\nindex 0000000..1104158\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/RevisionInfo.java\n@@ -0,0 +1,269 @@\n+/*\n+ *  Copyright (C) 2008  Roger C. Soares\n+ *\n+ *  This library is free software; you can redistribute it and/or\n+ *  modify it under the terms of the GNU Lesser General Public\n+ *  License, version 2.1, as published by the Free Software Foundation.\n+ *\n+ *  This library is distributed in the hope that it will be useful,\n+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n+ *  Lesser General Public License for more details.\n+ *\n+ *  You should have received a copy of the GNU Lesser General Public\n+ *  License along with this library; if not, write to the Free Software\n+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301\n+ */\n+package org.spearce.egit.ui;\n+\n+import java.text.SimpleDateFormat;\n+import java.util.Set;\n+\n+import org.eclipse.jface.text.Document;\n+import org.eclipse.jface.text.TextViewer;\n+import org.eclipse.swt.SWT;\n+import org.eclipse.swt.custom.SashForm;\n+import org.eclipse.swt.widgets.Composite;\n+import org.eclipse.ui.part.IPageSite;\n+import org.spearce.egit.core.internal.mapping.GitCommitFileRevision;\n+import org.spearce.egit.core.project.RepositoryMapping;\n+import org.spearce.egit.core.util.RepositoryUtil;\n+import org.spearce.jgit.lib.Commit;\n+import org.spearce.jgit.lib.ObjectId;\n+import org.spearce.jgit.lib.PersonIdent;\n+\n+/**\n+ * A revision viewer for the history page.\n+ * \n+ * @see RevisionInfoThread\n+ */\n+public class RevisionInfo extends SashForm {\n+\n+\tprivate static final SimpleDateFormat DATETIMETZ_FORMAT = new SimpleDateFormat(\n+\t\t\t\"yyyy-MM-dd HH:mm:ss z\");\n+\n+\tprivate IPageSite workbenchPageSite;\n+\n+\tprivate RepositoryMapping repositoryMapping;\n+\n+\tprivate TextViewer revDetailTextViewer;\n+\n+\tprivate TextViewer revCommentTextViewer;\n+\n+\tprivate boolean showDetail;\n+\n+\tprivate boolean showComment;\n+\n+\t/**\n+\t * Creates the viewer.\n+\t * \n+\t * @param parent\n+\t *            the parent widget\n+\t * @param workbenchPageSite\n+\t *            the workbench site\n+\t * @param repositoryMapping\n+\t *            the mapping containing the repository to be searched\n+\t */\n+\tpublic RevisionInfo(Composite parent, IPageSite workbenchPageSite,\n+\t\t\tRepositoryMapping repositoryMapping) {\n+\t\tsuper(parent, SWT.HORIZONTAL);\n+\n+\t\tthis.workbenchPageSite = workbenchPageSite;\n+\t\tthis.repositoryMapping = repositoryMapping;\n+\t\trevDetailTextViewer = new TextViewer(this, SWT.H_SCROLL | SWT.V_SCROLL\n+\t\t\t\t| SWT.READ_ONLY);\n+\t\trevCommentTextViewer = new TextViewer(this, SWT.H_SCROLL | SWT.V_SCROLL\n+\t\t\t\t| SWT.READ_ONLY);\n+\n+\t\tsetWeights(new int[] { 50, 50 });\n+\t}\n+\n+\t/**\n+\t * Sets word wrapping in the comment viewer.\n+\t * \n+\t * @param wrap\n+\t *            true enables wrap; false disables\n+\t */\n+\tpublic void setCommentWordWrap(boolean wrap) {\n+\t\trevCommentTextViewer.getTextWidget().setWordWrap(wrap);\n+\t}\n+\n+\t/**\n+\t * Updates the viewers and the status line to show information for\n+\t * <code>rev</code>.\n+\t * \n+\t * @param rev\n+\t */\n+\tpublic void setRevisionInfoTextViewers(GitCommitFileRevision rev) {\n+\t\tStringBuilder initialStaticInfo = new StringBuilder();\n+\t\tCommit commit = rev.getCommit();\n+\n+\t\tif (showDetail) {\n+\t\t\ttry {\n+\t\t\t\t// Commit line.\n+\t\t\t\tinitialStaticInfo.append(\"Commit: \");\n+\t\t\t\tinitialStaticInfo.append(rev.getContentIdentifier());\n+\t\t\t\tinitialStaticInfo.append(\"\\n\");\n+\n+\t\t\t\t// Author line.\n+\t\t\t\tinitialStaticInfo.append(\"Author: \");\n+\t\t\t\tinitialStaticInfo.append(formatPersonIdentForRevInfo(commit\n+\t\t\t\t\t\t.getAuthor()));\n+\t\t\t\tinitialStaticInfo.append(\"\\n\");\n+\n+\t\t\t\t// Committer line.\n+\t\t\t\tinitialStaticInfo.append(\"Committer: \");\n+\t\t\t\tinitialStaticInfo.append(formatPersonIdentForRevInfo(commit\n+\t\t\t\t\t\t.getCommitter()));\n+\t\t\t\tinitialStaticInfo.append(\"\\n\");\n+\n+\t\t\t\tRepositoryUtil repoUtil = new RepositoryUtil(repositoryMapping\n+\t\t\t\t\t\t.getRepository());\n+\n+\t\t\t\t// Tags line.\n+\t\t\t\tSet<String> commitTags = repoUtil.getTagNamesForCommit(commit);\n+\t\t\t\tint count = 0;\n+\t\t\t\tif (commitTags != null) {\n+\t\t\t\t\tinitialStaticInfo.append(\"Tags: \");\n+\t\t\t\t\tfor (String tagName : commitTags) {\n+\t\t\t\t\t\tif (count != 0) {\n+\t\t\t\t\t\t\tinitialStaticInfo.append(\", \");\n+\t\t\t\t\t\t}\n+\t\t\t\t\t\tinitialStaticInfo.append(tagName);\n+\t\t\t\t\t\tcount++;\n+\t\t\t\t\t}\n+\t\t\t\t\tinitialStaticInfo.append(\"\\n\");\n+\t\t\t\t}\n+\n+\t\t\t\t// Parent lines.\n+\t\t\t\tStringBuilder[] parentDescs = null;\n+\t\t\t\tif (commit.getParentIds() != null) {\n+\t\t\t\t\tObjectId[] parentIds = commit.getParentIds();\n+\t\t\t\t\tparentDescs = new StringBuilder[parentIds.length];\n+\t\t\t\t\tcount = 0;\n+\t\t\t\t\tfor (ObjectId pid : parentIds) {\n+\t\t\t\t\t\tparentDescs[count] = new StringBuilder();\n+\t\t\t\t\t\tinitialStaticInfo.append(\"Parent: \");\n+\t\t\t\t\t\tparentDescs[count].append(pid.toString());\n+\n+\t\t\t\t\t\tCommit pc = repositoryMapping.getRepository()\n+\t\t\t\t\t\t\t\t.mapCommit(pid);\n+\t\t\t\t\t\tparentDescs[count].append(\" (\");\n+\t\t\t\t\t\tString cmesg = pc.getMessage();\n+\t\t\t\t\t\tint enterIndex = cmesg.indexOf(\"\\n\");\n+\t\t\t\t\t\tif (enterIndex > 0) {\n+\t\t\t\t\t\t\tcmesg = cmesg.substring(0, enterIndex);\n+\t\t\t\t\t\t}\n+\t\t\t\t\t\tparentDescs[count].append(cmesg);\n+\t\t\t\t\t\tparentDescs[count].append(\")\");\n+\t\t\t\t\t\tinitialStaticInfo.append(parentDescs[count]);\n+\t\t\t\t\t\tinitialStaticInfo.append(\"\\n\");\n+\n+\t\t\t\t\t\tcount++;\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t\trevDetailTextViewer.setDocument(new Document(initialStaticInfo\n+\t\t\t\t\t\t.toString()));\n+\n+\t\t\t\t// Starts the revision thread to complete with the details that\n+\t\t\t\t// may take some time to compute.\n+\t\t\t\tRevisionInfoThread revInfoThread = new RevisionInfoThread();\n+\t\t\t\trevInfoThread.repositoryMapping = repositoryMapping;\n+\t\t\t\trevInfoThread.revDetailTextViewer = revDetailTextViewer;\n+\t\t\t\trevInfoThread.rev = rev;\n+\t\t\t\trevInfoThread.initialStaticInfo = initialStaticInfo;\n+\t\t\t\trevInfoThread.parentDescs = parentDescs;\n+\t\t\t\trevInfoThread.start();\n+\t\t\t} catch (Exception e) {\n+\t\t\t\t// TODO Auto-generated catch block\n+\t\t\t\te.printStackTrace();\n+\t\t\t\tStringBuilder revisionInfo = new StringBuilder();\n+\t\t\t\trevisionInfo.append(initialStaticInfo);\n+\t\t\t\trevisionInfo.append(\"\\n\");\n+\t\t\t\trevisionInfo.append(e.toString());\n+\t\t\t\trevDetailTextViewer.setDocument(new Document(revisionInfo\n+\t\t\t\t\t\t.toString()));\n+\t\t\t}\n+\t\t}\n+\n+\t\tString comment = rev.getComment();\n+\t\tif (showComment) {\n+\t\t\trevCommentTextViewer.setDocument(new Document(comment));\n+\t\t}\n+\n+\t\t// Adds the first comment line in the status line.\n+\t\tint enterIndex = comment.indexOf(\"\\n\");\n+\t\tif (enterIndex > 0) {\n+\t\t\tcomment = comment.substring(0, enterIndex);\n+\t\t}\n+\n+\t\tif (workbenchPageSite != null) {\n+\t\t\tworkbenchPageSite.getActionBars().getStatusLineManager()\n+\t\t\t\t\t.setMessage(comment);\n+\t\t}\n+\t}\n+\n+\tprivate static String formatPersonIdentForRevInfo(PersonIdent p) {\n+\t\tStringBuilder sb = new StringBuilder();\n+\t\tsb.append(p.getName());\n+\t\tif (p.getEmailAddress() != null) {\n+\t\t\tsb.append(\" <\");\n+\t\t\tsb.append(p.getEmailAddress());\n+\t\t\tsb.append(\">\");\n+\t\t}\n+\t\tif (p.getWhen() != null) {\n+\t\t\tsb.append(\" \");\n+\t\t\tsb.append(DATETIMETZ_FORMAT.format(p.getWhen()));\n+\t\t}\n+\n+\t\treturn sb.toString();\n+\t}\n+\n+\t/**\n+\t * The revision info has 2 viewers. This methods sets which viewers to show.\n+\t * \n+\t * @param showRevDetail\n+\t *            true shows the revision detail viewer; false hides\n+\t * @param showRevComment\n+\t *            true shows the comment viewers; false hides\n+\t */\n+\tpublic void setShowViewers(boolean showRevDetail, boolean showRevComment) {\n+\t\tthis.showDetail = showRevDetail;\n+\t\tthis.showComment = showRevComment;\n+\t\tif (!showDetail) {\n+\t\t\tthis.setMaximizedControl(revCommentTextViewer.getControl());\n+\t\t} else if (!showComment) {\n+\t\t\tthis.setMaximizedControl(revDetailTextViewer.getControl());\n+\t\t} else {\n+\t\t\tthis.setMaximizedControl(null);\n+\t\t}\n+\t}\n+\n+\t/**\n+\t * Blanks the viewers and the status line.\n+\t */\n+\tpublic void cleanRevisionInfoTextViewers() {\n+\t\tif (revDetailTextViewer != null && revCommentTextViewer != null) {\n+\t\t\tRevisionInfoThread.updateGlobalThreadIx();\n+\t\t\trevDetailTextViewer.setDocument(new Document(\"\"));\n+\t\t\trevCommentTextViewer.setDocument(new Document(\"\"));\n+\n+\t\t\t// Cleans the status line\n+\t\t\tif (workbenchPageSite != null) {\n+\t\t\t\tworkbenchPageSite.getActionBars().getStatusLineManager()\n+\t\t\t\t\t\t.setMessage(null);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\t/**\n+\t * Sets the mapping containing the repository to be searched.\n+\t * \n+\t * @param repositoryMapping\n+\t *            the mapping\n+\t */\n+\tpublic void setRepositoryMapping(RepositoryMapping repositoryMapping) {\n+\t\tthis.repositoryMapping = repositoryMapping;\n+\t}\n+\n+}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/RevisionInfoThread.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/RevisionInfoThread.java\nnew file mode 100644\nindex 0000000..27fd87f\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/RevisionInfoThread.java\n@@ -0,0 +1,212 @@\n+/*\n+ *  Copyright (C) 2008  Roger C. Soares\n+ *\n+ *  This library is free software; you can redistribute it and/or\n+ *  modify it under the terms of the GNU Lesser General Public\n+ *  License, version 2.1, as published by the Free Software Foundation.\n+ *\n+ *  This library is distributed in the hope that it will be useful,\n+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n+ *  Lesser General Public License for more details.\n+ *\n+ *  You should have received a copy of the GNU Lesser General Public\n+ *  License along with this library; if not, write to the Free Software\n+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301\n+ */\n+package org.spearce.egit.ui;\n+\n+import java.util.Set;\n+import java.util.concurrent.CancellationException;\n+\n+import org.eclipse.core.runtime.IProgressMonitor;\n+import org.eclipse.jface.text.Document;\n+import org.eclipse.jface.text.TextViewer;\n+import org.eclipse.swt.widgets.Display;\n+import org.spearce.egit.core.internal.mapping.GitCommitFileRevision;\n+import org.spearce.egit.core.project.RepositoryMapping;\n+import org.spearce.egit.core.util.CommitMetaInfo;\n+import org.spearce.egit.core.util.RepositoryUtil;\n+import org.spearce.jgit.lib.Commit;\n+import org.spearce.jgit.lib.ObjectId;\n+\n+/**\n+ * This class prints info the revisionInfo viewer that may take some time to be\n+ * retrieved. Only one thread is executed at a time.\n+ * <p>\n+ * This class maintains a <code>globalThreadIx</code> internal variable that\n+ * is incremented for each new thread started and the current running thread\n+ * constantly checks this variable. If the current thread has the same value as\n+ * <code>globalThreadIx</code> it continues executing, if it has a lower value\n+ * it means that a more recent commit was selected and the current isn't\n+ * necessary any more, so the current thread returns.\n+ * </p>\n+ * \n+ * @see RevisionInfo\n+ */\n+public class RevisionInfoThread extends Thread implements IProgressMonitor {\n+\n+\tRepositoryMapping repositoryMapping;\n+\n+\tTextViewer revDetailTextViewer;\n+\n+\tGitCommitFileRevision rev;\n+\n+\tStringBuilder initialStaticInfo;\n+\n+\tStringBuilder[] parentDescs;\n+\n+\tprivate static Display display = Display.getDefault();\n+\n+\tprivate static int globalThreadIx = 0;\n+\n+\tprivate int currentThreadIx;\n+\n+\t/**\n+\t * Creates a new object and increments the internal\n+\t * <code>globalThreadIx</code> variable causing any earlier running thread\n+\t * to return.\n+\t */\n+\tpublic RevisionInfoThread() {\n+\t\tsuper(\"revisioninfo_thread\" + ++globalThreadIx);\n+\t\tcurrentThreadIx = globalThreadIx;\n+\t}\n+\n+\tpublic void run() {\n+\t\texec(this);\n+\t}\n+\n+\tprivate synchronized static void exec(RevisionInfoThread revThread) {\n+\t\t// If it isn't the last event, just ignore it.\n+\t\tif (revThread.currentThreadIx < globalThreadIx) {\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tStringBuilder metaInfo = new StringBuilder();\n+\t\ttry {\n+\t\t\tCommit commit = revThread.rev.getCommit();\n+\n+\t\t\tRepositoryUtil repoUtil = new RepositoryUtil(\n+\t\t\t\t\trevThread.repositoryMapping.getRepository());\n+\t\t\tCommitMetaInfo commitMetaInfo = repoUtil.getCommitMetaInfo(commit,\n+\t\t\t\t\trevThread);\n+\n+\t\t\t// Child lines.\n+\t\t\tSet<Commit> children = commitMetaInfo.getChildrenCommits();\n+\t\t\tif (children != null) {\n+\t\t\t\tfor (Commit child : children) {\n+\t\t\t\t\tmetaInfo.append(\"Child: \");\n+\t\t\t\t\tObjectId cid = child.getCommitId();\n+\t\t\t\t\tmetaInfo.append(cid.toString());\n+\n+\t\t\t\t\tCommit cc = revThread.repositoryMapping.getRepository()\n+\t\t\t\t\t\t\t.mapCommit(cid);\n+\t\t\t\t\tmetaInfo.append(\" (\");\n+\t\t\t\t\tString cmesg = cc.getMessage();\n+\t\t\t\t\tint enterIndex = cmesg.indexOf(\"\\n\");\n+\t\t\t\t\tif (enterIndex > 0) {\n+\t\t\t\t\t\tcmesg = cmesg.substring(0, enterIndex);\n+\t\t\t\t\t}\n+\t\t\t\t\tmetaInfo.append(cmesg);\n+\t\t\t\t\tmetaInfo.append(\")\");\n+\t\t\t\t\tmetaInfo.append(\"\\n\");\n+\t\t\t\t}\n+\t\t\t}\n+\n+\t\t\t// Branches line.\n+\t\t\tSet<String> commitBranches = commitMetaInfo\n+\t\t\t\t\t.getBranchNamesContainingCommit();\n+\t\t\tif (commitBranches != null) {\n+\t\t\t\tmetaInfo.append(\"Branches: \");\n+\t\t\t\tint count = 0;\n+\t\t\t\tfor (String branchName : commitBranches) {\n+\t\t\t\t\tif (count != 0) {\n+\t\t\t\t\t\tmetaInfo.append(\", \");\n+\t\t\t\t\t}\n+\t\t\t\t\tif (branchName.startsWith(\"refs/heads/\")) {\n+\t\t\t\t\t\tmetaInfo.append(branchName.substring(11));\n+\t\t\t\t\t} else {\n+\t\t\t\t\t\tmetaInfo.append(branchName);\n+\t\t\t\t\t}\n+\t\t\t\t\tcount++;\n+\t\t\t\t}\n+\t\t\t\tmetaInfo.append(\"\\n\");\n+\t\t\t}\n+\t\t} catch (CancellationException e) {\n+\t\t\treturn;\n+\t\t} catch (Exception e) {\n+\t\t\t// TODO Auto-generated catch block\n+\t\t\te.printStackTrace();\n+\t\t\tif (revThread.currentThreadIx < globalThreadIx) {\n+\t\t\t\treturn;\n+\t\t\t}\n+\t\t\tprint(revThread, metaInfo, e.toString());\n+\t\t}\n+\n+\t\tif (revThread.currentThreadIx < globalThreadIx) {\n+\t\t\treturn;\n+\t\t}\n+\t\tprint(revThread, metaInfo, null);\n+\t}\n+\n+\tprivate static void print(final RevisionInfoThread revThread,\n+\t\t\tStringBuilder metaInfo, String errorMsg) {\n+\t\tStringBuilder revisionInfo = new StringBuilder();\n+\t\trevisionInfo.append(revThread.initialStaticInfo);\n+\t\trevisionInfo.append(metaInfo);\n+\t\tif (errorMsg != null) {\n+\t\t\trevisionInfo.append(\"\\n\");\n+\t\t\trevisionInfo.append(errorMsg);\n+\t\t}\n+\n+\t\tfinal String revInfo = revisionInfo.toString();\n+\t\tdisplay.asyncExec(new Runnable() {\n+\t\t\tpublic void run() {\n+\t\t\t\tif (revThread.currentThreadIx < globalThreadIx\n+\t\t\t\t\t\t|| revThread.revDetailTextViewer.getControl()\n+\t\t\t\t\t\t\t\t.isDisposed()) {\n+\t\t\t\t\treturn;\n+\t\t\t\t}\n+\t\t\t\trevThread.revDetailTextViewer\n+\t\t\t\t\t\t.setDocument(new Document(revInfo));\n+\t\t\t}\n+\t\t});\n+\t}\n+\n+\tstatic void updateGlobalThreadIx() {\n+\t\t++globalThreadIx;\n+\t}\n+\n+\tpublic void beginTask(String arg0, int arg1) {\n+\t\t// Empty\n+\t}\n+\n+\tpublic void done() {\n+\t\t// Empty\n+\t}\n+\n+\tpublic void internalWorked(double arg0) {\n+\t\t// Empty\n+\t}\n+\n+\tpublic boolean isCanceled() {\n+\t\treturn currentThreadIx < globalThreadIx;\n+\t}\n+\n+\tpublic void setCanceled(boolean arg0) {\n+\t\t// Empty\n+\t}\n+\n+\tpublic void setTaskName(String arg0) {\n+\t\t// Empty\n+\t}\n+\n+\tpublic void subTask(String arg0) {\n+\t\t// Empty\n+\t}\n+\n+\tpublic void worked(int arg0) {\n+\t\t// Empty\n+\t}\n+\n+}\n-- \n1.5.4.1\n"}]}