Don't try to match tags on a workspace revision
[egit.git] / org.spearce.egit.ui / src / org / spearce / egit / ui / GitHistoryPage.java
blobccae4f7b7180205c4da233b0cef905740e0ca0a6
1 /*
2 * Copyright (C) 2006 Robin Rosenberg
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License, version 2.1, as published by the Free Software Foundation.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
17 package org.spearce.egit.ui;
19 import java.io.IOException;
20 import java.util.Date;
21 import java.util.HashMap;
22 import java.util.Map;
24 import org.eclipse.compare.CompareConfiguration;
25 import org.eclipse.compare.structuremergeviewer.ICompareInput;
26 import org.eclipse.core.resources.IContainer;
27 import org.eclipse.core.resources.IProject;
28 import org.eclipse.core.resources.IResource;
29 import org.eclipse.core.resources.IResourceChangeEvent;
30 import org.eclipse.core.resources.IResourceChangeListener;
31 import org.eclipse.core.resources.ResourcesPlugin;
32 import org.eclipse.core.runtime.IAdaptable;
33 import org.eclipse.core.runtime.IProgressMonitor;
34 import org.eclipse.core.runtime.IStatus;
35 import org.eclipse.core.runtime.NullProgressMonitor;
36 import org.eclipse.core.runtime.Status;
37 import org.eclipse.core.runtime.jobs.Job;
38 import org.eclipse.jface.action.Action;
39 import org.eclipse.jface.action.IMenuListener;
40 import org.eclipse.jface.action.IMenuManager;
41 import org.eclipse.jface.action.MenuManager;
42 import org.eclipse.jface.viewers.ColumnWeightData;
43 import org.eclipse.jface.viewers.ILazyTreeContentProvider;
44 import org.eclipse.jface.viewers.ITableLabelProvider;
45 import org.eclipse.jface.viewers.ITreeContentProvider;
46 import org.eclipse.jface.viewers.LabelProvider;
47 import org.eclipse.jface.viewers.StructuredSelection;
48 import org.eclipse.jface.viewers.TableLayout;
49 import org.eclipse.jface.viewers.TreeViewer;
50 import org.eclipse.jface.viewers.Viewer;
51 import org.eclipse.swt.SWT;
52 import org.eclipse.swt.events.MouseEvent;
53 import org.eclipse.swt.events.MouseListener;
54 import org.eclipse.swt.events.MouseMoveListener;
55 import org.eclipse.swt.events.SelectionAdapter;
56 import org.eclipse.swt.events.SelectionEvent;
57 import org.eclipse.swt.graphics.Image;
58 import org.eclipse.swt.graphics.Point;
59 import org.eclipse.swt.layout.GridData;
60 import org.eclipse.swt.layout.GridLayout;
61 import org.eclipse.swt.widgets.Composite;
62 import org.eclipse.swt.widgets.Control;
63 import org.eclipse.swt.widgets.Event;
64 import org.eclipse.swt.widgets.Listener;
65 import org.eclipse.swt.widgets.Menu;
66 import org.eclipse.swt.widgets.Tree;
67 import org.eclipse.swt.widgets.TreeColumn;
68 import org.eclipse.swt.widgets.TreeItem;
69 import org.eclipse.team.core.RepositoryProvider;
70 import org.eclipse.team.core.history.IFileHistory;
71 import org.eclipse.team.core.history.IFileHistoryProvider;
72 import org.eclipse.team.core.history.IFileRevision;
73 import org.eclipse.team.internal.ui.TeamUIMessages;
74 import org.eclipse.team.internal.ui.history.DialogHistoryPageSite;
75 import org.eclipse.team.ui.history.HistoryPage;
76 import org.eclipse.team.ui.history.IHistoryCompareAdapter;
77 import org.eclipse.team.ui.history.IHistoryPageSite;
78 import org.spearce.egit.core.GitProvider;
79 import org.spearce.egit.core.internal.mapping.GitFileRevision;
80 import org.spearce.egit.core.project.RepositoryMapping;
81 import org.spearce.egit.ui.internal.actions.GitCompareRevisionAction;
82 import org.spearce.jgit.lib.Commit;
83 import org.spearce.jgit.lib.ObjectId;
84 import org.spearce.jgit.lib.Tag;
85 import org.spearce.jgit.lib.Repository.StGitPatch;
87 public class GitHistoryPage extends HistoryPage implements IAdaptable,
88 IHistoryCompareAdapter {
90 private static final String PREF_SHOWALLPROJECTVERSIONS = "org.spearce.egit.ui.githistorypage.showallprojectversions";
91 private static final String PREF_SHOWALLFOLDERVERSIONS = "org.spearce.egit.ui.githistorypage.showallfolderversions";
93 private Composite localComposite;
95 private TreeViewer viewer;
97 private Tree tree;
99 private IFileRevision[] fileRevisions;
101 protected boolean hintShowDiffNow;
103 private boolean showAllVersions;
105 private boolean showAllFolderVersions;
107 public GitHistoryPage(Object object) {
108 setInput(object);
109 showAllVersions = Activator.getDefault().getPreferenceStore()
110 .getBoolean(PREF_SHOWALLPROJECTVERSIONS);
111 showAllFolderVersions = Activator.getDefault().getPreferenceStore()
112 .getBoolean(PREF_SHOWALLFOLDERVERSIONS);
115 public boolean inputSet() {
116 if (viewer != null)
117 viewer.setInput(getInput());
118 // TODO Auto-generated method stub
119 return true;
122 public void createControl(Composite parent) {
123 localComposite = new Composite(parent, SWT.NONE);
124 GridLayout layout = new GridLayout();
125 layout.marginHeight = 0;
126 layout.marginWidth = 0;
127 localComposite.setLayout(layout);
128 GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
129 data.grabExcessVerticalSpace = true;
130 localComposite.setLayoutData(data);
132 createTree(localComposite);
134 IHistoryPageSite parentSite = getHistoryPageSite();
135 if (parentSite != null && parentSite instanceof DialogHistoryPageSite)
136 parentSite.setSelectionProvider(viewer);
138 final GitCompareRevisionAction compareAction = new GitCompareRevisionAction(
139 "Compare");
140 final GitCompareRevisionAction compareActionPrev = new GitCompareRevisionAction(
141 "Show commit");
142 tree.addMouseListener(new MouseListener() {
144 public void mouseUp(MouseEvent e) {
147 public void mouseDown(MouseEvent e) {
148 hintShowDiffNow = e.button==1;
151 public void mouseDoubleClick(MouseEvent e) {
156 tree.addMouseMoveListener(new MouseMoveListener() {
157 TreeItem lastItem;
158 public void mouseMove(MouseEvent e) {
159 TreeItem item = tree.getItem(new Point(e.x,e.y));
160 if (item != null && item!=lastItem) {
161 IFileRevision rev = (IFileRevision) item.getData();
162 if (rev == null)
163 return;
164 String commitStr=null;
165 if (appliedPatches!=null) {
166 String id = rev.getContentIdentifier();
167 if (!id.equals("Workspace")) {
168 StGitPatch patch = (StGitPatch) appliedPatches.get(new ObjectId(id));
169 if (patch!=null)
170 commitStr = "Patch: "+patch.getName();
171 } else {
172 commitStr = "Workspace:";
175 if (commitStr == null)
176 commitStr = "Commit: "+rev.getContentIdentifier();
177 tree.setToolTipText(commitStr+"\nAuthor:\t"+rev.getAuthor()+"\nDate:\t"+new Date(rev.getTimestamp())+"\n\n"+rev.getComment());
179 lastItem = item;
183 tree.addSelectionListener(new SelectionAdapter() {
184 public void widgetSelected(SelectionEvent e) {
185 // update the current
186 TreeItem[] selection = tree.getSelection();
187 IFileRevision[] selection2 = new IFileRevision[selection.length];
188 for (int i = 0; i < selection.length; ++i) {
189 selection2[i] = (IFileRevision) selection[i].getData();
192 compareAction.setCurrentFileRevision(fileRevisions[0]);
193 compareAction.selectionChanged(new StructuredSelection(
194 selection2));
195 IProject project = ((IResource) getInput()).getProject();
196 GitProvider provider = (GitProvider)RepositoryProvider
197 .getProvider(project);
198 RepositoryMapping repositoryMapping = provider.getData().getRepositoryMapping(project);
199 try {
200 if (selection2.length == 1 && hintShowDiffNow) {
201 ObjectId[] parentIds = ((GitFileRevision)selection2[0]).getCommit().getParentIds();
202 if (parentIds.length > 0) {
203 ObjectId parentId = parentIds[0];
204 Commit parent = repositoryMapping.getRepository().mapCommit(parentId);
205 IFileRevision previous = new GitFileRevision(parent,
206 ((GitFileRevision)selection2[0]).getResource(),
207 ((GitFileRevision)selection2[0]).getCount()+1);
208 compareActionPrev.setCurrentFileRevision(null);
209 compareActionPrev.selectionChanged(new StructuredSelection(new IFileRevision[] {selection2[0], previous}));
210 System.out.println("detail="+e.detail);
211 tree.getDisplay().asyncExec(new Runnable() {
212 public void run() {
213 if (GitCompareRevisionAction.findReusableCompareEditor(GitHistoryPage.this.getSite().getPage()) != null)
214 compareActionPrev.run();
218 } else {
219 compareActionPrev.setCurrentFileRevision(null);
220 compareActionPrev.selectionChanged(new StructuredSelection(new IFileRevision[0]));
222 } catch (IOException e1) {
223 // TODO Auto-generated catch block
224 e1.printStackTrace();
226 hintShowDiffNow = false;
229 compareAction.setPage(this);
230 compareActionPrev.setPage(this);
231 MenuManager menuMgr = new MenuManager();
232 Menu menu = menuMgr.createContextMenu(tree);
233 menuMgr.addMenuListener(new IMenuListener() {
234 public void menuAboutToShow(IMenuManager menuMgr) {
235 menuMgr.add(compareAction);
236 menuMgr.add(compareActionPrev);
239 menuMgr.setRemoveAllWhenShown(true);
240 tree.setMenu(menu);
242 GitHistoryResourceListener resourceListener = new GitHistoryResourceListener();
243 ResourcesPlugin.getWorkspace().addResourceChangeListener(
244 resourceListener, IResourceChangeEvent.POST_CHANGE);
246 Action showAllVersionsAction = new Action("\u2200" /* unicode: FOR ALL */) {
247 public void run() {
248 setShowAllVersions(isChecked());
249 if (historyRefreshJob.cancel()) {
250 System.out.println("rescheduling");
251 historyRefreshJob.schedule();
252 } else {
253 System.out.println("failed to cancel?");
257 showAllVersionsAction
258 .setToolTipText("Show all versions for the project containing the resource");
259 showAllVersionsAction.setChecked(isShowAllVersions());
260 getSite().getActionBars().getToolBarManager()
261 .add(showAllVersionsAction);
263 Action showAllFolderVersionsAction = new Action("F") {
264 public void run() {
265 setShowAllFolderVersion(isChecked());
266 if (historyRefreshJob.cancel()) {
267 System.out.println("rescheduling");
268 historyRefreshJob.schedule();
269 } else {
270 System.out.println("failed to cancel?");
274 showAllFolderVersionsAction
275 .setToolTipText("Show all versions for the folder containing the resource");
276 showAllFolderVersionsAction.setChecked(isShowAllFolderVersions());
277 getSite().getActionBars().getToolBarManager().add(
278 showAllFolderVersionsAction);
281 private boolean isShowAllVersions() {
282 return showAllVersions;
285 protected void setShowAllVersions(boolean showAllVersions) {
286 this.showAllVersions = showAllVersions;
287 Activator.getDefault().getPreferenceStore().setValue(
288 PREF_SHOWALLPROJECTVERSIONS, showAllVersions);
291 private boolean isShowAllFolderVersions() {
292 return showAllFolderVersions;
295 protected void setShowAllFolderVersion(boolean showAllFolderVersions) {
296 this.showAllFolderVersions = showAllFolderVersions;
297 Activator.getDefault().getPreferenceStore().setValue(
298 PREF_SHOWALLFOLDERVERSIONS, showAllFolderVersions);
301 class GitHistoryResourceListener implements IResourceChangeListener {
303 public void resourceChanged(IResourceChangeEvent event) {
304 System.out.println("resourceChanged(" + event + ")");
309 class GitHistoryLabelProvider extends LabelProvider implements
310 ITableLabelProvider {
312 public Image getColumnImage(Object element, int columnIndex) {
313 return null;
316 public String getColumnText(Object element, int columnIndex) {
317 if (columnIndex == 0) {
318 int count = ((GitFileRevision) element).getCount();
319 if (count < 0)
320 return "";
321 else
322 if (count == 0)
323 return "HEAD";
324 else
325 return "HEAD~"+count;
328 if (columnIndex == 1) {
329 String rss = ((IFileRevision) element).getURI().toString();
330 String rs = rss.substring(rss.length()-10);
331 String id = ((IFileRevision) element).getContentIdentifier();
332 if (appliedPatches!=null) {
333 if (!id.equals("Workspace")) {
334 StGitPatch patch = (StGitPatch) appliedPatches.get(new ObjectId(id));
335 if (patch!=null)
336 return patch.getName();
339 if (id != null)
340 if (id.length() > 9) // make sure "Workspace" is spelled out
341 return id.substring(0, 7) + "..";
342 else
343 return id;
344 else
345 return id + "@.." + rs;
348 if (columnIndex == 2) {
349 String id = ((IFileRevision)element).getContentIdentifier();
350 if (!id.equals("Workspace")) {
351 return "";
353 ObjectId oid = new ObjectId(id);
354 StringBuilder b=new StringBuilder();
355 if (tags != null) {
356 Tag[] matching = tags.get(oid);
357 if (matching != null) {
358 for (Tag t : matching) {
359 if (b.length() > 0)
360 b.append(' ');
361 b.append(t.getTag());
365 if (branches != null) {
366 if (b.length() >0)
367 b.append('\n');
368 String[] matching = branches.get(oid);
369 if (matching != null) {
370 for (String t : matching) {
371 if (b.length() > 0)
372 b.append(' ');
373 b.append(t);
377 return b.toString();
380 if (columnIndex == 3) {
381 Date d = new Date(((IFileRevision) element).getTimestamp());
382 if (d.getTime() == -1)
383 return "";
384 return d.toString();
386 if (columnIndex == 4)
387 return ((IFileRevision) element).getAuthor();
389 if (columnIndex == 5) {
390 String comment = ((IFileRevision) element).getComment();
391 if (comment == null)
392 return null;
393 int p = comment.indexOf('\n');
394 if (p >= 0)
395 return comment.substring(0, p);
396 else
397 return comment;
399 return Integer.toString(columnIndex);
403 private void createTree(Composite composite) {
404 tree = new Tree(composite, SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI
405 | SWT.FULL_SELECTION | SWT.VIRTUAL);
406 tree.setHeaderVisible(true);
407 tree.setLinesVisible(true);
408 GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
409 tree.setLayoutData(data);
410 tree.setData("HEAD");
411 tree.addListener(SWT.SetData, new Listener() {
412 public void handleEvent(Event event) {
413 try {
414 TreeItem item = (TreeItem) event.item;
415 Tree parent = item.getParent();
416 if (parent == null) {
417 item.setText(new String[] { "hej", "san" });
418 item.setData("");
419 } else {
420 ITableLabelProvider p = (ITableLabelProvider) viewer
421 .getLabelProvider();
422 for (int i = 0; i < 6; ++i) {
423 String text = p.getColumnText(
424 fileRevisions[event.index], i);
425 if (text != null)
426 item.setText(i, text);
427 else
428 item.setText(i, "");
430 item.setData(fileRevisions[event.index]);
432 } catch (Throwable b) {
433 b.printStackTrace();
437 TableLayout layout = new TableLayout();
438 tree.setLayout(layout);
440 viewer = new TreeViewer(tree, SWT.VIRTUAL | SWT.FULL_SELECTION);
442 viewer.setUseHashlookup(true);
444 createColumns();
446 viewer.setLabelProvider(new GitHistoryLabelProvider());
448 viewer.setContentProvider(new GitHistoryContentProvider());
450 viewer.setInput(getInput());
453 private Map appliedPatches;
454 private Map<ObjectId,Tag[]> tags;
455 private Map<ObjectId, String[]> branches;
457 class HistoryRefreshJob extends Job {
459 public HistoryRefreshJob(String name) {
460 super(name);
463 protected IStatus run(IProgressMonitor monitor) {
464 monitor = new NullProgressMonitor();
465 monitor.beginTask("UpdateHistory", IProgressMonitor.UNKNOWN);
466 IProject project = ((IResource) getInput()).getProject();
467 RepositoryProvider provider = RepositoryProvider
468 .getProvider(project);
469 RepositoryMapping repositoryMapping = ((GitProvider)provider).getData().getRepositoryMapping(project);
470 Map newappliedPatches = null;
471 try {
472 newappliedPatches = repositoryMapping.getRepository().getAppliedPatches();
473 } catch (IOException e) {
474 // TODO Auto-generated catch block
475 e.printStackTrace();
477 Map<ObjectId,Tag[]> newtags = new HashMap<ObjectId,Tag[]>();
478 try {
479 for (String name : repositoryMapping.getRepository().getTags()) {
480 Tag t = repositoryMapping.getRepository().mapTag(name);
481 Tag[] samecommit = newtags.get(t.getObjId());
482 if (samecommit==null) {
483 samecommit = new Tag[] { t };
484 } else {
485 Tag[] n=new Tag[samecommit.length+1];
486 for (int j=0; j<samecommit.length; ++j)
487 n[j] = samecommit[j];
488 n[n.length-1] = t;
489 samecommit = n;
491 newtags.put(t.getObjId(), samecommit);
493 } catch (IOException e) {
494 // TODO Auto-generated catch block
495 e.printStackTrace();
497 Map<ObjectId, String[]> newBranches = new HashMap<ObjectId, String[]>();
498 try {
499 for (String branch : repositoryMapping.getRepository().getBranches()) {
500 ObjectId id = repositoryMapping.getRepository().resolve("refs/heads/"+branch);
501 String[] samecommit = newBranches.get(id);
502 if (samecommit == null) {
503 samecommit = new String[] { branch };
504 } else {
505 String[] n=new String[samecommit.length + 1];
506 for (int j=0; j<samecommit.length; ++j)
507 n[j] = samecommit[j];
508 n[n.length-1] = branch;
509 samecommit = n;
511 newBranches.put(id, samecommit);
513 branches = newBranches;
514 } catch (IOException e) {
515 // TODO Auto-generated catch block
516 e.printStackTrace();
519 IFileHistoryProvider fileHistoryProvider = provider
520 .getFileHistoryProvider();
521 IResource startingPoint = (IResource) getInput();
522 if (isShowAllFolderVersions())
523 if (!(startingPoint instanceof IContainer))
524 startingPoint = startingPoint.getParent();
525 if (isShowAllVersions())
526 startingPoint = startingPoint.getProject();
527 IFileHistory fileHistoryFor = fileHistoryProvider
528 .getFileHistoryFor(startingPoint,
529 IFileHistoryProvider.SINGLE_LINE_OF_DESCENT,
530 monitor);
531 fileRevisions = fileHistoryFor.getFileRevisions();
533 final Map fnewappliedPatches = newappliedPatches;
534 final Map<ObjectId,Tag[]> ftags = newtags;
536 tree.getDisplay().asyncExec(new Runnable() {
538 public void run() {
539 tree.removeAll();
540 tree.setItemCount(fileRevisions.length);
541 tree.setData(fileRevisions);
542 tree.setLayoutData(new GridData(SWT.FILL,SWT.FILL,true,true));
543 System.out.println("inputchanged, invoking refresh");
544 appliedPatches = fnewappliedPatches;
545 tags = ftags;
546 viewer.refresh();
547 done(Status.OK_STATUS);
551 return Status.OK_STATUS;
556 HistoryRefreshJob historyRefreshJob = new HistoryRefreshJob("Git history refresh");
558 class GitHistoryContentProvider implements ITreeContentProvider,
559 ILazyTreeContentProvider {
561 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
562 if (newInput == null)
563 return;
564 System.out.println(new Date()+"inputChanged(" + viewer + "," + oldInput + ","
565 + newInput);
566 if (historyRefreshJob.cancel()) {
567 System.out.println("rescheduling");
568 historyRefreshJob.schedule();
569 } else {
570 System.out.println("failed to cancel?");
574 public void dispose() {
577 public Object[] getElements(Object inputElement) {
578 System.out.println("getElements(" + inputElement + ")");
579 return fileRevisions;
582 public boolean hasChildren(Object element) {
583 // TODO Auto-generated method stub
584 return false;
587 public Object getParent(Object element) {
588 // System.out.println("getParent(" + element + ")");
589 return null;
592 public Object[] getChildren(Object parentElement) {
593 // System.out.println("getElements(" + parentElement + ")");
594 return fileRevisions;
597 public void updateChildCount(Object element, int currentChildCount) {
598 viewer.setChildCount(element, fileRevisions!=null ? fileRevisions.length : 0);
601 public void updateElement(Object parent, int index) {
602 System.out.println("updateElement("+parent+","+index);
603 viewer.replace(parent, index, fileRevisions!=null ? fileRevisions[index] : null);
607 private void createColumns() {
608 // X SelectionListener headerListener = getColumnListener(viewer);
609 // count from head
610 TreeColumn col = new TreeColumn(tree, SWT.NONE);
611 col.setResizable(true);
612 col.setText("^");
613 // X col.addSelectionListener(headerListener);
614 ((TableLayout) tree.getLayout()).addColumnData(new ColumnWeightData(15,
615 true));
617 // revision
618 col = new TreeColumn(tree, SWT.NONE);
619 col.setResizable(true);
620 col.setText(TeamUIMessages.GenericHistoryTableProvider_Revision);
621 // X col.addSelectionListener(headerListener);
622 ((TableLayout) tree.getLayout()).addColumnData(new ColumnWeightData(15,
623 true));
625 // tags
626 col = new TreeColumn(tree, SWT.NONE);
627 col.setResizable(true);
628 col.setText("Tags");
629 // X col.addSelectionListener(headerListener);
630 ((TableLayout) tree.getLayout()).addColumnData(new ColumnWeightData(15,
631 true));
632 // creation date
633 col = new TreeColumn(tree, SWT.NONE);
634 col.setResizable(true);
635 col.setText(TeamUIMessages.GenericHistoryTableProvider_RevisionTime);
636 // X col.addSelectionListener(headerListener);
637 ((TableLayout) tree.getLayout()).addColumnData(new ColumnWeightData(30,
638 true));
640 // author
641 col = new TreeColumn(tree, SWT.NONE);
642 col.setResizable(true);
643 col.setText(TeamUIMessages.GenericHistoryTableProvider_Author);
644 // X col.addSelectionListener(headerListener);
645 ((TableLayout) tree.getLayout()).addColumnData(new ColumnWeightData(20,
646 true));
648 // comment
649 col = new TreeColumn(tree, SWT.NONE);
650 col.setResizable(true);
651 col.setText(TeamUIMessages.GenericHistoryTableProvider_Comment);
652 // X col.addSelectionListener(headerListener);
653 ((TableLayout) tree.getLayout()).addColumnData(new ColumnWeightData(35,
654 true));
657 public Control getControl() {
658 return localComposite;
661 public void setFocus() {
662 localComposite.setFocus();
665 public String getDescription() {
666 return "GIT History viewer";
669 public String getName() {
670 return getInput().toString();
673 public boolean isValidInput(Object object) {
674 // TODO Auto-generated method stub
675 return true;
678 public void refresh() {
679 // TODO Auto-generated method stub
683 public Object getAdapter(Class adapter) {
684 if (adapter == IHistoryCompareAdapter.class) {
685 return this;
687 return null;
690 public ICompareInput getCompareInput(Object object) {
691 // TODO Auto-generated method stub
692 return null;
695 public void prepareInput(ICompareInput input,
696 CompareConfiguration configuration, IProgressMonitor monitor) {
697 System.out.println("prepareInput()");
698 // TODO Auto-generated method stub