diff options
author | ilya_ivanov | 2011-02-03 18:32:39 +0000 |
---|---|---|
committer | Dariusz Luksza | 2011-02-03 20:39:52 +0000 |
commit | 390b6b146aa218a9c985e6ce9df2845eb575be48 (patch) | |
tree | aafcdfd0b31cb51e5a976ea3adb8976a1bbb9225 | |
parent | d5ad12d2ea644c4800c64c284a626183f3497e68 (diff) | |
download | egit-390b6b146aa218a9c985e6ce9df2845eb575be48.tar.gz egit-390b6b146aa218a9c985e6ce9df2845eb575be48.tar.xz egit-390b6b146aa218a9c985e6ce9df2845eb575be48.zip |
Branches and Tags links in commit message viewer
Some analog of 'gitk' utility links. Added following info to commit
message viewer:
Branches - links to branch refs
Tags - list of tags pointing to this commit
Follows - link to previous tag in history
Precedes - link to next tag in history
Bug: 336223
Change-Id: I1c61d2c7eca14ba534302eec6e38bdbe85e0107f
Signed-off-by: ilya.ivanov <ilya.ivanov@intland.com>
Signed-off-by: Dariusz Luksza <dariusz@luksza.org>
4 files changed, 223 insertions, 8 deletions
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIText.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIText.java index acd4e04541..d03560d7a3 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIText.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIText.java @@ -1684,6 +1684,18 @@ public class UIText extends NLS { public static String CommitMessageViewer_child; /** */ + public static String CommitMessageViewer_branches; + + /** */ + public static String CommitMessageViewer_tags; + + /** */ + public static String CommitMessageViewer_follows; + + /** */ + public static String CommitMessageViewer_precedes; + + /** */ public static String CommitMessageViewer_commit; /** */ 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 0f21ab52fb..ccc9c69de4 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 @@ -109,6 +109,9 @@ class CommitGraphTable { private SWTCommitList allCommits; + // used for resolving PlotCommit objects by ids + private HashMap<String, PlotCommit> commitsMap = null; + private RevFlag highlight; private HistoryPageInput input; @@ -164,8 +167,8 @@ class CommitGraphTable { CommitGraphTable(final Composite parent, final IPageSite site, final MenuManager menuMgr) { - this(parent); - final IAction selectAll = createStandardAction(ActionFactory.SELECT_ALL); + this(parent); + final IAction selectAll = createStandardAction(ActionFactory.SELECT_ALL); getControl().addFocusListener(new FocusListener() { public void focusLost(FocusEvent e) { site.getActionBars().setGlobalActionHandler( @@ -231,8 +234,14 @@ class CommitGraphTable { } void selectCommit(final RevCommit c) { - table.setSelection(new StructuredSelection(c)); - table.reveal(c); + if (c instanceof PlotCommit) { + table.setSelection(new StructuredSelection(c)); + table.reveal(c); + } else { + PlotCommit swtCommit = commitsMap.get(c.getId().name()); + table.setSelection(new StructuredSelection(swtCommit)); + table.reveal(swtCommit); + } } void addSelectionChangedListener(final ISelectionChangedListener l) { @@ -276,13 +285,21 @@ class CommitGraphTable { allCommits = list; table.setInput(asArray); if (asArray != null && asArray.length > 0) { - if (oldList != list) + if (oldList != list) { selectCommit(asArray[0]); + initCommitsMap(); + } } else { table.getTable().deselectAll(); } } + private void initCommitsMap() { + commitsMap = new HashMap<String, PlotCommit>(); + for (PlotCommit commit : allCommits) + commitsMap.put(commit.getId().name(), commit); + } + private void createColumns(final Table rawTable, final TableLayout layout) { final TableColumn graph = new TableColumn(rawTable, SWT.NONE); graph.setResizable(true); diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitMessageViewer.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitMessageViewer.java index 049cef089b..ab234b61bd 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitMessageViewer.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitMessageViewer.java @@ -18,6 +18,10 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -42,10 +46,16 @@ import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jgit.diff.DiffFormatter; import org.eclipse.jgit.diff.RawText; +import org.eclipse.jgit.errors.IncorrectObjectTypeException; +import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.PersonIdent; +import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revplot.PlotCommit; import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; @@ -305,7 +315,7 @@ class CommitMessageViewer extends TextViewer implements for (int i = 0; i < commit.getChildCount(); i++) { final RevCommit p = commit.getChild(i); d.append(UIText.CommitMessageViewer_child); - d.append(": "); //$NON-NLS-1$ + d.append(": "); //$NON-NLS-1$ addLink(d, styles, p); d.append(" ("); //$NON-NLS-1$ d.append(p.getShortMessage()); @@ -313,6 +323,63 @@ class CommitMessageViewer extends TextViewer implements d.append(LF); } + List<Ref> branches = getBranches(); + if (!branches.isEmpty()) { + d.append(UIText.CommitMessageViewer_branches); + d.append(": "); //$NON-NLS-1$ + for (Iterator<Ref> i = branches.iterator(); i.hasNext(); ) { + Ref head = i.next(); + RevCommit p; + try { + p = new RevWalk(db).parseCommit(head.getObjectId()); + addLink(d, formatHeadRef(head), styles, p); + if (i.hasNext()) + d.append(", "); //$NON-NLS-1$ + } catch (MissingObjectException e) { + Activator.logError(e.getMessage(), e); + } catch (IncorrectObjectTypeException e) { + Activator.logError(e.getMessage(), e); + } catch (IOException e) { + Activator.logError(e.getMessage(), e); + } + } + d.append(LF); + } + + String tagsString = getTagsString(); + if (tagsString.length() > 0) { + d.append(UIText.CommitMessageViewer_tags); + d.append(": "); //$NON-NLS-1$ + d.append(tagsString); + d.append(LF); + } + + try { + Ref followingTag = getNextTag(false); + if (followingTag != null) { + d.append(UIText.CommitMessageViewer_follows); + d.append(": "); //$NON-NLS-1$ + RevCommit p = new RevWalk(db).parseCommit(followingTag.getObjectId()); + addLink(d, formatTagRef(followingTag), styles, p); + d.append(LF); + } + } catch (IOException e) { + Activator.logError(e.getMessage(), e); + } + + try { + Ref precedingTag = getNextTag(true); + if (precedingTag != null) { + d.append(UIText.CommitMessageViewer_precedes); + d.append(": "); //$NON-NLS-1$ + RevCommit p = new RevWalk(db).parseCommit(precedingTag.getObjectId()); + addLink(d, formatTagRef(precedingTag), styles, p); + d.append(LF); + } + } catch (IOException e) { + Activator.logError(e.getMessage(), e); + } + makeGrayText(d, styles); d.append(LF); String msg = commit.getFullMessage(); @@ -349,6 +416,37 @@ class CommitMessageViewer extends TextViewer implements getTextWidget().setStyleRanges(arr); } + private String getTagsString() { + StringBuilder sb = new StringBuilder(); + Map<String, Ref> tagsMap = db.getTags(); + for (String tagName : tagsMap.keySet()) { + ObjectId peeledId = tagsMap.get(tagName).getPeeledObjectId(); + if (peeledId != null && peeledId.equals(commit)) { + if (sb.length() > 0) + sb.append(", "); //$NON-NLS-1$ + sb.append(tagName); + } + } + + return sb.toString(); + } + + private String formatHeadRef(Ref ref) { + final String name = ref.getName(); + if (name.startsWith(Constants.R_HEADS)) + return name.substring(Constants.R_HEADS.length()); + else if (name.startsWith(Constants.R_REMOTES)) + return name.substring(Constants.R_REMOTES.length()); + return name; + } + + private String formatTagRef(Ref ref) { + final String name = ref.getName(); + if (name.startsWith(Constants.R_TAGS)) + return name.substring(Constants.R_TAGS.length()); + return name; + } + private void makeGrayText(StringBuilder d, ArrayList<StyleRange> styles) { int p0 = 0; for (int i = 0; i < styles.size(); ++i) { @@ -371,18 +469,23 @@ class CommitMessageViewer extends TextViewer implements } } - private void addLink(final StringBuilder d, + private void addLink(final StringBuilder d, String linkLabel, final ArrayList<StyleRange> styles, final RevCommit to) { final ObjectLink sr = new ObjectLink(); sr.targetCommit = to; sr.foreground = sys_linkColor; sr.underline = true; sr.start = d.length(); - d.append(to.getId().name()); + d.append(linkLabel); sr.length = d.length() - sr.start; styles.add(sr); } + private void addLink(final StringBuilder d, + final ArrayList<StyleRange> styles, final RevCommit to) { + addLink(d, to.getId().name(), styles, to); + } + private void addDiff(final StringBuilder d, final ArrayList<StyleRange> styles) { final DiffFormatter diffFmt = new DiffFormatter( @@ -527,4 +630,83 @@ class CommitMessageViewer extends TextViewer implements } + /** + * Finds nextdoor tagged revition. Searches forwards (in descendants) or backwards (in ancestors) + * @param searchDescendant if <code>false</code>, will search for tagged revision in ancestors + * @return {@link Ref} or <code>null</code> if no tag found + * @throws IOException + */ + private Ref getNextTag(boolean searchDescendant) throws IOException { + RevWalk revWalk = new RevWalk(db); + + Map<String, Ref> tagsMap = db.getTags(); + Ref tagRef = null; + + for (String tagName : tagsMap.keySet()) { + // both RevCommits must be allocated using same RevWalk instance, + // otherwise isMergedInto returns wrong result! + RevCommit current = revWalk.parseCommit(commit); + RevCommit newTag = revWalk.parseCommit(tagsMap.get(tagName).getObjectId()); + + if (newTag.getId().equals(commit)) + continue; + + // check if newTag matches our criteria + if (isMergedInto(revWalk, newTag, current, searchDescendant)) { + if (tagRef != null) { + RevCommit oldTag = revWalk.parseCommit(tagRef.getObjectId()); + + // both oldTag and newTag satisfy search criteria, so taking the closest one + if (isMergedInto(revWalk, oldTag, newTag, searchDescendant)) + tagRef = tagsMap.get(tagName); + } else + tagRef = tagsMap.get(tagName); + } + } + + return tagRef; + } + + /** + * @param rw + * @param base + * @param tip + * @param swap if <code>true</code>, base and tip arguments are swapped + * @return <code>true</code> if there is a path directly from tip to base (and thus base is fully merged into tip); <code>false</code> otherwise. + * @throws IOException + */ + private boolean isMergedInto(final RevWalk rw, final RevCommit base, final RevCommit tip, boolean swap) + throws IOException { + return !swap ? rw.isMergedInto(base, tip) : rw.isMergedInto(tip, base); + } + + /** + * @return List of heads from those current commit is reachable + */ + private List<Ref> getBranches() { + RevWalk revWalk = new RevWalk(db); + List<Ref> result = new ArrayList<Ref>(); + + try { + Map<String, Ref> refsMap = new HashMap<String, Ref>(); + refsMap.putAll(db.getRefDatabase().getRefs(Constants.R_HEADS)); + // add remote heads to search + refsMap.putAll(db.getRefDatabase().getRefs(Constants.R_REMOTES)); + + for (String headName : refsMap.keySet()) { + RevCommit headCommit = revWalk.parseCommit(refsMap.get(headName).getObjectId()); + // the base RevCommit also must be allocated using same RevWalk instance, + // otherwise isMergedInto returns wrong result! + RevCommit base = revWalk.parseCommit(commit); + + if (revWalk.isMergedInto(base, headCommit)) + result.add(refsMap.get(headName)); // commit is reachable from this head + } + } catch (IOException e) { + // skip exception + } + + return result; + } + } diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/uitext.properties b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/uitext.properties index 9a5181c063..cc1c23382b 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/uitext.properties +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/uitext.properties @@ -552,6 +552,10 @@ SpellCheckingMessageArea_paste=&Paste SpellCheckingMessageArea_selectAll=Select &All CommitMessageViewer_author=Author CommitMessageViewer_child=Child +CommitMessageViewer_branches=Branches +CommitMessageViewer_tags=Tags +CommitMessageViewer_follows=Follows +CommitMessageViewer_precedes=Precedes CommitMessageViewer_commit=commit CommitMessageViewer_committer=Committer CommitMessageViewer_errorGettingFileDifference=Can't get file difference of {0}. |