TMF: added support for process filter in the TimeGraphFilterDialog.
authorGeneroso Pagano <generoso.pagano@gmail.com>
Wed, 27 Aug 2014 13:31:10 +0000 (15:31 +0200)
committerPatrick Tasse <patrick.tasse@gmail.com>
Mon, 8 Sep 2014 19:45:56 +0000 (15:45 -0400)
On top of the filter dialog a text field enables filtering over the
tree of process names.

A node of the tree is shown if:
- the node matches with the search pattern
- one of the children of the node matches with the search pattern
- one of the parents of the node matches with the search pattern

The buttons check-all and check-subtree check only the visible items.
The button uncheck-subtree unchecks all the children (the hidden
children too, since it makes no sense a checked child with an unchecked
parent).
The button uncheck-all unchecks everything, except the hidden roots
(and corresponding subtrees).

Change-Id: I517f3333bb7b6da9fd14eaaac90e7b914671479e
Signed-off-by: Generoso Pagano <generoso.pagano@gmail.com>
Reviewed-on: https://git.eclipse.org/r/32404
Tested-by: Hudson CI
Reviewed-by: Patrick Tasse <patrick.tasse@gmail.com>
Tested-by: Patrick Tasse <patrick.tasse@gmail.com>
org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/views/timegraph/AbstractTimeGraphView.java
org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/widgets/timegraph/TimeGraphCombo.java
org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/widgets/timegraph/dialogs/FilteredCheckboxTree.java [new file with mode: 0644]
org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/widgets/timegraph/dialogs/TimeGraphFilterDialog.java
org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/widgets/timegraph/dialogs/TreePatternFilter.java [new file with mode: 0644]

index e48dbd5e2fa7ea2ec9d0356de424e0e6e1aa2238..63ea76a77a677c2f140a3a3f0c15193d402d2c8b 100644 (file)
@@ -33,6 +33,7 @@ import org.eclipse.jface.action.IAction;
 import org.eclipse.jface.action.IStatusLineManager;
 import org.eclipse.jface.action.IToolBarManager;
 import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.viewers.ILabelProvider;
 import org.eclipse.jface.viewers.ILabelProviderListener;
 import org.eclipse.jface.viewers.ISelectionProvider;
 import org.eclipse.jface.viewers.ITableLabelProvider;
@@ -389,7 +390,7 @@ public abstract class AbstractTimeGraphView extends TmfView {
      * this class typically need to override the getColumnText method if they
      * have more than one column to display
      */
-    protected static class TreeLabelProvider implements ITableLabelProvider {
+    protected static class TreeLabelProvider implements ITableLabelProvider, ILabelProvider {
 
         @Override
         public void addListener(ILabelProviderListener listener) {
@@ -422,6 +423,23 @@ public abstract class AbstractTimeGraphView extends TmfView {
             return new String();
         }
 
+        /**
+         * @since 3.1
+         */
+        @Override
+        public Image getImage(Object element) {
+            return null;
+        }
+
+        /**
+         * @since 3.1
+         */
+        @Override
+        public String getText(Object element) {
+            TimeGraphEntry entry = (TimeGraphEntry) element;
+            return entry.getName();
+        }
+
     }
 
     private class BuildThread extends Thread {
index ecffa3e8db615544d2ff2472f5c14a297074c7e2..3b772fd33f4b2682d4a7052765aef56a6ce6b48b 100644 (file)
@@ -681,9 +681,9 @@ public class TimeGraphCombo extends Composite {
                 fTimeGraphViewer.refresh();
                 fInhibitTreeSelection = false;
                 alignTreeItems(true);
-                // Reset selection to first entry
+                // Reset selection
                 if (fFilterDialog.getResult().length > 0) {
-                    setSelection((ITimeGraphEntry) fFilterDialog.getResult()[0]);
+                    setSelection(null);
                 }
             }
         }
diff --git a/org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/widgets/timegraph/dialogs/FilteredCheckboxTree.java b/org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/widgets/timegraph/dialogs/FilteredCheckboxTree.java
new file mode 100644 (file)
index 0000000..23c097a
--- /dev/null
@@ -0,0 +1,193 @@
+/*******************************************************************************
+ * Copyright (c) 2014 Inria
+ *
+ * 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
+ *
+ * Contributors:
+ *   Generoso Pagano, Inria - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.linuxtools.tmf.ui.widgets.timegraph.dialogs;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.runtime.jobs.IJobChangeEvent;
+import org.eclipse.core.runtime.jobs.JobChangeAdapter;
+import org.eclipse.jface.viewers.CheckStateChangedEvent;
+import org.eclipse.jface.viewers.CheckboxTreeViewer;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.jface.viewers.ICheckable;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.dialogs.FilteredTree;
+import org.eclipse.ui.dialogs.PatternFilter;
+import org.eclipse.ui.progress.WorkbenchJob;
+
+/**
+ * A <code>FilteredTree</code> wrapping a <code>CheckboxTreeViewer</code>.
+ *
+ * This tree keeps the check state of the nodes in sync, regardless of the fact
+ * that a node is filtered or not. This way, even if an node is filtered (not
+ * visible), the caller can get and set the check state.
+ *
+ * Note that all the "uncheck" operations act only on what is not filtered and
+ * what is child of something not filtered (even if such a child is filtered).
+ * On the contrary, all the "check" operations act only on what is not filtered.
+ *
+ * @author "Generoso Pagano <generoso.pagano@inria.fr>"
+ * @since 3.1
+ */
+public class FilteredCheckboxTree extends FilteredTree implements ICheckable {
+
+    /**
+     * Set containing only the tree items that are checked
+     */
+    private Set<Object> fObjects = new HashSet<>();
+
+    /**
+     * Handle to the tree viewer
+     */
+    private CheckboxTreeViewer fCheckboxTreeViewer;
+
+    /**
+     * Create a new instance of the receiver.
+     *
+     * @param parent
+     *            the parent <code>Composite</code>
+     * @param treeStyle
+     *            the style bits for the <code>Tree</code>
+     * @param filter
+     *            the filter to be used
+     * @param useNewLook
+     *            <code>true</code> if the new <code>FilteredTree</code> look
+     *            should be used
+     */
+    public FilteredCheckboxTree(Composite parent, int treeStyle, PatternFilter filter,
+            boolean useNewLook) {
+        super(parent, treeStyle, filter, useNewLook);
+    }
+
+    @Override
+    protected TreeViewer doCreateTreeViewer(Composite parentComposite, int style) {
+        fCheckboxTreeViewer = new CheckboxTreeViewer(parentComposite, style);
+        fCheckboxTreeViewer.addCheckStateListener(new ICheckStateListener() {
+            @Override
+            public void checkStateChanged(CheckStateChangedEvent event) {
+                if (event.getChecked()) {
+                    fObjects.add(event.getElement());
+                } else {
+                    fObjects.remove(event.getElement());
+                }
+            }
+        });
+        return fCheckboxTreeViewer;
+    }
+
+    @Override
+    protected WorkbenchJob doCreateRefreshJob() {
+        WorkbenchJob job = super.doCreateRefreshJob();
+        job.addJobChangeListener(new JobChangeAdapter() {
+            @Override
+            public void done(IJobChangeEvent event) {
+                fCheckboxTreeViewer.expandAll();
+                fCheckboxTreeViewer.setCheckedElements(getCheckedElements());
+            }
+        });
+        return job;
+    }
+
+    @Override
+    public boolean getChecked(Object element) {
+        return fObjects.contains(element);
+    }
+
+    @Override
+    public boolean setChecked(Object element, boolean state) {
+        boolean checkable = fCheckboxTreeViewer.setChecked(element, state);
+        if (!state) {
+            fObjects.remove(element);
+        } else if (checkable) {
+            fObjects.add(element);
+        }
+        return checkable;
+    }
+
+    @Override
+    public void addCheckStateListener(ICheckStateListener listener) {
+        fCheckboxTreeViewer.addCheckStateListener(listener);
+    }
+
+    @Override
+    public void removeCheckStateListener(ICheckStateListener listener) {
+        fCheckboxTreeViewer.addCheckStateListener(listener);
+    }
+
+    /**
+     * Returns all the checked elements of this tree, either visible or not.
+     *
+     * @return an array containing all the checked elements
+     */
+    public Object[] getCheckedElements() {
+        return fObjects.toArray();
+    }
+
+    /**
+     * Checks all the passed elements and unchecks all the other.
+     *
+     * @param elements
+     *            the elements to check
+     */
+    public void setCheckedElements(Object[] elements) {
+        fObjects = new HashSet<>();
+        for (Object element : elements) {
+            fObjects.add(element);
+        }
+        fCheckboxTreeViewer.setCheckedElements(elements);
+    }
+
+    /**
+     * Sets the check state for the given element and its children in this
+     * viewer. The unchecked state is always set, while the checked state is set
+     * only on visible elements.
+     *
+     * @param element
+     *            the element
+     * @param state
+     *            the check state to set
+     * @return <code>true</code> if the check state could be set, and
+     *         <code>false</code> otherwise
+     */
+    public boolean setSubtreeChecked(Object element, boolean state) {
+        checkSubtree(element, state);
+        return fCheckboxTreeViewer.setSubtreeChecked(element, state);
+    }
+
+    /**
+     * Recursively sets the check state on an element and its children, using
+     * the politic specified in {@link #setSubtreeChecked(Object, boolean)}
+     * documentation.
+     *
+     * @param element
+     *            the element
+     * @param state
+     *            the check state to set
+     */
+    private void checkSubtree(Object element, boolean state) {
+        if (!state || (fCheckboxTreeViewer.testFindItem(element) != null)) {
+            if (state) {
+                fObjects.add(element);
+            } else {
+                fObjects.remove(element);
+            }
+            for (Object o : ((ITreeContentProvider) fCheckboxTreeViewer.getContentProvider()).getChildren(element)) {
+                checkSubtree(o, state);
+            }
+        }
+    }
+
+}
index b2b1c8d3a8098a118ebf7a5e85e0bc3e8333ed39..76465cd0710690bcbcebb3c2353b8878100ee3bb 100644 (file)
@@ -14,6 +14,7 @@
  *          align the selection buttons to the right
  *      François Rajotte - Support for multiple columns + selection control
  *      Patrick Tasse - Fix Sonar warnings
+ *      Generoso Pagano - Add tree filter
  *******************************************************************************/
 
 package org.eclipse.linuxtools.tmf.ui.widgets.timegraph.dialogs;
@@ -50,12 +51,13 @@ import org.eclipse.swt.widgets.Tree;
 import org.eclipse.swt.widgets.TreeColumn;
 import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.dialogs.ISelectionStatusValidator;
+import org.eclipse.ui.dialogs.PatternFilter;
 import org.eclipse.ui.dialogs.SelectionStatusDialog;
 
 /**
- * Filter dialog for the time graphs
- * This class is derived from the CheckedTreeSelectionDialog
- * It was necessary to develop this similar dialog to allow multiple columns
+ * Filter dialog for the time graphs This class is derived from the
+ * CheckedTreeSelectionDialog It was necessary to develop this similar dialog to
+ * allow multiple columns
  *
  * @version 1.0
  * @since 2.0
@@ -70,7 +72,7 @@ public class TimeGraphFilterDialog extends SelectionStatusDialog {
     private static final int DEFAULT_WIDTH = 60;
     private static final int DEFAULT_HEIGHT = 18;
 
-    private CheckboxTreeViewer fViewer;
+    private FilteredCheckboxTree fTree;
 
     private IBaseLabelProvider fLabelProvider;
 
@@ -205,21 +207,24 @@ public class TimeGraphFilterDialog extends SelectionStatusDialog {
     }
 
     /**
-     * @param contentProvider The content provider for the table
+     * @param contentProvider
+     *            The content provider for the table
      */
     public void setContentProvider(ITreeContentProvider contentProvider) {
         fContentProvider = contentProvider;
     }
 
     /**
-     * @param labelProvider The label provider for the table
+     * @param labelProvider
+     *            The label provider for the table
      */
     public void setLabelProvider(IBaseLabelProvider labelProvider) {
         fLabelProvider = labelProvider;
     }
 
     /**
-     * @param columnNames An array of column names to display
+     * @param columnNames
+     *            An array of column names to display
      */
     public void setColumnNames(String[] columnNames) {
         if (columnNames != null) {
@@ -236,7 +241,7 @@ public class TimeGraphFilterDialog extends SelectionStatusDialog {
     protected void updateOKStatus() {
         if (!fIsEmpty) {
             if (fValidator != null) {
-                fCurrStatus = fValidator.validate(fViewer.getCheckedElements());
+                fCurrStatus = fValidator.validate(fTree.getCheckedElements());
                 updateStatus(fCurrStatus);
             } else if (!fCurrStatus.isOK()) {
                 fCurrStatus = new Status(IStatus.OK, PlatformUI.PLUGIN_ID,
@@ -265,7 +270,7 @@ public class TimeGraphFilterDialog extends SelectionStatusDialog {
 
     @Override
     protected void computeResult() {
-        setResult(Arrays.asList(fViewer.getCheckedElements()));
+        setResult(Arrays.asList(fTree.getCheckedElements()));
     }
 
     @Override
@@ -274,10 +279,10 @@ public class TimeGraphFilterDialog extends SelectionStatusDialog {
             @Override
             public void run() {
                 TimeGraphFilterDialog.super.create();
-                fViewer.setCheckedElements(getInitialElementSelections()
+                fTree.setCheckedElements(getInitialElementSelections()
                         .toArray());
                 if (fExpandedElements != null) {
-                    fViewer.setExpandedElements(fExpandedElements);
+                    fTree.getViewer().setExpandedElements(fExpandedElements);
                 }
                 updateOKStatus();
             }
@@ -312,9 +317,11 @@ public class TimeGraphFilterDialog extends SelectionStatusDialog {
      * @return the tree viewer
      */
     protected CheckboxTreeViewer createTreeViewer(Composite parent) {
-        fViewer = new CheckboxTreeViewer(parent, SWT.BORDER | SWT.MULTI);
+        PatternFilter filter = new TreePatternFilter();
+        filter.setIncludeLeadingWildcard(true);
+        fTree = new FilteredCheckboxTree(parent, SWT.BORDER | SWT.MULTI, filter, true);
 
-        Tree tree = fViewer.getTree();
+        Tree tree = fTree.getViewer().getTree();
         tree.setHeaderVisible(true);
         for (String columnName : fColumnNames) {
             TreeColumn column = new TreeColumn(tree, SWT.LEFT);
@@ -322,28 +329,22 @@ public class TimeGraphFilterDialog extends SelectionStatusDialog {
             column.pack();
         }
 
-        fViewer.setContentProvider(fContentProvider);
-        fViewer.setLabelProvider(fLabelProvider);
-        fViewer.addCheckStateListener(new CheckStateListener());
-        fViewer.addCheckStateListener(new ICheckStateListener() {
-            @Override
-            public void checkStateChanged(CheckStateChangedEvent event) {
-                updateOKStatus();
-            }
-        });
-        fViewer.setComparator(fComparator);
+        fTree.getViewer().setContentProvider(fContentProvider);
+        fTree.getViewer().setLabelProvider(fLabelProvider);
+        fTree.addCheckStateListener(new CheckStateListener());
+        fTree.getViewer().setComparator(fComparator);
         if (fFilters != null) {
             for (int i = 0; i != fFilters.size(); i++) {
-                fViewer.addFilter(fFilters.get(i));
+                fTree.getViewer().addFilter(fFilters.get(i));
             }
         }
-        fViewer.setInput(fInput);
+        fTree.getViewer().setInput(fInput);
 
-        //pack the columns again for a nice view...
+        // pack the columns again for a nice view...
         for (TreeColumn column : tree.getColumns()) {
             column.pack();
         }
-        return fViewer;
+        return (CheckboxTreeViewer) fTree.getViewer();
     }
 
     /**
@@ -352,7 +353,7 @@ public class TimeGraphFilterDialog extends SelectionStatusDialog {
      * @return the tree viewer
      */
     protected CheckboxTreeViewer getTreeViewer() {
-        return fViewer;
+        return (CheckboxTreeViewer) fTree.getViewer();
     }
 
     /**
@@ -395,7 +396,6 @@ public class TimeGraphFilterDialog extends SelectionStatusDialog {
                 IDialogConstants.DESELECT_ALL_ID, Messages.TmfTimeFilterDialog_UNCHECK_ALL,
                 false);
 
-
         /*
          * Apply the layout again after creating the buttons to override
          * createButton messing with the columns
@@ -407,7 +407,7 @@ public class TimeGraphFilterDialog extends SelectionStatusDialog {
         checkSelectedButton.addSelectionListener(new SelectionAdapter() {
             @Override
             public void widgetSelected(SelectionEvent e) {
-                TreeSelection selection = (TreeSelection) fViewer.getSelection();
+                TreeSelection selection = (TreeSelection) fTree.getViewer().getSelection();
 
                 for (Object element : selection.toArray()) {
                     checkElement(element);
@@ -420,7 +420,7 @@ public class TimeGraphFilterDialog extends SelectionStatusDialog {
         checkSubtreeButton.addSelectionListener(new SelectionAdapter() {
             @Override
             public void widgetSelected(SelectionEvent e) {
-                TreeSelection selection = (TreeSelection) fViewer.getSelection();
+                TreeSelection selection = (TreeSelection) fTree.getViewer().getSelection();
 
                 for (Object element : selection.toArray()) {
                     checkElementAndSubtree(element);
@@ -434,7 +434,7 @@ public class TimeGraphFilterDialog extends SelectionStatusDialog {
                 Object[] viewerElements = fContentProvider.getElements(fInput);
 
                 for (int i = 0; i < viewerElements.length; i++) {
-                    fViewer.setSubtreeChecked(viewerElements[i], true);
+                    fTree.setSubtreeChecked(viewerElements[i], true);
                 }
 
                 updateOKStatus();
@@ -444,7 +444,7 @@ public class TimeGraphFilterDialog extends SelectionStatusDialog {
         uncheckSelectedButton.addSelectionListener(new SelectionAdapter() {
             @Override
             public void widgetSelected(SelectionEvent e) {
-                TreeSelection selection = (TreeSelection) fViewer.getSelection();
+                TreeSelection selection = (TreeSelection) fTree.getViewer().getSelection();
 
                 for (Object element : selection.toArray()) {
                     uncheckElement(element);
@@ -457,7 +457,7 @@ public class TimeGraphFilterDialog extends SelectionStatusDialog {
         uncheckSubtreeButton.addSelectionListener(new SelectionAdapter() {
             @Override
             public void widgetSelected(SelectionEvent e) {
-                TreeSelection selection = (TreeSelection) fViewer.getSelection();
+                TreeSelection selection = (TreeSelection) fTree.getViewer().getSelection();
 
                 for (Object element : selection.toArray()) {
                     uncheckElement(element);
@@ -470,7 +470,13 @@ public class TimeGraphFilterDialog extends SelectionStatusDialog {
         uncheckAllButton.addSelectionListener(new SelectionAdapter() {
             @Override
             public void widgetSelected(SelectionEvent e) {
-                fViewer.setCheckedElements(new Object[0]);
+                Object[] viewerElements = fContentProvider.getElements(fInput);
+                for (Object element : viewerElements) {
+                    if (fTree.getViewer().testFindItem(element) != null) {
+                        // uncheck only visible roots and their children
+                        uncheckElement(element);
+                    }
+                }
                 updateOKStatus();
             }
         });
@@ -485,11 +491,11 @@ public class TimeGraphFilterDialog extends SelectionStatusDialog {
      *            The element to check.
      */
     private void checkElement(Object element) {
-        fViewer.setChecked(element, true);
+        fTree.setChecked(element, true);
 
         Object parent = fContentProvider.getParent(element);
 
-        if (parent != null) {
+        if (parent != null && !fTree.getChecked(parent)) {
             checkElement(parent);
         }
     }
@@ -515,7 +521,7 @@ public class TimeGraphFilterDialog extends SelectionStatusDialog {
      *            The element to uncheck.
      */
     private void uncheckElement(Object element) {
-        fViewer.setChecked(element, false);
+        fTree.setChecked(element, false);
 
         for (Object child : fContentProvider.getChildren(element)) {
             uncheckElement(child);
@@ -527,7 +533,7 @@ public class TimeGraphFilterDialog extends SelectionStatusDialog {
         if (elements.length > 0 && fFilters != null) {
             for (int i = 0; i < fFilters.size(); i++) {
                 ViewerFilter curr = fFilters.get(i);
-                elements = curr.filter(fViewer, input, elements);
+                elements = curr.filter(fTree.getViewer(), input, elements);
             }
         }
         return elements.length == 0;
@@ -554,7 +560,10 @@ public class TimeGraphFilterDialog extends SelectionStatusDialog {
                 }
             } catch (ClassCastException e) {
                 return;
+            } finally {
+                updateOKStatus();
             }
         }
+
     }
-}
+}
\ No newline at end of file
diff --git a/org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/widgets/timegraph/dialogs/TreePatternFilter.java b/org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/widgets/timegraph/dialogs/TreePatternFilter.java
new file mode 100644 (file)
index 0000000..14277eb
--- /dev/null
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2014 Inria
+ *
+ * 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
+ *
+ * Contributors:
+ *   Generoso Pagano, Inria - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.linuxtools.tmf.ui.widgets.timegraph.dialogs;
+
+import org.eclipse.jface.viewers.AbstractTreeViewer;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.ui.dialogs.PatternFilter;
+
+/**
+ * A filter extending the <code>org.eclipse.ui.dialogs.PatternFilter<code>.
+ *
+ * It redefines the {@link #isElementVisible(Viewer, Object)}} method in order
+ * to have a match on a node if: the node matches or one of the children matches
+ * or one of the parents matches.
+ *
+ * @author "Generoso Pagano <generoso.pagano@inria.fr>"
+ * @since 3.1
+ */
+public class TreePatternFilter extends PatternFilter {
+
+    @Override
+    public boolean isElementVisible(Viewer viewer, Object element) {
+        return super.isElementVisible(viewer, element) || isChildMatch(viewer, element);
+    }
+
+    /**
+     * Check if at least one of the parents of this element is a match with the
+     * filter text.
+     *
+     * @param viewer
+     *            the viewer that contains the element
+     * @param element
+     *            the tree element to check
+     * @return true if the given element has a parent that matches the filter
+     *         text
+     */
+    private boolean isChildMatch(Viewer viewer, Object element) {
+        Object parent = ((ITreeContentProvider) ((AbstractTreeViewer) viewer).getContentProvider())
+                .getParent(element);
+        while (parent != null) {
+            if (isLeafMatch(viewer, parent)) {
+                return true;
+            }
+            parent = ((ITreeContentProvider) ((AbstractTreeViewer) viewer).getContentProvider())
+                    .getParent(parent);
+        }
+        return false;
+    }
+
+}
This page took 0.037974 seconds and 5 git commands to generate.