├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── MANIFEST.in ├── README.md ├── doc ├── command.bat ├── commands.txt ├── sample.dot ├── sample.dot.svg ├── sample_annotated_tag.dot ├── sample_annotated_tag.dot.svg ├── sample_blob.dot ├── sample_blob.dot.svg ├── sample_commit.dot ├── sample_commit.dot.svg ├── sample_full.dot ├── sample_full.dot.svg ├── sample_local_branch.dot ├── sample_local_branch.dot.svg ├── sample_local_head.dot ├── sample_local_head.dot.svg ├── sample_remote_branch.dot ├── sample_remote_branch.dot.svg ├── sample_remote_head.dot ├── sample_remote_head.dot.svg ├── sample_remote_server.dot ├── sample_remote_server.dot.svg ├── sample_tag.dot ├── sample_tag.dot.svg ├── sample_tree.dot ├── sample_tree.dot.svg ├── sample_upstream.dot └── sample_upstream.dot.svg ├── git_graph ├── __init__.py ├── cli.py ├── dot_graph.py ├── git_functions.py └── git_graph_class.py ├── requirements.txt ├── setup.py └── tests ├── data ├── .gitattributes ├── full_repo │ ├── .gitGraph │ │ └── expected.dot │ ├── .gitZ │ │ ├── COMMIT_EDITMSG │ │ ├── HEAD │ │ ├── ORIG_HEAD │ │ ├── config │ │ ├── description │ │ ├── hooks │ │ │ ├── applypatch-msg.sample │ │ │ ├── commit-msg.sample │ │ │ ├── post-update.sample │ │ │ ├── pre-applypatch.sample │ │ │ ├── pre-commit.sample │ │ │ ├── pre-push.sample │ │ │ ├── pre-rebase.sample │ │ │ ├── pre-receive.sample │ │ │ ├── prepare-commit-msg.sample │ │ │ └── update.sample │ │ ├── index │ │ ├── info │ │ │ └── exclude │ │ ├── logs │ │ │ ├── HEAD │ │ │ └── refs │ │ │ │ └── heads │ │ │ │ ├── idea1 │ │ │ │ ├── idea2 │ │ │ │ └── master │ │ ├── objects │ │ │ ├── 23 │ │ │ │ └── 58d7dee4b70234fb7078d735a835673dc4b45b │ │ │ ├── 34 │ │ │ │ └── 47a63ea27b15856a751c3f631546ad6c98d07a │ │ │ ├── 54 │ │ │ │ └── 378aec6a6ea34638ac687217745e574360285e │ │ │ ├── 73 │ │ │ │ └── 7c972823aec2a30e726cd39821edf8d4b4826b │ │ │ ├── 01 │ │ │ │ └── ccfc8a7031f57c92a694c727fc16a1b6f6a3c8 │ │ │ ├── 02 │ │ │ │ └── 41c07298c9b2bb84793a02418716f302632564 │ │ │ ├── 0c │ │ │ │ └── 213d3ee3c6dd438e02ea8f465548f8dd56288b │ │ │ ├── 6e │ │ │ │ └── 414aa7e6b2bb6f8bd9fcc652ecd7a349547f92 │ │ │ ├── 8e │ │ │ │ └── 26413dad1890d45502dd31a75f9c403eee2fef │ │ │ ├── c9 │ │ │ │ └── 9b3621c6f61f36a468d9da2f2f4cb6c2c7f834 │ │ │ ├── e0 │ │ │ │ └── 4c9e73c27f423fecb1d8220c4a4b5ca217f2de │ │ │ ├── e5 │ │ │ │ └── 3574e083bfb447086df95ad1214d87b6ae45c4 │ │ │ ├── ec │ │ │ │ └── f7586c0df1003ea0a3efa5a00dc3ceaac570f8 │ │ │ └── f9 │ │ │ │ └── f2794a3dae393bdb07affde1719aee32e6c236 │ │ └── refs │ │ │ ├── heads │ │ │ ├── idea1 │ │ │ ├── idea2 │ │ │ └── master │ │ │ └── tags │ │ │ └── v0.0.1 │ ├── dir1 │ │ └── file1.txt │ ├── file2.txt │ └── file3.txt ├── minimal_repo │ ├── .gitGraph │ │ └── expected.dot │ ├── .gitZ │ │ ├── COMMIT_EDITMSG │ │ ├── HEAD │ │ ├── config │ │ ├── description │ │ ├── hooks │ │ │ ├── applypatch-msg.sample │ │ │ ├── commit-msg.sample │ │ │ ├── post-update.sample │ │ │ ├── pre-applypatch.sample │ │ │ ├── pre-commit.sample │ │ │ ├── pre-push.sample │ │ │ ├── pre-rebase.sample │ │ │ ├── pre-receive.sample │ │ │ ├── prepare-commit-msg.sample │ │ │ └── update.sample │ │ ├── index │ │ ├── info │ │ │ └── exclude │ │ ├── logs │ │ │ ├── HEAD │ │ │ └── refs │ │ │ │ └── heads │ │ │ │ └── master │ │ ├── objects │ │ │ ├── 54 │ │ │ │ └── 378aec6a6ea34638ac687217745e574360285e │ │ │ ├── 73 │ │ │ │ └── 7c972823aec2a30e726cd39821edf8d4b4826b │ │ │ ├── 6e │ │ │ │ └── 414aa7e6b2bb6f8bd9fcc652ecd7a349547f92 │ │ │ ├── e5 │ │ │ │ └── 3574e083bfb447086df95ad1214d87b6ae45c4 │ │ │ └── ec │ │ │ │ └── f7586c0df1003ea0a3efa5a00dc3ceaac570f8 │ │ └── refs │ │ │ └── heads │ │ │ └── master │ ├── dir1 │ │ └── file1.txt │ └── file2.txt └── remote_repo │ ├── .gitGraph │ └── expected.dot │ ├── .gitZ │ ├── HEAD │ ├── config │ ├── description │ ├── hooks │ │ ├── applypatch-msg.sample │ │ ├── commit-msg.sample │ │ ├── post-update.sample │ │ ├── pre-applypatch.sample │ │ ├── pre-commit.sample │ │ ├── pre-push.sample │ │ ├── pre-rebase.sample │ │ ├── pre-receive.sample │ │ ├── prepare-commit-msg.sample │ │ └── update.sample │ ├── index │ ├── info │ │ └── exclude │ ├── logs │ │ ├── HEAD │ │ └── refs │ │ │ ├── heads │ │ │ ├── idea1 │ │ │ ├── idea2 │ │ │ └── master │ │ │ └── remotes │ │ │ └── origin │ │ │ └── HEAD │ ├── objects │ │ ├── 23 │ │ │ └── 58d7dee4b70234fb7078d735a835673dc4b45b │ │ ├── 34 │ │ │ └── 47a63ea27b15856a751c3f631546ad6c98d07a │ │ ├── 54 │ │ │ └── 378aec6a6ea34638ac687217745e574360285e │ │ ├── 73 │ │ │ └── 7c972823aec2a30e726cd39821edf8d4b4826b │ │ ├── 01 │ │ │ └── ccfc8a7031f57c92a694c727fc16a1b6f6a3c8 │ │ ├── 02 │ │ │ └── 41c07298c9b2bb84793a02418716f302632564 │ │ ├── 0c │ │ │ └── 213d3ee3c6dd438e02ea8f465548f8dd56288b │ │ ├── 6e │ │ │ └── 414aa7e6b2bb6f8bd9fcc652ecd7a349547f92 │ │ ├── 8e │ │ │ └── 26413dad1890d45502dd31a75f9c403eee2fef │ │ ├── b7 │ │ │ └── cd4c5e269572826551e2358805684d23e9b187 │ │ ├── c9 │ │ │ └── 9b3621c6f61f36a468d9da2f2f4cb6c2c7f834 │ │ ├── e0 │ │ │ └── 4c9e73c27f423fecb1d8220c4a4b5ca217f2de │ │ ├── e5 │ │ │ └── 3574e083bfb447086df95ad1214d87b6ae45c4 │ │ ├── ec │ │ │ └── f7586c0df1003ea0a3efa5a00dc3ceaac570f8 │ │ └── f9 │ │ │ └── f2794a3dae393bdb07affde1719aee32e6c236 │ ├── packed-refs │ └── refs │ │ ├── heads │ │ ├── idea1 │ │ ├── idea2 │ │ └── master │ │ └── remotes │ │ └── origin │ │ └── HEAD │ ├── dir1 │ └── file1.txt │ ├── file2.txt │ └── file3.txt ├── test_cli.py └── test_dot_graph.py /.gitignore: -------------------------------------------------------------------------------- 1 | /.gitGraph 2 | .idea/ 3 | build/ 4 | dist/ 5 | examples/ 6 | *.egg-info/ 7 | *.pyc 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.6" 4 | - "3.7" 5 | - "3.8" 6 | - "3.8-dev" 7 | - "nightly" 8 | 9 | 10 | addons: 11 | apt: 12 | packages: 13 | - graphviz 14 | - git 15 | 16 | 17 | install: 18 | - sudo apt-get install graphviz 19 | - sudo apt-get install git 20 | - python setup.py install 21 | 22 | 23 | script: 24 | - flake8 25 | - pytest -vv -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Henri-Olivier Duché 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE.txt 2 | include README.md 3 | include requirements.txt 4 | include doc/*.svg 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Git-graph 2 | 3 | ### Learn (or teach) Git fast and well - *by visualizing the inner graph of your Git repositories* 4 | ___ 5 | 6 | ![full](doc/sample_full.dot.svg) 7 | 8 | > [Git is a fast, scalable, distributed revision control system with an unusually rich command set 9 | that provides both high-level operations and full access to internals.](https://git-scm.com/docs/git) 10 | 11 | As wonderful as it may be, there is a downside coming with this "unusually rich command set", a kind of anxiety that affects beginners in particular and can be summed up in one question: 12 | > "What the hell is going to happen to my repository if I launch this Git command ?" 13 | 14 | A good way to overcome this difficulty is to experiment. 15 | This is made easy thanks to Git lightness and the fact it is immediately up and running in any directory with `git init`. 16 | 17 | Git-graph is a Git plugin, written in Python, that displays your Git repository inner content as a [Directed Acyclic Graph](https://en.wikipedia.org/wiki/Directed_acyclic_graph) (DAG). 18 | This structured visual representation of Git internal data demystifies the impact of each Git command and considerably improves the learning curve. 19 | 20 | ## Install 21 | 22 | #### From PyPI 23 | To install Git-graph from PyPI: 24 | 1. You first need to install [Graphviz](https://www.graphviz.org/download/) and check that the dot binary is correctly set in you system's path. 25 | 2. Then run: 26 | ``` 27 | pip install git-graph 28 | ``` 29 | 30 | #### From GitHub 31 | To install Git-graph from GitHub: 32 | 1. You first need to install [Graphviz](https://www.graphviz.org/download/) and check that the dot binary is correctly set in you system's path. 33 | 2. Then run: 34 | ``` 35 | git clone https://github.com/hoduche/git-graph 36 | ``` 37 | 3. Finally, inside the newly created git-graph folder, run (with Python 3 and setuptools): 38 | ``` 39 | python setup.py install 40 | ``` 41 | 42 | ## Run 43 | 44 | #### As a Git plugin 45 | Git-graph is a Git plugin that is run from a Git repository with the command: 46 | ``` 47 | git graph 48 | ``` 49 | 50 | Running `git graph` from a Git repository will: 51 | 1. scan your `.git` folder 52 | 2. build and save a graph representation of the `.git` folder internals as text (`.dot`) and image (PDF by default) in a `.gitGraph` folder 53 | 3. popup a window that displays the image of your graph 54 | 55 | A color code helps in distinguishing in the graph the different kinds of object Git is using in its implementation: 56 | 57 | | Object kind | Letter | Representation | Object kind | Letter | Representation | 58 | | -------------- | :----: | -------------------------------------------------- | -------------- | :----: | -------------------------------------------------- | 59 | | blob | b | ![blob](doc/sample_blob.dot.svg) | remote branch | r | ![remote_branch](doc/sample_remote_branch.dot.svg) | 60 | | tree | t | ![tree](doc/sample_tree.dot.svg) | remote head | d | ![remote_head](doc/sample_remote_head.dot.svg) | 61 | | commit | c | ![commit](doc/sample_commit.dot.svg) | remote server | s | ![remote_server](doc/sample_remote_server.dot.svg) | 62 | | local branch | l | ![local_branch](doc/sample_local_branch.dot.svg) | annotated tag | a | ![annotated_tag](doc/sample_annotated_tag.dot.svg) | 63 | | local head | h | ![local_head](doc/sample_local_head.dot.svg) | tag | g | ![tag](doc/sample_tag.dot.svg) | 64 | | upstream link | u | ![upstream](doc/sample_upstream.dot.svg) | 65 | 66 | By default all nodes are displayed in the output graph when running `git graph`. 67 | It is possible to only display a user selection of object kinds using the `-n` or `--nodes` option and picking the letters corresponding to your choice. 68 | For instance to only display blobs, trees and commits: 69 | ``` 70 | git graph -n btc 71 | ``` 72 | 73 | By default Git-graph considers it is launched from a Git repository. 74 | It is possible to indicate the path to another Git repository with the `-p` or `--path` option: 75 | ``` 76 | git graph -p examples/demo 77 | ``` 78 | 79 | The default output format is PDF. 80 | Other output graphics formats (either vector or raster) can be set with the `-f` or `--format` option: 81 | (the full list of possible formats can be found on the [Graphviz documentation website](https://graphviz.gitlab.io/_pages/doc/info/output.html)) 82 | ``` 83 | git graph -f svg 84 | ``` 85 | 86 | Finally it is possible to prevent the graph image from poping up once constructed, with the `-c` or `--conceal` option: 87 | ``` 88 | git graph -c 89 | ``` 90 | 91 | #### As a Python program 92 | ``` 93 | python git_graph/cli.py -p examples/demo -n btc -f svg 94 | ``` 95 | or 96 | ``` 97 | ./git_graph/cli.py -p examples/demo -n btc -f svg 98 | ``` 99 | 100 | #### As a Python module 101 | 102 | ```python 103 | import git_graph.dot_graph as dg 104 | dg.DotGraph('..').persist() 105 | dg.DotGraph('../examples/demo', nodes='btc').persist(form='svg', conceal=True) 106 | ``` 107 | -------------------------------------------------------------------------------- /doc/command.bat: -------------------------------------------------------------------------------- 1 | for /r %%G in (*) do ( 2 | for %%F in (svg) do ( 3 | dot %%G -T%%F -O 4 | ) 5 | ) 6 | -------------------------------------------------------------------------------- /doc/commands.txt: -------------------------------------------------------------------------------- 1 | ------------------------------------- 2 | doc Python Packaging User Guide 3 | ------------------------------------- 4 | https://packaging.python.org/ 5 | https://media.readthedocs.org/pdf/python-packaging-user-guide/latest/python-packaging-user-guide.pdf 6 | 132 pages - 22 jan 2019 7 | 8 | https://python-packaging.readthedocs.io/en/latest/ 9 | https://media.readthedocs.org/pdf/python-packaging/latest/python-packaging.pdf 10 | 29 pages - v0.1 - 20 sep 2017 11 | 12 | ------------------------------------- 13 | doc setuptools 14 | ------------------------------------- 15 | https://setuptools.readthedocs.io/en/latest/ 16 | https://media.readthedocs.org/pdf/setuptools/latest/setuptools.pdf 17 | 203 pages - v40.6.3 - 24 dec 2018 18 | 19 | ------------------------------------- 20 | doc conda 21 | ------------------------------------- 22 | https://docs.conda.io/projects/conda-build/en/latest/index.html 23 | https://readthedocs.com/projects/continuumio-conda-build/downloads/pdf/latest/ 24 | 99 pages - 22 jan 2019 25 | 26 | 27 | 28 | ------------------------------------- 29 | [‎1/‎9/‎2019 5:26 PM] Marc Wouts: 30 | https://mybinder.org/v2/gh/hoduche/git-graph/master 31 | 32 | [‎1/‎18/‎2019 3:56 PM] Henri-Olivier Duche: 33 | Salut Marc. 34 | Je travaille sur le packaging de mon application git-graph (en python). 35 | Aurais-tu des conseils à me donner 36 | 1. sur la structure du projet (setup.py, requirements, tests ...) 37 | 2. sur les repos sur lesquels je dois livrer des choses (GitHUb, PyPI, conda, medium ...) 38 | 39 | [‎1/‎18/‎2019 3:57 PM] Marc Wouts: 40 | Oui bien sûr ! 41 | 42 | [‎1/‎18/‎2019 3:57 PM] Henri-Olivier Duche: 43 | Cool. 44 | 45 | [‎1/‎18/‎2019 3:57 PM] Marc Wouts: 46 | 1. Il y a beaucoup de doc sur ce sujet 47 | je pense que la première étape 48 | c'est de faire marcher les tests en local 49 | puis de les déployer sur travis-ci 50 | Ainsi, tu auras validé ton setup.py, tes tests, et tes requirements 51 | 52 | [‎1/‎18/‎2019 3:59 PM] Marc Wouts: 53 | 2. Dans l'ordre: github / pypi / medium / conda 54 | 55 | [‎1/‎18/‎2019 3:59 PM] Henri-Olivier Duche: 56 | Parfait :) 57 | 58 | [‎1/‎18/‎2019 3:59 PM] Marc Wouts: 59 | laisse un peu de temps entre chaque etape pour être sur que ça marche ! 60 | Ah, et pense à binder 61 | ce n'est pas mal pour tester le fichier 'requirements' 62 | 63 | [‎1/‎18/‎2019 4:01 PM] Henri-Olivier Duche: 64 | Finalement j'ai penché plutôt vers un plugin git : on tape git graph en ligen de commande et ça crée une image 65 | Je ne sais plus trop si j'ai besoin d'un binder 66 | Je ne crois pas que je vais mettre un notebook dans le projet in fine 67 | 68 | [‎1/‎18/‎2019 4:02 PM] Marc Wouts: 69 | ah pourquoi pas 70 | 71 | [‎1/‎18/‎2019 4:02 PM] Henri-Olivier Duche: 72 | Pour travis-ci, tu as fait des tutoriaux sur leur site ? 73 | 74 | [‎1/‎18/‎2019 4:02 PM] Marc Wouts: 75 | oui c'était assez facile 76 | 77 | [‎1/‎18/‎2019 4:02 PM] Henri-Olivier Duche: 78 | parfait, je vais donc commencer par là ce weekend 79 | 80 | [‎1/‎18/‎2019 4:02 PM] Marc Wouts: 81 | il faut juste écrire un fichier .travis.yml 82 | et bien sûr créer un compte chez travis 83 | 84 | [‎1/‎18/‎2019 4:03 PM] Henri-Olivier Duche: 85 | Et je vais écrire un peu plus de tests car pour l'instant, c'est léger :) 86 | Merci beaucoup en tout cas. 87 | 88 | [‎1/‎18/‎2019 4:04 PM] Marc Wouts: 89 | Jette un coup d'oeil à pytest-cov 90 | moi j'adore 91 | 92 | [‎1/‎18/‎2019 4:05 PM] Henri-Olivier Duche: 93 | Je vais le faire 94 | 95 | [‎1/‎18/‎2019 4:05 PM] Marc Wouts: 96 | https://codecov.io/gh/mwouts/jupytext/tree/1.0.0/jupytext 97 | 98 | [‎1/‎18/‎2019 4:05 PM] Henri-Olivier Duche: 99 | Tu travailles en windows ou en linux chez toi ? 100 | 101 | [‎1/‎18/‎2019 4:05 PM] Marc Wouts: 102 | Clique sur un fichier 103 | et tu vois les lignes non couvertes 104 | en windows 105 | avec la CI en linux 106 | ça fait une bonne couverture 107 | 108 | [‎1/‎18/‎2019 4:06 PM] Henri-Olivier Duche: 109 | la CI en linux sur le site de travis ? 110 | Ils te fournissent une machine virtuelle ? 111 | 112 | [‎1/‎18/‎2019 4:06 PM] Marc Wouts: 113 | Oui, par défaut c'est un environnement linux 114 | il y a des options pour tester d'autres OS 115 | que je n'ai pas étudiées 116 | 117 | [‎1/‎18/‎2019 4:07 PM] Henri-Olivier Duche: 118 | C'est très bien comme ça 119 | Ton code est drolement bien couvert ! 120 | 121 | [‎1/‎18/‎2019 4:07 PM] Marc Wouts: 122 | Il fallait bien ça 123 | 124 | [‎1/‎18/‎2019 4:07 PM] Henri-Olivier Duche: 125 | La page que tu m'as envoyée est générée par travis ? 126 | 127 | [‎1/‎18/‎2019 4:07 PM] Marc Wouts: 128 | N'oublie pas que je suis débutant en python 129 | 130 | [‎1/‎18/‎2019 4:08 PM] Henri-Olivier Duche: 131 | Je suis plus débutant python que toi 132 | 133 | [‎1/‎18/‎2019 4:08 PM] Marc Wouts: 134 | https://github.com/mwouts/jupytext/blob/8d90a28d24ad0045ea6ba0320c96cf4dc7427535/.travis.yml#L26-L29 135 | C'est la commande codecov 136 | qui envoie les infos de coverage à codecov.io 137 | 138 | [‎1/‎18/‎2019 4:09 PM] Henri-Olivier Duche: 139 | OK 140 | 141 | [‎1/‎9/‎2019 5:26 PM] Marc Wouts: 142 | https://mybinder.org/v2/gh/hoduche/git-graph/master 143 | 144 | 145 | 146 | 147 | ------------------------------------- 148 | 149 | [‎1/‎11/‎2019 5:25 PM] Henri-Olivier Duche: 150 | Si je trouve le temps de faire un peu de dev ce weekend, tu me conseillerais qqch pour packager mes dépendances sur GitHub (graphviz et pathlib) ? 151 | 152 | [‎1/‎11/‎2019 5:25 PM] Lamine Souiki: 153 | c'est son setup.py qui doit tirer ça de pip 154 | 155 | [‎1/‎11/‎2019 5:26 PM] Henri-Olivier Duche: 156 | Même graphviz (le programme, pas la lib python) ? 157 | Et ça veut dire que les gens qui font git clone de mon projet doivent faire un pip install qqch après avoir fait git clone ? 158 | 159 | [‎1/‎11/‎2019 5:28 PM] Lamine Souiki: 160 | Ca veut dire que les gens qui clonent ton truc doivent: 161 | faire un sudo apt-get/yum install de graphviz 162 | et faire un pip install . # par exemple. 163 | 164 | [‎1/‎11/‎2019 5:29 PM] Henri-Olivier Duche: 165 | Super merci. 166 | Et faire un binder d'un jupyter notebook, c'est utile aussi ? 167 | 168 | [‎1/‎11/‎2019 5:30 PM] Lamine Souiki: 169 | Ouais, ça peut le faire aussi. Mais c'est plus rare de voir ça. 170 | 171 | [‎1/‎11/‎2019 5:30 PM] Henri-Olivier Duche: 172 | OK l'un n'empêche pas l'autre, je vais commencer par ce que tu dis. 173 | Merci encore et bon weekend 174 | 175 | [‎1/‎24/‎2019 6:38 PM] Lamine Souiki: 176 | pathlib; python_version < '3.0' 177 | meta.yaml: 178 | requirements 179 | build: 180 | - pathlib #[py2k] 181 | 182 | [‎1/‎24/‎2019 6:56 PM] Lamine Souiki: 183 | git-graph renommé en git_graph 184 | dans le requirements.txt: graphviz 185 | dans le entrypoint: 186 | 187 | dans dot-graph.py 188 | import git_graph.git_graph as gg 189 | dans git-graph.py: 190 | from . import git_functions as gf 191 | 192 | [‎1/‎24/‎2019 6:57 PM] Lamine Souiki: 193 | dans le entry-point: 194 | from git_graph.dot_graph import main 195 | 196 | ------------------------------------- 197 | 198 | pathlib is in standard library in python3 but must be imported in python2 and only with pip as there is no conda 199 | graphviz can be imported in pip but python-graphviz must be imported in conda instead 200 | pip environments do not find the dot executable 201 | 202 | ------------------------------------- 203 | graphviz 2.38.0 c'est le vrai dot en C, téléchargeable par conda uniquement (ou apt-get) 204 | graphviz 0.10.1 c'est l'api python, téléchargée par pip 205 | python-graphviz 0.8.4 c'est l'api python, téléchargée par conda 206 | 207 | donc pip install graphviz installe 208 | graphviz 0.10.1 209 | 210 | et conda install graphviz installe 211 | graphviz 2.38.0 hc0b501a_12 cfm_current 212 | 213 | et conda install python-graphviz installe 214 | python-graphviz 0.8.4 py36h07884b1_3 cfm_current 215 | 216 | ------------------------------------- 217 | office + python2 + pip 218 | ------------------------------------- 219 | conda create -n o2p 220 | source activate o2p 221 | conda install pip 222 | pip install graphviz 223 | pip install pathlib 224 | python git-graph/dot_graph.py 225 | the dot file is generated but the pdf is not because 226 | raise ExecutableNotFound(cmd) 227 | graphviz.backend.ExecutableNotFound: failed to execute ['dot', '-Tpdf', '-O', '/home/hduche/workspace/git-graph/.gitGraph/auto.Kk0Jew.dot'], make sure the Graphviz executables are on your systems' PATH 228 | 229 | conda list 230 | # packages in environment at /home/hduche/conda/envs/o2p: 231 | # 232 | graphviz 0.10.1 233 | pathlib 1.0.1 234 | pip 9.0.1 py27hedf312d_1 cfm_current 235 | python 2.7.13 hd4cba35_1 cfm_current 236 | setuptools 36.3.0 py27h2c15a6f_0 cfm_current 237 | wheel 0.29.0 py27h0029886_0 cfm_current 238 | 239 | ------------------------------------- 240 | office + python3 + pip 241 | ------------------------------------- 242 | conda create -n o3p 243 | source activate o3p 244 | conda install python=3 245 | pip install graphviz 246 | python git-graph/dot_graph.py 247 | the dot file is generated but the pdf is not because 248 | raise ExecutableNotFound(cmd) 249 | graphviz.backend.ExecutableNotFound: failed to execute ['dot', '-Tpdf', '-O', '/home/hduche/workspace/git-graph/.gitGraph/auto.jsy9euui.dot'], make sure the Graphviz executables are on your systems' PATH 250 | 251 | conda list 252 | # packages in environment at /home/hduche/conda/envs/o3p: 253 | # 254 | graphviz 0.10.1 255 | libffi 3.0.9 h4823ff6_2 cfm_current 256 | pip 9.0.1 py36h82e8305_1 cfm_current 257 | python 3.6.2 h46af431_3 cfm_current 258 | setuptools 36.3.0 py36hdd866b5_0 cfm_current 259 | wheel 0.29.0 py36heecbd39_0 cfm_current 260 | 261 | ------------------------------------- 262 | office + python2 + conda 263 | ------------------------------------- 264 | conda create -n o2c 265 | source activate o2c 266 | conda install python-graphviz 267 | pip install pathlib 268 | python git-graph/dot_graph.py 269 | ok but not the first time - I had to deactivate then reactivate again to see graphviz module 270 | the order in the dot file is different than in python3 271 | the graph is symetrical with respect to python3 272 | 273 | conda list 274 | # packages in environment at /home/hduche/conda/envs/o2c: 275 | # 276 | expat 2.0.1 ha890ba6_1 cfm_current 277 | graphviz 2.38.0 hc0b501a_12 cfm_current 278 | jpeg 9c h99d5b2f_2 cfm_current 279 | pathlib 1.0.1 280 | pip 9.0.1 py27hedf312d_1 cfm_current 281 | python 2.7.13 hd4cba35_1 cfm_current 282 | python-graphviz 0.8.4 py27hc1e39db_3 cfm_current 283 | setuptools 36.3.0 py27h2c15a6f_0 cfm_current 284 | wheel 0.29.0 py27h0029886_0 cfm_current 285 | 286 | ------------------------------------- 287 | office + python3 + conda 288 | ------------------------------------- 289 | conda create -n o3c 290 | source activate o3c 291 | conda install python=3 292 | conda install python-graphviz 293 | python git-graph/dot_graph.py 294 | ok but not the first time - I had to deactivate then reactivate again to see graphviz module 295 | 296 | conda list 297 | # packages in environment at /home/hduche/conda/envs/o3c: 298 | # 299 | expat 2.0.1 ha890ba6_1 cfm_current 300 | graphviz 2.38.0 hc0b501a_12 cfm_current 301 | jpeg 9c h99d5b2f_2 cfm_current 302 | libffi 3.0.9 h4823ff6_2 cfm_current 303 | pip 9.0.1 py36h82e8305_1 cfm_current 304 | python 3.6.2 h46af431_3 cfm_current 305 | python-graphviz 0.8.4 py36h07884b1_3 cfm_current 306 | setuptools 36.3.0 py36hdd866b5_0 cfm_current 307 | wheel 0.29.0 py36heecbd39_0 cfm_current 308 | 309 | 310 | 311 | 312 | ------------------------------------- 313 | git branch -vv --abbrev=0 314 | ------------------------------------- 315 | list all local branches with their pointed commit SHA1 hash and their upstream branch if any + the local HEAD as '*' 316 | ex: 317 | git branch -vv --abbrev=0 318 | docu 63e89ea8d3cc887dd52fddbd606acd86c1423f9e [origin/docu] add command in README 319 | graph_display 50d0be5c6d16819e1fb296361164b1d130cba258 add unit tests 320 | graph_display2 50d0be5c6d16819e1fb296361164b1d130cba258 add unit tests 321 | * master 50d0be5c6d16819e1fb296361164b1d130cba258 [origin/master] add unit tests 322 | tata f166e7327ee8b6c1a9487a623fe12782938b1f5f [origin/graph_display] improve colors 323 | 324 | ------------------------------------- 325 | git branch -rv --abbrev=0 326 | ------------------------------------- 327 | list all remote-tracking branches with their pointed commit SHA1 hash + the remote HEADs with their pointed branch 328 | ex: 329 | git branch -rv --abbrev=0 330 | origin/HEAD -> origin/master 331 | origin/docu 63e89ea8d3cc887dd52fddbd606acd86c1423f9e add command in README 332 | origin/graph_display f166e7327ee8b6c1a9487a623fe12782938b1f5f improve colors 333 | origin/master 50d0be5c6d16819e1fb296361164b1d130cba258 add unit tests 334 | 335 | ------------------------------------- 336 | git ls-remote origin 337 | ------------------------------------- 338 | list all refs on origin remote server with their pointed commit SHA1 hash 339 | ex: 340 | git ls-remote origin 341 | c1e7aab7ebef15cc628dd317dd3d5134cea7c03f HEAD 342 | 63e89ea8d3cc887dd52fddbd606acd86c1423f9e refs/heads/docu 343 | f166e7327ee8b6c1a9487a623fe12782938b1f5f refs/heads/graph_display 344 | c1e7aab7ebef15cc628dd317dd3d5134cea7c03f refs/heads/master 345 | 9cd0a2357f89f4d149e3a7f3d33b5ea1b816a1e8 refs/heads/simpler_graph 346 | d280d4d5122bdca050ed97bb50a15ba3bb572c3d refs/heads/useful_commands 347 | 601614bc23c653e5d766ed882e62ff67658bd752 refs/tags/v1.1 348 | b31b39a732fe5bf62609f37f0e5ad3d46f1961e7 refs/tags/v1.1^{} 349 | db75702131a2760f5caaedcaa2817482cd7c4d7d refs/tags/v2.1 350 | 1cb5bb694a1f34a33d89a2a5197262cf35fb1814 refs/tags/v2.1^{} 351 | f26629c4f6ae74c7629c580f14dcf559f9c66e93 refs/tags/v2.2 352 | a3a9ad768bcd4c7e136f2c258bea178896bc89da refs/tags/v2.2.2 353 | 354 | ------------------------------------- 355 | git for-each-ref 356 | ------------------------------------- 357 | list all refs with their pointed file SHA1 hash and type 358 | careful the origin/HEAD content is origin/master but it displays the underlying commit 359 | ex: 360 | git for-each-ref 361 | c1e7aab7ebef15cc628dd317dd3d5134cea7c03f commit refs/heads/master 362 | c1e7aab7ebef15cc628dd317dd3d5134cea7c03f commit refs/remotes/origin/HEAD 363 | 63e89ea8d3cc887dd52fddbd606acd86c1423f9e commit refs/remotes/origin/docu 364 | f166e7327ee8b6c1a9487a623fe12782938b1f5f commit refs/remotes/origin/graph_display 365 | c1e7aab7ebef15cc628dd317dd3d5134cea7c03f commit refs/remotes/origin/master 366 | 9cd0a2357f89f4d149e3a7f3d33b5ea1b816a1e8 commit refs/remotes/origin/simpler_graph 367 | d280d4d5122bdca050ed97bb50a15ba3bb572c3d commit refs/remotes/origin/useful_commands 368 | 601614bc23c653e5d766ed882e62ff67658bd752 tag refs/tags/v1.1 369 | db75702131a2760f5caaedcaa2817482cd7c4d7d tag refs/tags/v2.1 370 | f26629c4f6ae74c7629c580f14dcf559f9c66e93 commit refs/tags/v2.2 371 | a3a9ad768bcd4c7e136f2c258bea178896bc89da commit refs/tags/v2.2.2 372 | 373 | ------------------------------------- 374 | git rev-list --parents --all 375 | ------------------------------------- 376 | list all commits relationship: parent childrens (adjacency list - sadly with only commits) 377 | ex: 378 | git rev-list --parents --all 379 | 9cd0a2357f89f4d149e3a7f3d33b5ea1b816a1e8 c1e7aab7ebef15cc628dd317dd3d5134cea7c03f 380 | c1e7aab7ebef15cc628dd317dd3d5134cea7c03f d8f41e82e5acfd8646bc1194d32392690b838b49 381 | d8f41e82e5acfd8646bc1194d32392690b838b49 9301b80aa6365c5fe3b9fc08984d0e9644245bdd d280d4d5122bdca050ed97bb50a15ba3bb572c3d 382 | 9301b80aa6365c5fe3b9fc08984d0e9644245bdd f166e7327ee8b6c1a9487a623fe12782938b1f5f 63e89ea8d3cc887dd52fddbd606acd86c1423f9e 383 | d280d4d5122bdca050ed97bb50a15ba3bb572c3d 143936a65400a372523d5a63327a7f1c2dc87532 384 | f166e7327ee8b6c1a9487a623fe12782938b1f5f 4cb8e2b32b82c88fab0f3ae0a6da906d0fbc727a 385 | 4cb8e2b32b82c88fab0f3ae0a6da906d0fbc727a 8da782a32080c15e7c33170a68b4975c9b7c5176 386 | 8da782a32080c15e7c33170a68b4975c9b7c5176 6b60b20596c99dd37cd2a49aeffa9464d280bea7 387 | 6b60b20596c99dd37cd2a49aeffa9464d280bea7 7fc5b73cc9b16754b41cb0d73d8b14f7471052ce 388 | 7fc5b73cc9b16754b41cb0d73d8b14f7471052ce 50d0be5c6d16819e1fb296361164b1d130cba258 389 | 50d0be5c6d16819e1fb296361164b1d130cba258 143936a65400a372523d5a63327a7f1c2dc87532 390 | 143936a65400a372523d5a63327a7f1c2dc87532 a63a23d7c8223a5da608445d72c4758f7300a469 391 | 63e89ea8d3cc887dd52fddbd606acd86c1423f9e a3a9ad768bcd4c7e136f2c258bea178896bc89da 392 | a3a9ad768bcd4c7e136f2c258bea178896bc89da 3b4592020095d87eae562d2001b7b4434db7aae6 393 | 3b4592020095d87eae562d2001b7b4434db7aae6 f26629c4f6ae74c7629c580f14dcf559f9c66e93 394 | a63a23d7c8223a5da608445d72c4758f7300a469 f26629c4f6ae74c7629c580f14dcf559f9c66e93 395 | f26629c4f6ae74c7629c580f14dcf559f9c66e93 1cb5bb694a1f34a33d89a2a5197262cf35fb1814 396 | 1cb5bb694a1f34a33d89a2a5197262cf35fb1814 07531ec74b54d3814439c45cc5e89cec44c19de1 397 | 07531ec74b54d3814439c45cc5e89cec44c19de1 b31b39a732fe5bf62609f37f0e5ad3d46f1961e7 398 | b31b39a732fe5bf62609f37f0e5ad3d46f1961e7 d344e7f9f097efab654841bf40ca8f996961aa85 399 | d344e7f9f097efab654841bf40ca8f996961aa85 4e15f3baa8e2aeabeedae9a7e5cc9e0a8dabeb88 400 | 4e15f3baa8e2aeabeedae9a7e5cc9e0a8dabeb88 401 | 402 | ------------------------------------- 403 | git rev-list --children --all 404 | ------------------------------------- 405 | list all commits relationship: children parents (inverse adjacency list - sadly with only commits) 406 | ex: 407 | git rev-list --children --all 408 | 9cd0a2357f89f4d149e3a7f3d33b5ea1b816a1e8 409 | c1e7aab7ebef15cc628dd317dd3d5134cea7c03f 9cd0a2357f89f4d149e3a7f3d33b5ea1b816a1e8 410 | d8f41e82e5acfd8646bc1194d32392690b838b49 c1e7aab7ebef15cc628dd317dd3d5134cea7c03f 411 | 9301b80aa6365c5fe3b9fc08984d0e9644245bdd d8f41e82e5acfd8646bc1194d32392690b838b49 412 | d280d4d5122bdca050ed97bb50a15ba3bb572c3d d8f41e82e5acfd8646bc1194d32392690b838b49 413 | f166e7327ee8b6c1a9487a623fe12782938b1f5f 9301b80aa6365c5fe3b9fc08984d0e9644245bdd 414 | 4cb8e2b32b82c88fab0f3ae0a6da906d0fbc727a f166e7327ee8b6c1a9487a623fe12782938b1f5f 415 | 8da782a32080c15e7c33170a68b4975c9b7c5176 4cb8e2b32b82c88fab0f3ae0a6da906d0fbc727a 416 | 6b60b20596c99dd37cd2a49aeffa9464d280bea7 8da782a32080c15e7c33170a68b4975c9b7c5176 417 | 7fc5b73cc9b16754b41cb0d73d8b14f7471052ce 6b60b20596c99dd37cd2a49aeffa9464d280bea7 418 | 50d0be5c6d16819e1fb296361164b1d130cba258 7fc5b73cc9b16754b41cb0d73d8b14f7471052ce 419 | 143936a65400a372523d5a63327a7f1c2dc87532 50d0be5c6d16819e1fb296361164b1d130cba258 d280d4d5122bdca050ed97bb50a15ba3bb572c3d 420 | 63e89ea8d3cc887dd52fddbd606acd86c1423f9e 9301b80aa6365c5fe3b9fc08984d0e9644245bdd 421 | a3a9ad768bcd4c7e136f2c258bea178896bc89da 63e89ea8d3cc887dd52fddbd606acd86c1423f9e 422 | 3b4592020095d87eae562d2001b7b4434db7aae6 a3a9ad768bcd4c7e136f2c258bea178896bc89da 423 | a63a23d7c8223a5da608445d72c4758f7300a469 143936a65400a372523d5a63327a7f1c2dc87532 424 | f26629c4f6ae74c7629c580f14dcf559f9c66e93 a63a23d7c8223a5da608445d72c4758f7300a469 3b4592020095d87eae562d2001b7b4434db7aae6 425 | 1cb5bb694a1f34a33d89a2a5197262cf35fb1814 f26629c4f6ae74c7629c580f14dcf559f9c66e93 426 | 07531ec74b54d3814439c45cc5e89cec44c19de1 1cb5bb694a1f34a33d89a2a5197262cf35fb1814 427 | b31b39a732fe5bf62609f37f0e5ad3d46f1961e7 07531ec74b54d3814439c45cc5e89cec44c19de1 428 | d344e7f9f097efab654841bf40ca8f996961aa85 b31b39a732fe5bf62609f37f0e5ad3d46f1961e7 429 | 4e15f3baa8e2aeabeedae9a7e5cc9e0a8dabeb88 d344e7f9f097efab654841bf40ca8f996961aa85 430 | 431 | ------------------------------------- 432 | git cat-file --batch-check --batch-all-objects 433 | ------------------------------------- 434 | list all objects name, type and size in the repository and any alternate object stores (not just reachable objects) 435 | objects are visited in order sorted by their hashes 436 | from git v2.19, adding --unordered to the command is more efficient: 437 | git cat-file --batch-check --batch-all-objects --unordered 438 | ex: 439 | git cat-file --batch-check --batch-all-objects 440 | 03272ca28be0f469aa5cf5efa33467158a7bd752 tree 215 441 | 06355b6658efeddc28205bd52caa679f41c4e400 tree 93 442 | 064c0919c52a43de880e09d387737ae6a78e0496 tree 379 443 | 072160e569495b8d92cae9a7cfd88ff4c939cf84 blob 9078 444 | 07531ec74b54d3814439c45cc5e89cec44c19de1 commit 260 445 | 082afceb1ed8a58d6eb3af61044b7d950c76d89b tree 181 446 | 082f5febabe49135c653059c3aa12c379774c8f1 blob 1297 447 | 098ec39baccdd661e55d6f193d945f16c82fa434 blob 11946 448 | 0a0db835516f7550a42ff97cb929359b716577f4 blob 390 449 | 0ae8d5b15021ffe48c9c00699a29055dd5b49880 tree 367 450 | 0c5acbe08e8ee5cd0bd00e631fb4a721dc12ef4f blob 6257 451 | 143936a65400a372523d5a63327a7f1c2dc87532 commit 274 452 | 1cb5bb694a1f34a33d89a2a5197262cf35fb1814 commit 260 453 | 1e17eb68697ea0909df63841f213a7a68bc65d6d tree 441 454 | 271404b0eaf8a0d01928d98d8168811d94778fe1 blob 62 455 | 273ab7228976e294b78f969adb26367d33b11516 blob 4468 456 | 2dee652d69d132910c410b76694b94ef9b34f49f blob 1860 457 | 378d9da4a558c1f2f37cfad286435b9000c77fcb tree 215 458 | 38d498e57b11b4a12239cb59ef529130a095f56b blob 1578 459 | 3b4592020095d87eae562d2001b7b4434db7aae6 commit 256 460 | 3e6ddd604fbd4c2f536bf288dd6f91b5d0f919eb blob 11257 461 | 47aa5c1840c9dfe8cdf0b9af3f52fde592f4c401 tree 401 462 | 49d514c9f4b5db7ee13ba925d45d2564d8b9189d blob 1604 463 | 4bb00ff7a9bf59e6205e9c72dbd828f0a71b41e1 blob 3634 464 | 4c6347f61f9ea69d36fe4b7efd8c5749efb022e7 tree 131 465 | 4cb8e2b32b82c88fab0f3ae0a6da906d0fbc727a commit 262 466 | 4d8ab2681e40339ad662b28464d899a327c7e0c9 tree 94 467 | 4ddf57132a0be0b609b96d0333341864a83a0495 blob 1521 468 | 4e15f3baa8e2aeabeedae9a7e5cc9e0a8dabeb88 commit 207 469 | 50d0be5c6d16819e1fb296361164b1d130cba258 commit 245 470 | 601614bc23c653e5d766ed882e62ff67658bd752 tag 163 471 | 616ac4a5f1e21ecda1b1d713dcefd338d247354f tree 367 472 | 63e89ea8d3cc887dd52fddbd606acd86c1423f9e commit 262 473 | 659013e5e3bbd28d019175dee996c48730beaf8a blob 1165 474 | 662242692e43526d9a74d726a7d8be8344e7ecf4 blob 54 475 | 67101d3ca9bdf232e08a7096efb362fffef03661 blob 5967 476 | 699deb908c9339acc22e51ad782e7c0803e16f09 blob 468350 477 | 6af208e080d131c631d6b18a1926d59be762057e blob 71 478 | 6b60b20596c99dd37cd2a49aeffa9464d280bea7 commit 264 479 | 6eb99f80de3389a3c63451ed04ada586f2f9bbe6 blob 340005 480 | 6f4eddb5bc495af77431d7ad572ebfd848de5b1e blob 4978 481 | 73d3772e0c4876ed90136d1345275fcb2b372fc6 tree 181 482 | 7796b932da8fbc85d96cd04633a63eb488ea9d9f blob 1079 483 | 7f07f128f373600b5a719352961da6cf81425432 tree 181 484 | 7fbdc9fc19e8c4afbd1dec969a81d20e34eae4b7 blob 5681 485 | 7fc5b73cc9b16754b41cb0d73d8b14f7471052ce commit 241 486 | 7fc8dfa08410916402af985f8b988f822689f6c6 blob 152385 487 | 8096038d92828e0a8d3ede6230f2dc8904c6fc53 blob 1861 488 | 8306e15c66ec8c8c6e749ad8f797baa2ce7ceee3 blob 3272 489 | 8d17a6d3323b83919bf66a8ab4fb1811b87fa8d6 blob 3312 490 | 8da782a32080c15e7c33170a68b4975c9b7c5176 commit 251 491 | 9301b80aa6365c5fe3b9fc08984d0e9644245bdd commit 308 492 | 947465ff7e5baf876b47461ddb65529e98a67054 tree 319 493 | 98f8cab7aebc0584d291d7be5b19be25a6bb988b tree 215 494 | 9a804fdb86f84a472149cfd3b41477d52a194df8 blob 1308214 495 | 9cd0a2357f89f4d149e3a7f3d33b5ea1b816a1e8 commit 270 496 | 9f7941766b1bab0be1d9e3de5268c1594e2b6bab blob 5376 497 | a3a9ad768bcd4c7e136f2c258bea178896bc89da commit 255 498 | a63a23d7c8223a5da608445d72c4758f7300a469 commit 258 499 | ac188658a60679f49dff5b9127abc3b6a1845212 blob 1401 500 | b31b39a732fe5bf62609f37f0e5ad3d46f1961e7 commit 271 501 | b326c06d5be0279cd956de66626f63e6d0c5bb2b blob 5832 502 | b3f5bfabb5267ea3fabe55acffadfe7041232c98 blob 1856 503 | b4a925c5557e8a667c175c8ef71a312870d87177 blob 66 504 | b7b87d3067d3d188414a5b35c4ef0f5f765194d3 tree 379 505 | c06f761743067ab3e7878a87b59406008700ceb6 blob 4939 506 | c1e7aab7ebef15cc628dd317dd3d5134cea7c03f commit 273 507 | c2e159f81e79043a7eea7b58f79e70936840937d blob 23 508 | c316aaf6ce18318b3f5c97fa6d28907e033a880c blob 836290 509 | c56301d089fbb4d19dcffc31fcaefc762e79328d tree 367 510 | c9b2936ac096730df1629655d04761345f891fda blob 1065 511 | cc20e3905a4a54b7eca67447bf2a37915caef524 blob 1035 512 | cf3661304a83aee233299530910efee8934626dd blob 1308585 513 | d1974a153c1b7ce922808a5ecfedde553225caae blob 3566 514 | d280d4d5122bdca050ed97bb50a15ba3bb572c3d commit 255 515 | d344e7f9f097efab654841bf40ca8f996961aa85 commit 255 516 | d4b4bc9f94e17ebd126d549dc08cd5dfb73456f5 blob 74 517 | d50feab79bd939c5b459910562c79e1e8c3397e8 blob 1627 518 | d7a1f1b19da711490ee929fec7f9066af03cf55d blob 3641 519 | d84c889b48d8872c92de987e8fcfc2461d9d2712 tree 319 520 | d858162fb2ae0591aa55bd7b267234aa4acef1df blob 6366 521 | d8f41e82e5acfd8646bc1194d32392690b838b49 commit 319 522 | da40dca2917b94880d4f00b990c6111a04180c5d blob 224 523 | db75702131a2760f5caaedcaa2817482cd7c4d7d tag 164 524 | dbf2ab1befa87a1f02920f0327f796b1ce9990dc blob 3216 525 | def22f7888caca4a113b137f1a59c31543021490 blob 5615 526 | dfd0ca4bdfe44306babd19b434242acc3f2ef2df blob 30 527 | e09676d2c2c9f395a151704794e412dc18dfb789 blob 303 528 | e21552e6d398011de1642992758c7c83b62fcfcc blob 56 529 | e2f2743f711e28d9ee5f5e408fe16da3ae8e52d1 blob 2614 530 | e336771e7844b22782aa31facdad2f78b56fb7ea tree 181 531 | e621df40dde90ff58d9a527df9ed1c91dda79f3c blob 1121 532 | e75f6ef554e4a4c8d2d591dd3a083e85282530ca tree 367 533 | ea56c17adef257441b0e0259aad6a083d90598fd tree 131 534 | ee5cef3980f9a9377b07b7baeec1c73827d247bb tree 181 535 | eef90f8c88e85d7e4ee27840fd3a171ebec7cceb blob 5211 536 | ef025ec26b833b4e4a81ab0c90d7af88ee0f4c55 blob 11944 537 | f12b13cd935f7388491856b20667fb98f0c7e759 blob 5615 538 | f166e7327ee8b6c1a9487a623fe12782938b1f5f commit 245 539 | f26629c4f6ae74c7629c580f14dcf559f9c66e93 commit 254 540 | fc51948ea18ef517b2a790c61ddd47b550a20281 tree 221 541 | 542 | 543 | 544 | 545 | ------------------------------------- 546 | git merge-base --is-ancestor 547 | 548 | git rev-list --objects --all 549 | git rev-list --branches --all 550 | git rev-list --remotes --all 551 | git rev-list --tags --all 552 | 553 | git rev-list --objects --all | git cat-file --batch-check='%(objectname) %(objecttype) %(rest)' | grep '^[^ ]* blob' | cut -d" " -f1,3- 554 | 555 | 556 | git rev-parse 557 | cat .git/HEAD 558 | git symbolic-ref HEAD -q 559 | 560 | 561 | ------------------------------------- 562 | git rev-parse 563 | ------------------------------------- 564 | on each branch gives the SHA1 hash of the pointed commit 565 | ex: 566 | git rev-parse docu 567 | 63e89ea8d3cc887dd52fddbd606acd86c1423f9e 568 | git rev-parse origin/master 569 | 50d0be5c6d16819e1fb296361164b1d130cba258 570 | 571 | -------------------------------------------------------------------------------- /doc/sample.dot: -------------------------------------------------------------------------------- 1 | digraph auto { 2 | graph [bgcolor=transparent] 3 | node [fixedsize=true style=filled width=0.95] 4 | "03b4fc4666527e20c196b3946a9bf180b67cd36c" [label="03b4fc4" fillcolor="#9ccc66"] 5 | "015997188f3853a8912a9c1cf9cdd0b57cc5ee53" [label=0159971 fillcolor="#bc9b8f"] 6 | "09b9936f5afac451ca98cf7bd874a38d72ab04e7" [label="09b9936" fillcolor="#85d5fa"] 7 | master [label=master fillcolor="#9999ff"] 8 | HEAD [label=HEAD fillcolor="#e6ccff"] 9 | "origin/master" [label=master fillcolor="#ffa366"] 10 | "origin/HEAD" [label=HEAD fillcolor="#ffbeb3"] 11 | origin [label=origin fillcolor="#ff6666"] 12 | "86b176a7d3de1724f728a4e64d2d5d29610af84f" [label="86b176a" fillcolor="#ffdf80"] 13 | "v0.0.1" [label="v0.0.1" fillcolor="#ffc61a"] 14 | } 15 | -------------------------------------------------------------------------------- /doc/sample.dot.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | auto 11 | 12 | 03b4fc4666527e20c196b3946a9bf180b67cd36c 13 | 14 | 03b4fc4 15 | 16 | 17 | 015997188f3853a8912a9c1cf9cdd0b57cc5ee53 18 | 19 | 0159971 20 | 21 | 22 | 09b9936f5afac451ca98cf7bd874a38d72ab04e7 23 | 24 | 09b9936 25 | 26 | 27 | master 28 | 29 | master 30 | 31 | 32 | HEAD 33 | 34 | HEAD 35 | 36 | 37 | origin/master 38 | 39 | master 40 | 41 | 42 | origin/HEAD 43 | 44 | HEAD 45 | 46 | 47 | origin 48 | 49 | origin 50 | 51 | 52 | 86b176a7d3de1724f728a4e64d2d5d29610af84f 53 | 54 | 86b176a 55 | 56 | 57 | v0.0.1 58 | 59 | v0.0.1 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /doc/sample_annotated_tag.dot: -------------------------------------------------------------------------------- 1 | digraph auto { 2 | graph [bgcolor=transparent] 3 | node [fixedsize=true style=filled width=0.95] 4 | "86b176a7d3de1724f728a4e64d2d5d29610af84f" [label="86b176a" fillcolor="#ffdf80"] 5 | } 6 | -------------------------------------------------------------------------------- /doc/sample_annotated_tag.dot.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | auto 11 | 12 | 86b176a7d3de1724f728a4e64d2d5d29610af84f 13 | 14 | 86b176a 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /doc/sample_blob.dot: -------------------------------------------------------------------------------- 1 | digraph auto { 2 | graph [bgcolor=transparent] 3 | node [fixedsize=true style=filled width=0.95] 4 | "03b4fc4666527e20c196b3946a9bf180b67cd36c" [label="03b4fc4" fillcolor="#9ccc66"] 5 | } 6 | -------------------------------------------------------------------------------- /doc/sample_blob.dot.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | auto 11 | 12 | 03b4fc4666527e20c196b3946a9bf180b67cd36c 13 | 14 | 03b4fc4 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /doc/sample_commit.dot: -------------------------------------------------------------------------------- 1 | digraph auto { 2 | graph [bgcolor=transparent] 3 | node [fixedsize=true style=filled width=0.95] 4 | "09b9936f5afac451ca98cf7bd874a38d72ab04e7" [label="09b9936" fillcolor="#85d5fa"] 5 | } 6 | -------------------------------------------------------------------------------- /doc/sample_commit.dot.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | auto 11 | 12 | 09b9936f5afac451ca98cf7bd874a38d72ab04e7 13 | 14 | 09b9936 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /doc/sample_full.dot: -------------------------------------------------------------------------------- 1 | digraph auto { 2 | graph [bgcolor=transparent] 3 | node [fixedsize=true style=filled width=0.95] 4 | "1cd909e05d33f0f6bc4ea1caf19b5749b434ceb3" [label="1cd909e" fillcolor="#9ccc66"] 5 | "4fa2aa96733ad000e2e14791e416c928052e865a" [label="4fa2aa9" fillcolor="#9ccc66"] 6 | c5821ac9e04931231df30b2e78b4041e597e4ac9 [label=c5821ac fillcolor="#9ccc66"] 7 | "13f45043c17ec5a98a8593724358e02bf638887b" [label="13f4504" fillcolor="#bc9b8f"] 8 | "13f45043c17ec5a98a8593724358e02bf638887b" -> c5821ac9e04931231df30b2e78b4041e597e4ac9 9 | "13f45043c17ec5a98a8593724358e02bf638887b" -> "4fa2aa96733ad000e2e14791e416c928052e865a" 10 | "772eecc93f3d9c775c62f9667cade808a49b11a1" [label="772eecc" fillcolor="#bc9b8f"] 11 | "772eecc93f3d9c775c62f9667cade808a49b11a1" -> "1cd909e05d33f0f6bc4ea1caf19b5749b434ceb3" 12 | "772eecc93f3d9c775c62f9667cade808a49b11a1" -> "4fa2aa96733ad000e2e14791e416c928052e865a" 13 | "15acd4c0640ae4aa1b3fa1f44d38bbc28f145120" [label="15acd4c" fillcolor="#85d5fa"] 14 | "15acd4c0640ae4aa1b3fa1f44d38bbc28f145120" -> "772eecc93f3d9c775c62f9667cade808a49b11a1" 15 | "5dc7848e27ac621b5b63dd10b7a16ec405d0e2ad" [label="5dc7848" fillcolor="#85d5fa"] 16 | "5dc7848e27ac621b5b63dd10b7a16ec405d0e2ad" -> "13f45043c17ec5a98a8593724358e02bf638887b" 17 | "5dc7848e27ac621b5b63dd10b7a16ec405d0e2ad" -> "15acd4c0640ae4aa1b3fa1f44d38bbc28f145120" 18 | master [label=master fillcolor="#9999ff"] 19 | master -> "5dc7848e27ac621b5b63dd10b7a16ec405d0e2ad" 20 | HEAD [label=HEAD fillcolor="#e6ccff"] 21 | HEAD -> "15acd4c0640ae4aa1b3fa1f44d38bbc28f145120" 22 | v1 [label=v1 fillcolor="#ffc61a"] 23 | v1 -> "5dc7848e27ac621b5b63dd10b7a16ec405d0e2ad" 24 | } 25 | -------------------------------------------------------------------------------- /doc/sample_full.dot.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | auto 11 | 12 | 0c213d3ee3c6dd438e02ea8f465548f8dd56288b 13 | 14 | 0c213d3 15 | 16 | 17 | 54378aec6a6ea34638ac687217745e574360285e 18 | 19 | 54378ae 20 | 21 | 22 | ecf7586c0df1003ea0a3efa5a00dc3ceaac570f8 23 | 24 | ecf7586 25 | 26 | 27 | f9f2794a3dae393bdb07affde1719aee32e6c236 28 | 29 | f9f2794 30 | 31 | 32 | 0241c07298c9b2bb84793a02418716f302632564 33 | 34 | 0241c07 35 | 36 | 37 | 0241c07298c9b2bb84793a02418716f302632564->54378aec6a6ea34638ac687217745e574360285e 38 | 39 | 40 | 41 | 42 | 0241c07298c9b2bb84793a02418716f302632564->f9f2794a3dae393bdb07affde1719aee32e6c236 43 | 44 | 45 | 46 | 47 | 2358d7dee4b70234fb7078d735a835673dc4b45b 48 | 49 | 2358d7d 50 | 51 | 52 | 0241c07298c9b2bb84793a02418716f302632564->2358d7dee4b70234fb7078d735a835673dc4b45b 53 | 54 | 55 | 56 | 57 | 2358d7dee4b70234fb7078d735a835673dc4b45b->0c213d3ee3c6dd438e02ea8f465548f8dd56288b 58 | 59 | 60 | 61 | 62 | 6e414aa7e6b2bb6f8bd9fcc652ecd7a349547f92 63 | 64 | 6e414aa 65 | 66 | 67 | 6e414aa7e6b2bb6f8bd9fcc652ecd7a349547f92->54378aec6a6ea34638ac687217745e574360285e 68 | 69 | 70 | 71 | 72 | 737c972823aec2a30e726cd39821edf8d4b4826b 73 | 74 | 737c972 75 | 76 | 77 | 737c972823aec2a30e726cd39821edf8d4b4826b->ecf7586c0df1003ea0a3efa5a00dc3ceaac570f8 78 | 79 | 80 | 81 | 82 | 737c972823aec2a30e726cd39821edf8d4b4826b->6e414aa7e6b2bb6f8bd9fcc652ecd7a349547f92 83 | 84 | 85 | 86 | 87 | 8e26413dad1890d45502dd31a75f9c403eee2fef 88 | 89 | 8e26413 90 | 91 | 92 | 8e26413dad1890d45502dd31a75f9c403eee2fef->54378aec6a6ea34638ac687217745e574360285e 93 | 94 | 95 | 96 | 97 | 8e26413dad1890d45502dd31a75f9c403eee2fef->f9f2794a3dae393bdb07affde1719aee32e6c236 98 | 99 | 100 | 101 | 102 | 8e26413dad1890d45502dd31a75f9c403eee2fef->6e414aa7e6b2bb6f8bd9fcc652ecd7a349547f92 103 | 104 | 105 | 106 | 107 | e04c9e73c27f423fecb1d8220c4a4b5ca217f2de 108 | 109 | e04c9e7 110 | 111 | 112 | e04c9e73c27f423fecb1d8220c4a4b5ca217f2de->ecf7586c0df1003ea0a3efa5a00dc3ceaac570f8 113 | 114 | 115 | 116 | 117 | e04c9e73c27f423fecb1d8220c4a4b5ca217f2de->2358d7dee4b70234fb7078d735a835673dc4b45b 118 | 119 | 120 | 121 | 122 | 01ccfc8a7031f57c92a694c727fc16a1b6f6a3c8 123 | 124 | 01ccfc8 125 | 126 | 127 | 01ccfc8a7031f57c92a694c727fc16a1b6f6a3c8->8e26413dad1890d45502dd31a75f9c403eee2fef 128 | 129 | 130 | 131 | 132 | e53574e083bfb447086df95ad1214d87b6ae45c4 133 | 134 | e53574e 135 | 136 | 137 | 01ccfc8a7031f57c92a694c727fc16a1b6f6a3c8->e53574e083bfb447086df95ad1214d87b6ae45c4 138 | 139 | 140 | 141 | 142 | e53574e083bfb447086df95ad1214d87b6ae45c4->737c972823aec2a30e726cd39821edf8d4b4826b 143 | 144 | 145 | 146 | 147 | 3447a63ea27b15856a751c3f631546ad6c98d07a 148 | 149 | 3447a63 150 | 151 | 152 | 3447a63ea27b15856a751c3f631546ad6c98d07a->0241c07298c9b2bb84793a02418716f302632564 153 | 154 | 155 | 156 | 157 | 3447a63ea27b15856a751c3f631546ad6c98d07a->01ccfc8a7031f57c92a694c727fc16a1b6f6a3c8 158 | 159 | 160 | 161 | 162 | c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834 163 | 164 | c99b362 165 | 166 | 167 | 3447a63ea27b15856a751c3f631546ad6c98d07a->c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834 168 | 169 | 170 | 171 | 172 | c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834->e04c9e73c27f423fecb1d8220c4a4b5ca217f2de 173 | 174 | 175 | 176 | 177 | c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834->e53574e083bfb447086df95ad1214d87b6ae45c4 178 | 179 | 180 | 181 | 182 | idea1 183 | 184 | idea1 185 | 186 | 187 | idea1->01ccfc8a7031f57c92a694c727fc16a1b6f6a3c8 188 | 189 | 190 | 191 | 192 | idea2 193 | 194 | idea2 195 | 196 | 197 | idea2->c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834 198 | 199 | 200 | 201 | 202 | master 203 | 204 | master 205 | 206 | 207 | master->3447a63ea27b15856a751c3f631546ad6c98d07a 208 | 209 | 210 | 211 | 212 | HEAD 213 | 214 | HEAD 215 | 216 | 217 | HEAD->idea1 218 | 219 | 220 | 221 | 222 | origin/idea1 223 | 224 | idea1 225 | 226 | 227 | origin/idea1->01ccfc8a7031f57c92a694c727fc16a1b6f6a3c8 228 | 229 | 230 | 231 | 232 | origin/idea1->idea1 233 | 234 | 235 | 236 | 237 | origin/master 238 | 239 | master 240 | 241 | 242 | origin/master->3447a63ea27b15856a751c3f631546ad6c98d07a 243 | 244 | 245 | 246 | 247 | origin/master->master 248 | 249 | 250 | 251 | 252 | origin/HEAD 253 | 254 | HEAD 255 | 256 | 257 | origin/HEAD->origin/master 258 | 259 | 260 | 261 | 262 | origin 263 | 264 | origin 265 | 266 | 267 | origin->origin/idea1 268 | 269 | 270 | 271 | 272 | origin->origin/master 273 | 274 | 275 | 276 | 277 | origin->origin/HEAD 278 | 279 | 280 | 281 | 282 | b7cd4c5e269572826551e2358805684d23e9b187 283 | 284 | b7cd4c5 285 | 286 | 287 | b7cd4c5e269572826551e2358805684d23e9b187->3447a63ea27b15856a751c3f631546ad6c98d07a 288 | 289 | 290 | 291 | 292 | v0.0.1 293 | 294 | v0.0.1 295 | 296 | 297 | v0.0.1->c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834 298 | 299 | 300 | 301 | 302 | v0.0.2 303 | 304 | v0.0.2 305 | 306 | 307 | v0.0.2->b7cd4c5e269572826551e2358805684d23e9b187 308 | 309 | 310 | 311 | 312 | 313 | -------------------------------------------------------------------------------- /doc/sample_local_branch.dot: -------------------------------------------------------------------------------- 1 | digraph auto { 2 | graph [bgcolor=transparent] 3 | node [fixedsize=true style=filled width=0.95] 4 | master [label=master fillcolor="#9999ff"] 5 | } 6 | -------------------------------------------------------------------------------- /doc/sample_local_branch.dot.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | auto 11 | 12 | master 13 | 14 | master 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /doc/sample_local_head.dot: -------------------------------------------------------------------------------- 1 | digraph auto { 2 | graph [bgcolor=transparent] 3 | node [fixedsize=true style=filled width=0.95] 4 | HEAD [label=HEAD fillcolor="#e6ccff"] 5 | } 6 | -------------------------------------------------------------------------------- /doc/sample_local_head.dot.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | auto 11 | 12 | HEAD 13 | 14 | HEAD 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /doc/sample_remote_branch.dot: -------------------------------------------------------------------------------- 1 | digraph auto { 2 | graph [bgcolor=transparent] 3 | node [fixedsize=true style=filled width=0.95] 4 | "origin/master" [label=master fillcolor="#ffa366"] 5 | } 6 | -------------------------------------------------------------------------------- /doc/sample_remote_branch.dot.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | auto 11 | 12 | origin/master 13 | 14 | master 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /doc/sample_remote_head.dot: -------------------------------------------------------------------------------- 1 | digraph auto { 2 | graph [bgcolor=transparent] 3 | node [fixedsize=true style=filled width=0.95] 4 | "origin/HEAD" [label=HEAD fillcolor="#ffbeb3"] 5 | } 6 | -------------------------------------------------------------------------------- /doc/sample_remote_head.dot.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | auto 11 | 12 | origin/HEAD 13 | 14 | HEAD 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /doc/sample_remote_server.dot: -------------------------------------------------------------------------------- 1 | digraph auto { 2 | graph [bgcolor=transparent] 3 | node [fixedsize=true style=filled width=0.95] 4 | origin [label=origin fillcolor="#ff6666"] 5 | } 6 | -------------------------------------------------------------------------------- /doc/sample_remote_server.dot.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | auto 11 | 12 | origin 13 | 14 | origin 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /doc/sample_tag.dot: -------------------------------------------------------------------------------- 1 | digraph auto { 2 | graph [bgcolor=transparent] 3 | node [fixedsize=true style=filled width=0.95] 4 | "v0.0.1" [label="v0.0.1" fillcolor="#ffc61a"] 5 | } 6 | -------------------------------------------------------------------------------- /doc/sample_tag.dot.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | auto 11 | 12 | v0.0.1 13 | 14 | v0.0.1 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /doc/sample_tree.dot: -------------------------------------------------------------------------------- 1 | digraph auto { 2 | graph [bgcolor=transparent] 3 | node [fixedsize=true style=filled width=0.95] 4 | "015997188f3853a8912a9c1cf9cdd0b57cc5ee53" [label=0159971 fillcolor="#bc9b8f"] 5 | } 6 | -------------------------------------------------------------------------------- /doc/sample_tree.dot.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | auto 11 | 12 | 015997188f3853a8912a9c1cf9cdd0b57cc5ee53 13 | 14 | 0159971 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /doc/sample_upstream.dot: -------------------------------------------------------------------------------- 1 | digraph auto { 2 | graph [bgcolor=transparent] 3 | node [fixedsize=true style=invis height=0.05] 4 | A -> B 5 | } 6 | -------------------------------------------------------------------------------- /doc/sample_upstream.dot.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | auto 11 | 12 | 13 | 14 | A->B 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /git_graph/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '1.2' 2 | -------------------------------------------------------------------------------- /git_graph/cli.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import argparse 4 | 5 | import pathlib 6 | 7 | from git_graph import __version__ 8 | import git_graph.dot_graph as dg 9 | 10 | 11 | def main(args=None): 12 | example_text = '''examples: 13 | git graph 14 | git graph -p examples/demo -n btc -f svg 15 | ''' 16 | 17 | node_text = '''node types to display in the graph (default is all). 18 | 'commits' and 'branches' will focus output on commits and branches 19 | respectively. 20 | For further control, you can also pick the letters corresponding to 21 | your choice: 22 | | Node type | Letter | 23 | | -------------- | ------ | 24 | | blob | b | 25 | | tree | t | 26 | | commit | c | 27 | | local branche | l | 28 | | local head | h | 29 | | remote branche | r | 30 | | remote head | d | 31 | | remote server | s | 32 | | annotated tag | a | 33 | | tag | g | 34 | | upstream link | u | 35 | ''' 36 | 37 | parser = argparse.ArgumentParser( 38 | prog='git graph', 39 | description='Save and display your Git repositories inner content ' 40 | 'as a Directed Acyclic Graph (DAG)', 41 | epilog=example_text, 42 | formatter_class=argparse.RawTextHelpFormatter) 43 | parser.add_argument('-V', '--version', action='version', 44 | version=__version__) 45 | parser.add_argument('-p', '--path', type=str, action='store', default='.', 46 | help='path to your git repository (default is here)') 47 | parser.add_argument('-n', '--nodes', default=dg.ALL_NODES, 48 | help=node_text) 49 | parser.add_argument('-f', '--format', default=dg.DEFAULT_FORMAT, 50 | help='format of graph output: pdf, svg, png... ' 51 | '(default is pdf)') 52 | parser.add_argument('-c', '--conceal', action='store_true', default=False, 53 | help='conceal graph (deactivated by default)') 54 | args = parser.parse_args(args=args) 55 | 56 | git_path = dg.get_git_repository(pathlib.Path(args.path)) 57 | if git_path is not None: 58 | dot_graph = dg.DotGraph(git_path, nodes=args.nodes) 59 | file = dot_graph.persist(form=args.format, conceal=args.conceal) 60 | git_graph_path = git_path.resolve() / '.gitGraph' 61 | print(f'{file} saved in {git_graph_path}') 62 | return file 63 | else: 64 | print('Not a git repository') 65 | 66 | 67 | # to debug real use cases, set in your Debug Configuration something like: 68 | # Parameters = -n btc -f png 69 | # 70 | # this configuration is generated automatically by pycharm at first debug 71 | # it can be found in Run/Edit Configurations/Python 72 | if __name__ == '__main__': 73 | main() 74 | -------------------------------------------------------------------------------- /git_graph/dot_graph.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | import graphviz 4 | 5 | import git_graph.git_graph_class as gg 6 | 7 | DEFAULT_FORMAT = 'pdf' 8 | CURRENT_FOLDER = '.' 9 | SHORT = 7 10 | 11 | ALL = 'all' 12 | COMMITS = 'commits' 13 | BRANCHES = 'branches' 14 | 15 | ALL_NODES = 'dchatsglurb' 16 | COMMIT_NODES = ALL_NODES.replace('b', '').replace('t', '') 17 | BRANCH_NODES = COMMIT_NODES.replace('c', '') 18 | 19 | 20 | def get_git_repository(path): 21 | if (path / '.git').is_dir(): 22 | return path 23 | else: 24 | parents = path.resolve().parents 25 | for each in parents: 26 | if (each / '.git').is_dir(): 27 | return each 28 | 29 | 30 | def handle_specific_node_sets(nodes=ALL_NODES): 31 | if nodes == ALL: 32 | result = ALL_NODES 33 | elif nodes == COMMITS: 34 | result = COMMIT_NODES 35 | elif nodes == BRANCHES: 36 | result = BRANCH_NODES 37 | else: 38 | result = nodes 39 | return result 40 | 41 | 42 | def filter_nodes(git_graph, nodes=ALL_NODES): 43 | node_set = set() 44 | if 'b' in nodes: 45 | node_set.update(git_graph.blobs) 46 | if 't' in nodes: 47 | node_set.update(git_graph.trees) 48 | if 'c' in nodes: 49 | node_set.update(git_graph.commits) 50 | if 'l' in nodes: 51 | node_set.update(git_graph.local_branches) 52 | if 'h' in nodes: 53 | node_set.update(git_graph.local_head[0]) 54 | if 'r' in nodes: 55 | node_set.update(git_graph.remote_branches) 56 | if 'd' in nodes: 57 | node_set.update(git_graph.remote_heads) 58 | if 's' in nodes: 59 | node_set.update(git_graph.remote_servers) 60 | if 'a' in nodes: 61 | node_set.update(git_graph.annotated_tags) 62 | if 'g' in nodes: 63 | node_set.update(git_graph.tags) 64 | if 'u' in nodes: 65 | pass 66 | return node_set 67 | 68 | 69 | class DotGraph(graphviz.Digraph): 70 | 71 | def __init__(self, git_path, nodes=ALL_NODES): 72 | graphviz.Digraph.__init__(self, name='auto', 73 | graph_attr={'bgcolor': 'transparent'}, 74 | node_attr={'style': 'filled', 75 | 'fixedsize': 'true', 76 | 'width': '0.95'}) 77 | self.git_path = git_path 78 | git_graph = gg.GitGraph(self.git_path).build_graph() 79 | nodes = handle_specific_node_sets(nodes) 80 | node_set = filter_nodes(git_graph, nodes) 81 | if 'b' in nodes: 82 | for b in git_graph.blobs: 83 | self.node(b, label=b[:SHORT], fillcolor="#9ccc66") # green 84 | if 't' in nodes: 85 | for t in git_graph.trees: 86 | self.node(t, label=t[:SHORT], fillcolor="#bc9b8f") # brown 87 | for e in git_graph.trees[t]: 88 | if e[0] in node_set: 89 | self.edge(t, e[0]) 90 | if 'c' in nodes: 91 | for c in git_graph.commits: 92 | self.node(c, label=c[:SHORT], fillcolor="#85d5fa") # blue 93 | for e in git_graph.commits[c]: 94 | if e in node_set: 95 | self.edge(c, e) 96 | if 'l' in nodes: 97 | for l in git_graph.local_branches: 98 | self.node(l, label=l[:SHORT], fillcolor="#9999ff") # violet 99 | e = git_graph.local_branches[l] 100 | if e in node_set: 101 | self.edge(l, e) 102 | if 'h' in nodes: 103 | h = git_graph.local_head[0] 104 | self.node(h, label=h[:SHORT], fillcolor="#e6ccff") # pale violet 105 | e = git_graph.local_head[1] 106 | if e in node_set: 107 | self.edge(h, e) 108 | if 'r' in nodes: 109 | for r in git_graph.remote_branches: 110 | self.node(r, label=r[r.find('/') + 1:][:SHORT], 111 | fillcolor="#ffa366") # orange 112 | e = git_graph.remote_branches[r] 113 | if e in node_set: 114 | self.edge(r, e) 115 | if 'd' in nodes: 116 | for d in git_graph.remote_heads: 117 | self.node(d, label=d[d.find('/') + 1:][:SHORT], 118 | fillcolor="#ffbeb3") # pale orange 119 | e = git_graph.remote_heads[d] 120 | if e in node_set: 121 | self.edge(d, e) 122 | if 's' in nodes: 123 | for s in git_graph.remote_servers: 124 | self.node(s, label=s[:SHORT], fillcolor="#ff6666") # red 125 | for e in git_graph.remote_servers[s]: 126 | if e in node_set: 127 | self.edge(s, e) 128 | if 'a' in nodes: 129 | for a in git_graph.annotated_tags: 130 | self.node(a, label=a[:SHORT], 131 | fillcolor="#ffdf80") # pale yellow 132 | e = git_graph.annotated_tags[a] 133 | if e in node_set: 134 | self.edge(a, e) 135 | if 'g' in nodes: 136 | for g in git_graph.tags: 137 | self.node(g, label=g[:SHORT], fillcolor="#ffc61a") # yellow 138 | e = git_graph.tags[g] 139 | if e in node_set: 140 | self.edge(g, e) 141 | if 'u' in nodes: # no color (only edges) 142 | for u in git_graph.upstreams: 143 | e = git_graph.upstreams[u] 144 | if e in node_set: 145 | self.edge(u, e) 146 | 147 | def persist(self, form=DEFAULT_FORMAT, conceal=True): 148 | self.format = form 149 | git_graph_path = self.git_path / '.gitGraph' 150 | if not git_graph_path.is_dir(): 151 | git_graph_path.mkdir() 152 | dot_file_name = datetime.datetime.now().strftime( 153 | '%Y_%m_%d_%H_%M_%S_git_graph.dot') 154 | dot_file = git_graph_path / dot_file_name 155 | if conceal: 156 | self.render(dot_file) 157 | else: 158 | self.view(dot_file) 159 | image_file_name = dot_file_name + '.' + self.format 160 | return image_file_name 161 | -------------------------------------------------------------------------------- /git_graph/git_functions.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | lbr = 'refs/heads/' 4 | rbr = 'refs/remotes/' 5 | tr = 'refs/tags/' 6 | 7 | 8 | def execute_git_command(path, command): 9 | if not (path / '.git').is_dir(): 10 | print('Not a git repository') 11 | return [] 12 | bash_command = 'git -C ' + str(path) + ' ' + command 13 | try: 14 | output = subprocess.run(bash_command.split(), stdout=subprocess.PIPE) 15 | except subprocess.CalledProcessError: 16 | print('Not a git command') 17 | return [] 18 | else: 19 | result = output.stdout.decode('utf-8').splitlines() 20 | return result 21 | 22 | 23 | def read_git_file(path, sha1_file): 24 | return execute_git_command(path, 'cat-file -p ' + sha1_file) 25 | 26 | 27 | def get_git_objects(path): 28 | objects = [line.split() for line in execute_git_command( 29 | path, 'cat-file --batch-check --batch-all-objects')] 30 | blobs = [obj[0] for obj in objects if obj[1] == 'blob'] 31 | trees = [obj[0] for obj in objects if obj[1] == 'tree'] 32 | commits = [obj[0] for obj in objects if obj[1] == 'commit'] 33 | annotated_tags = [obj[0] for obj in objects if obj[1] == 'tag'] 34 | return blobs, trees, commits, annotated_tags 35 | 36 | 37 | def get_git_references(path): 38 | references = [line.split() 39 | for line in execute_git_command(path, 'for-each-ref')] 40 | local_branches = {ref[2][len(lbr):]: ref[0] 41 | for ref in references if ref[2].startswith(lbr)} 42 | remote_branches = {ref[2][len(rbr):]: ref[0] 43 | for ref in references 44 | if ref[2].startswith(rbr) and '/HEAD' not in ref[2]} 45 | tags = {ref[2][len(tr):]: ref[0] 46 | for ref in references if ref[2].startswith(tr)} 47 | return local_branches, remote_branches, tags 48 | 49 | 50 | def get_git_local_head(path): 51 | symbolic_ref = execute_git_command(path, 'symbolic-ref HEAD -q') 52 | if symbolic_ref: 53 | return symbolic_ref[0][len(lbr):] 54 | else: 55 | commit = execute_git_command(path, 'rev-parse HEAD') 56 | return commit[0] 57 | 58 | 59 | def get_git_remote_heads(path): 60 | lines = execute_git_command(path, 'branch -rv --abbrev=0') 61 | lines_split = [line.split() for line in lines if '/HEAD ' in line] 62 | remote_heads = {ls[0]: ls[2] for ls in lines_split} 63 | return remote_heads 64 | 65 | 66 | def get_git_upstreams(path): 67 | lines = execute_git_command(path, 'branch -vv --abbrev=0') 68 | lines_split = [line[2:].split() for line in lines if '[' in line] 69 | upstreams = {ls[2][1:-1]: ls[0] for ls in lines_split} 70 | return upstreams 71 | -------------------------------------------------------------------------------- /git_graph/git_graph_class.py: -------------------------------------------------------------------------------- 1 | import collections as c 2 | import re 3 | 4 | import git_graph.git_functions as gf 5 | 6 | 7 | def build_git_trees(path, trees): 8 | result = c.defaultdict(list) 9 | pattern = '(tree|blob) (.+)\t(.+)' 10 | for each_tree in trees: 11 | for each_line in gf.read_git_file(path, each_tree): 12 | match = re.search(pattern, each_line) 13 | if match: 14 | result[each_tree].append((match.group(2), match.group(3))) 15 | return result 16 | 17 | 18 | def build_git_commits(path, commits): 19 | result = c.defaultdict(list) 20 | pattern = '(tree|parent) (.+)' 21 | for each_commit in commits: 22 | for each_line in gf.read_git_file(path, each_commit): 23 | match = re.search(pattern, each_line) 24 | if match: 25 | result[each_commit].append(match.group(2)) 26 | return result 27 | 28 | 29 | def build_git_remote_servers(remote_branches, remote_heads): 30 | result = c.defaultdict(list) 31 | for rb in remote_branches: 32 | server = rb[:rb.find('/')] 33 | result[server].append(rb) 34 | for rb in remote_heads: 35 | server = rb[:rb.find('/')] 36 | result[server].append(rb) 37 | return result 38 | 39 | 40 | def build_git_annotated_tags(path, annotated_tags): 41 | result = {} 42 | pattern = '(object) (.+)' 43 | for each_annotated_tag in annotated_tags: 44 | for each_line in gf.read_git_file(path, each_annotated_tag): 45 | match = re.search(pattern, each_line) 46 | if match: 47 | result[each_annotated_tag] = match.group(2) 48 | return result 49 | 50 | 51 | class GitGraph: 52 | 53 | def __init__(self, path): 54 | self.path = path 55 | # b: -> nothing 56 | self.blobs = [] 57 | # t: -> 0 to N blobs and 0 to N trees 58 | self.trees = c.defaultdict(list) 59 | # c: -> 1 tree and 0 to N commits 60 | self.commits = c.defaultdict(list) 61 | # l: -> 1 commit 62 | self.local_branches = {} 63 | # h: -> 1 local_branch 64 | self.local_head = ('', '') 65 | # r: -> 1 commit 66 | self.remote_branches = {} 67 | # d: -> 1 remote branch 68 | self.remote_heads = {} 69 | # s: -> 1 to N remote_branches 70 | self.remote_servers = c.defaultdict(list) 71 | # a: -> 1 commit 72 | self.annotated_tags = {} 73 | # g: -> 1 commit or 1 annotated_tag 74 | self.tags = {} 75 | # u: -> 1 local_branch 76 | self.upstreams = {} 77 | 78 | def build_graph(self): 79 | blobs, trees, commits, annotated_tags = gf.get_git_objects(self.path) 80 | local_branches, remote_branches, tags = gf.get_git_references( 81 | self.path) 82 | local_head = gf.get_git_local_head(self.path) 83 | remote_heads = gf.get_git_remote_heads(self.path) 84 | upstreams = gf.get_git_upstreams(self.path) 85 | 86 | self.blobs = blobs 87 | self.trees = build_git_trees(self.path, trees) 88 | self.commits = build_git_commits(self.path, commits) 89 | self.local_branches = local_branches 90 | self.local_head = ('HEAD', local_head) 91 | self.remote_branches = remote_branches 92 | self.remote_heads = remote_heads 93 | self.remote_servers = build_git_remote_servers( 94 | remote_branches, remote_heads) 95 | self.annotated_tags = build_git_annotated_tags( 96 | self.path, annotated_tags) 97 | self.tags = tags 98 | self.upstreams = upstreams 99 | return self 100 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | graphviz 2 | pytest>=3.9 3 | flake8 -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | import pathlib 4 | import re 5 | 6 | import setuptools 7 | 8 | project_path = pathlib.Path(__file__).parent 9 | 10 | 11 | def find_readme(): 12 | with open(project_path / 'README.md', encoding='utf-8') as readme_file: 13 | result = readme_file.read() 14 | return result 15 | 16 | 17 | def find_requirements(): 18 | with open(project_path / 'requirements.txt', 19 | encoding='utf-8') as requirements_file: 20 | result = [each_line.strip() 21 | for each_line in requirements_file.read().splitlines()] 22 | return result 23 | 24 | 25 | def find_version(): 26 | with open(project_path / 'git_graph' / '__init__.py', 27 | encoding='utf-8') as version_file: 28 | pattern = '^__version__ = [\'\"]([^\'\"]*)[\'\"]' 29 | match = re.search(pattern, version_file.readline().strip()) 30 | if match: 31 | result = match.group(1) 32 | return result 33 | 34 | 35 | setuptools.setup( 36 | name='git-graph', 37 | version=find_version(), 38 | description='Learn Git fast and well ' 39 | '- by visualizing the inner graph of your Git repositories', 40 | long_description=find_readme(), 41 | long_description_content_type='text/markdown', 42 | url='https://github.com/hoduche/git-graph', 43 | author='Henri-Olivier Duché', 44 | author_email='hoduche@yahoo.fr', 45 | license='MIT', 46 | keywords='git directed acyclic graph dag graphviz dot', 47 | packages=setuptools.find_packages(), 48 | include_package_data=True, 49 | install_requires=find_requirements(), 50 | python_requires='>=3.6', 51 | entry_points={'console_scripts': ['git-graph=git_graph.cli:main']}, 52 | classifiers=[ 53 | "Programming Language :: Python :: 3", 54 | "License :: OSI Approved :: MIT License", 55 | "Operating System :: OS Independent", 56 | ], 57 | ) 58 | -------------------------------------------------------------------------------- /tests/data/.gitattributes: -------------------------------------------------------------------------------- 1 | * binary 2 | -------------------------------------------------------------------------------- /tests/data/full_repo/.gitGraph/expected.dot: -------------------------------------------------------------------------------- 1 | digraph auto { 2 | graph [bgcolor=transparent] 3 | node [fixedsize=true style=filled width=0.95] 4 | "01ccfc8a7031f57c92a694c727fc16a1b6f6a3c8" -> "8e26413dad1890d45502dd31a75f9c403eee2fef" 5 | "01ccfc8a7031f57c92a694c727fc16a1b6f6a3c8" -> e53574e083bfb447086df95ad1214d87b6ae45c4 6 | "01ccfc8a7031f57c92a694c727fc16a1b6f6a3c8" [label="01ccfc8" fillcolor="#85d5fa"] 7 | "0241c07298c9b2bb84793a02418716f302632564" -> "2358d7dee4b70234fb7078d735a835673dc4b45b" 8 | "0241c07298c9b2bb84793a02418716f302632564" -> "54378aec6a6ea34638ac687217745e574360285e" 9 | "0241c07298c9b2bb84793a02418716f302632564" -> f9f2794a3dae393bdb07affde1719aee32e6c236 10 | "0241c07298c9b2bb84793a02418716f302632564" [label="0241c07" fillcolor="#bc9b8f"] 11 | "0c213d3ee3c6dd438e02ea8f465548f8dd56288b" [label="0c213d3" fillcolor="#9ccc66"] 12 | "2358d7dee4b70234fb7078d735a835673dc4b45b" -> "0c213d3ee3c6dd438e02ea8f465548f8dd56288b" 13 | "2358d7dee4b70234fb7078d735a835673dc4b45b" [label="2358d7d" fillcolor="#bc9b8f"] 14 | "3447a63ea27b15856a751c3f631546ad6c98d07a" -> "01ccfc8a7031f57c92a694c727fc16a1b6f6a3c8" 15 | "3447a63ea27b15856a751c3f631546ad6c98d07a" -> "0241c07298c9b2bb84793a02418716f302632564" 16 | "3447a63ea27b15856a751c3f631546ad6c98d07a" -> c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834 17 | "3447a63ea27b15856a751c3f631546ad6c98d07a" [label="3447a63" fillcolor="#85d5fa"] 18 | "54378aec6a6ea34638ac687217745e574360285e" [label="54378ae" fillcolor="#9ccc66"] 19 | "6e414aa7e6b2bb6f8bd9fcc652ecd7a349547f92" -> "54378aec6a6ea34638ac687217745e574360285e" 20 | "6e414aa7e6b2bb6f8bd9fcc652ecd7a349547f92" [label="6e414aa" fillcolor="#bc9b8f"] 21 | "737c972823aec2a30e726cd39821edf8d4b4826b" -> "6e414aa7e6b2bb6f8bd9fcc652ecd7a349547f92" 22 | "737c972823aec2a30e726cd39821edf8d4b4826b" -> ecf7586c0df1003ea0a3efa5a00dc3ceaac570f8 23 | "737c972823aec2a30e726cd39821edf8d4b4826b" [label="737c972" fillcolor="#bc9b8f"] 24 | "8e26413dad1890d45502dd31a75f9c403eee2fef" -> "54378aec6a6ea34638ac687217745e574360285e" 25 | "8e26413dad1890d45502dd31a75f9c403eee2fef" -> "6e414aa7e6b2bb6f8bd9fcc652ecd7a349547f92" 26 | "8e26413dad1890d45502dd31a75f9c403eee2fef" -> f9f2794a3dae393bdb07affde1719aee32e6c236 27 | "8e26413dad1890d45502dd31a75f9c403eee2fef" [label="8e26413" fillcolor="#bc9b8f"] 28 | "v0.0.1" -> c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834 29 | "v0.0.1" [label="v0.0.1" fillcolor="#ffc61a"] 30 | HEAD -> master 31 | HEAD [label=HEAD fillcolor="#e6ccff"] 32 | c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834 -> e04c9e73c27f423fecb1d8220c4a4b5ca217f2de 33 | c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834 -> e53574e083bfb447086df95ad1214d87b6ae45c4 34 | c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834 [label=c99b362 fillcolor="#85d5fa"] 35 | e04c9e73c27f423fecb1d8220c4a4b5ca217f2de -> "2358d7dee4b70234fb7078d735a835673dc4b45b" 36 | e04c9e73c27f423fecb1d8220c4a4b5ca217f2de -> ecf7586c0df1003ea0a3efa5a00dc3ceaac570f8 37 | e04c9e73c27f423fecb1d8220c4a4b5ca217f2de [label=e04c9e7 fillcolor="#bc9b8f"] 38 | e53574e083bfb447086df95ad1214d87b6ae45c4 -> "737c972823aec2a30e726cd39821edf8d4b4826b" 39 | e53574e083bfb447086df95ad1214d87b6ae45c4 [label=e53574e fillcolor="#85d5fa"] 40 | ecf7586c0df1003ea0a3efa5a00dc3ceaac570f8 [label=ecf7586 fillcolor="#9ccc66"] 41 | f9f2794a3dae393bdb07affde1719aee32e6c236 [label=f9f2794 fillcolor="#9ccc66"] 42 | idea1 -> "01ccfc8a7031f57c92a694c727fc16a1b6f6a3c8" 43 | idea1 [label=idea1 fillcolor="#9999ff"] 44 | idea2 -> c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834 45 | idea2 [label=idea2 fillcolor="#9999ff"] 46 | master -> "3447a63ea27b15856a751c3f631546ad6c98d07a" 47 | master [label=master fillcolor="#9999ff"] 48 | } 49 | -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/COMMIT_EDITMSG: -------------------------------------------------------------------------------- 1 | commit3 2 | -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/heads/master 2 | -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/ORIG_HEAD: -------------------------------------------------------------------------------- 1 | c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834 2 | -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/config: -------------------------------------------------------------------------------- 1 | [core] 2 | repositoryformatversion = 0 3 | filemode = false 4 | bare = false 5 | logallrefupdates = true 6 | symlinks = false 7 | ignorecase = true 8 | -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/description: -------------------------------------------------------------------------------- 1 | Unnamed repository; edit this file 'description' to name the repository. 2 | -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/hooks/applypatch-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message taken by 4 | # applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. The hook is 8 | # allowed to edit the commit message file. 9 | # 10 | # To enable this hook, rename this file to "applypatch-msg". 11 | 12 | . git-sh-setup 13 | commitmsg="$(git rev-parse --git-path hooks/commit-msg)" 14 | test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"} 15 | : 16 | -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/hooks/commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message. 4 | # Called by "git commit" with one argument, the name of the file 5 | # that has the commit message. The hook should exit with non-zero 6 | # status after issuing an appropriate message if it wants to stop the 7 | # commit. The hook is allowed to edit the commit message file. 8 | # 9 | # To enable this hook, rename this file to "commit-msg". 10 | 11 | # Uncomment the below to add a Signed-off-by line to the message. 12 | # Doing this in a hook is a bad idea in general, but the prepare-commit-msg 13 | # hook is more suited to it. 14 | # 15 | # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 16 | # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" 17 | 18 | # This example catches duplicate Signed-off-by lines. 19 | 20 | test "" = "$(grep '^Signed-off-by: ' "$1" | 21 | sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { 22 | echo >&2 Duplicate Signed-off-by lines. 23 | exit 1 24 | } 25 | -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/hooks/post-update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare a packed repository for use over 4 | # dumb transports. 5 | # 6 | # To enable this hook, rename this file to "post-update". 7 | 8 | exec git update-server-info 9 | -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/hooks/pre-applypatch.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed 4 | # by applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. 8 | # 9 | # To enable this hook, rename this file to "pre-applypatch". 10 | 11 | . git-sh-setup 12 | precommit="$(git rev-parse --git-path hooks/pre-commit)" 13 | test -x "$precommit" && exec "$precommit" ${1+"$@"} 14 | : 15 | -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/hooks/pre-commit.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed. 4 | # Called by "git commit" with no arguments. The hook should 5 | # exit with non-zero status after issuing an appropriate message if 6 | # it wants to stop the commit. 7 | # 8 | # To enable this hook, rename this file to "pre-commit". 9 | 10 | if git rev-parse --verify HEAD >/dev/null 2>&1 11 | then 12 | against=HEAD 13 | else 14 | # Initial commit: diff against an empty tree object 15 | against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 16 | fi 17 | 18 | # If you want to allow non-ASCII filenames set this variable to true. 19 | allownonascii=$(git config --bool hooks.allownonascii) 20 | 21 | # Redirect output to stderr. 22 | exec 1>&2 23 | 24 | # Cross platform projects tend to avoid non-ASCII filenames; prevent 25 | # them from being added to the repository. We exploit the fact that the 26 | # printable range starts at the space character and ends with tilde. 27 | if [ "$allownonascii" != "true" ] && 28 | # Note that the use of brackets around a tr range is ok here, (it's 29 | # even required, for portability to Solaris 10's /usr/bin/tr), since 30 | # the square bracket bytes happen to fall in the designated range. 31 | test $(git diff --cached --name-only --diff-filter=A -z $against | 32 | LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0 33 | then 34 | cat <<\EOF 35 | Error: Attempt to add a non-ASCII file name. 36 | 37 | This can cause problems if you want to work with people on other platforms. 38 | 39 | To be portable it is advisable to rename the file. 40 | 41 | If you know what you are doing you can disable this check using: 42 | 43 | git config hooks.allownonascii true 44 | EOF 45 | exit 1 46 | fi 47 | 48 | # If there are whitespace errors, print the offending file names and fail. 49 | exec git diff-index --check --cached $against -- 50 | -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/hooks/pre-push.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # An example hook script to verify what is about to be pushed. Called by "git 4 | # push" after it has checked the remote status, but before anything has been 5 | # pushed. If this script exits with a non-zero status nothing will be pushed. 6 | # 7 | # This hook is called with the following parameters: 8 | # 9 | # $1 -- Name of the remote to which the push is being done 10 | # $2 -- URL to which the push is being done 11 | # 12 | # If pushing without using a named remote those arguments will be equal. 13 | # 14 | # Information about the commits which are being pushed is supplied as lines to 15 | # the standard input in the form: 16 | # 17 | # 18 | # 19 | # This sample shows how to prevent push of commits where the log message starts 20 | # with "WIP" (work in progress). 21 | 22 | remote="$1" 23 | url="$2" 24 | 25 | z40=0000000000000000000000000000000000000000 26 | 27 | while read local_ref local_sha remote_ref remote_sha 28 | do 29 | if [ "$local_sha" = $z40 ] 30 | then 31 | # Handle delete 32 | : 33 | else 34 | if [ "$remote_sha" = $z40 ] 35 | then 36 | # New branch, examine all commits 37 | range="$local_sha" 38 | else 39 | # Update to existing branch, examine new commits 40 | range="$remote_sha..$local_sha" 41 | fi 42 | 43 | # Check for WIP commit 44 | commit=`git rev-list -n 1 --grep '^WIP' "$range"` 45 | if [ -n "$commit" ] 46 | then 47 | echo >&2 "Found WIP commit in $local_ref, not pushing" 48 | exit 1 49 | fi 50 | fi 51 | done 52 | 53 | exit 0 54 | -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/hooks/pre-rebase.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Copyright (c) 2006, 2008 Junio C Hamano 4 | # 5 | # The "pre-rebase" hook is run just before "git rebase" starts doing 6 | # its job, and can prevent the command from running by exiting with 7 | # non-zero status. 8 | # 9 | # The hook is called with the following parameters: 10 | # 11 | # $1 -- the upstream the series was forked from. 12 | # $2 -- the branch being rebased (or empty when rebasing the current branch). 13 | # 14 | # This sample shows how to prevent topic branches that are already 15 | # merged to 'next' branch from getting rebased, because allowing it 16 | # would result in rebasing already published history. 17 | 18 | publish=next 19 | basebranch="$1" 20 | if test "$#" = 2 21 | then 22 | topic="refs/heads/$2" 23 | else 24 | topic=`git symbolic-ref HEAD` || 25 | exit 0 ;# we do not interrupt rebasing detached HEAD 26 | fi 27 | 28 | case "$topic" in 29 | refs/heads/??/*) 30 | ;; 31 | *) 32 | exit 0 ;# we do not interrupt others. 33 | ;; 34 | esac 35 | 36 | # Now we are dealing with a topic branch being rebased 37 | # on top of master. Is it OK to rebase it? 38 | 39 | # Does the topic really exist? 40 | git show-ref -q "$topic" || { 41 | echo >&2 "No such branch $topic" 42 | exit 1 43 | } 44 | 45 | # Is topic fully merged to master? 46 | not_in_master=`git rev-list --pretty=oneline ^master "$topic"` 47 | if test -z "$not_in_master" 48 | then 49 | echo >&2 "$topic is fully merged to master; better remove it." 50 | exit 1 ;# we could allow it, but there is no point. 51 | fi 52 | 53 | # Is topic ever merged to next? If so you should not be rebasing it. 54 | only_next_1=`git rev-list ^master "^$topic" ${publish} | sort` 55 | only_next_2=`git rev-list ^master ${publish} | sort` 56 | if test "$only_next_1" = "$only_next_2" 57 | then 58 | not_in_topic=`git rev-list "^$topic" master` 59 | if test -z "$not_in_topic" 60 | then 61 | echo >&2 "$topic is already up-to-date with master" 62 | exit 1 ;# we could allow it, but there is no point. 63 | else 64 | exit 0 65 | fi 66 | else 67 | not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"` 68 | /usr/bin/perl -e ' 69 | my $topic = $ARGV[0]; 70 | my $msg = "* $topic has commits already merged to public branch:\n"; 71 | my (%not_in_next) = map { 72 | /^([0-9a-f]+) /; 73 | ($1 => 1); 74 | } split(/\n/, $ARGV[1]); 75 | for my $elem (map { 76 | /^([0-9a-f]+) (.*)$/; 77 | [$1 => $2]; 78 | } split(/\n/, $ARGV[2])) { 79 | if (!exists $not_in_next{$elem->[0]}) { 80 | if ($msg) { 81 | print STDERR $msg; 82 | undef $msg; 83 | } 84 | print STDERR " $elem->[1]\n"; 85 | } 86 | } 87 | ' "$topic" "$not_in_next" "$not_in_master" 88 | exit 1 89 | fi 90 | 91 | exit 0 92 | 93 | ################################################################ 94 | 95 | This sample hook safeguards topic branches that have been 96 | published from being rewound. 97 | 98 | The workflow assumed here is: 99 | 100 | * Once a topic branch forks from "master", "master" is never 101 | merged into it again (either directly or indirectly). 102 | 103 | * Once a topic branch is fully cooked and merged into "master", 104 | it is deleted. If you need to build on top of it to correct 105 | earlier mistakes, a new topic branch is created by forking at 106 | the tip of the "master". This is not strictly necessary, but 107 | it makes it easier to keep your history simple. 108 | 109 | * Whenever you need to test or publish your changes to topic 110 | branches, merge them into "next" branch. 111 | 112 | The script, being an example, hardcodes the publish branch name 113 | to be "next", but it is trivial to make it configurable via 114 | $GIT_DIR/config mechanism. 115 | 116 | With this workflow, you would want to know: 117 | 118 | (1) ... if a topic branch has ever been merged to "next". Young 119 | topic branches can have stupid mistakes you would rather 120 | clean up before publishing, and things that have not been 121 | merged into other branches can be easily rebased without 122 | affecting other people. But once it is published, you would 123 | not want to rewind it. 124 | 125 | (2) ... if a topic branch has been fully merged to "master". 126 | Then you can delete it. More importantly, you should not 127 | build on top of it -- other people may already want to 128 | change things related to the topic as patches against your 129 | "master", so if you need further changes, it is better to 130 | fork the topic (perhaps with the same name) afresh from the 131 | tip of "master". 132 | 133 | Let's look at this example: 134 | 135 | o---o---o---o---o---o---o---o---o---o "next" 136 | / / / / 137 | / a---a---b A / / 138 | / / / / 139 | / / c---c---c---c B / 140 | / / / \ / 141 | / / / b---b C \ / 142 | / / / / \ / 143 | ---o---o---o---o---o---o---o---o---o---o---o "master" 144 | 145 | 146 | A, B and C are topic branches. 147 | 148 | * A has one fix since it was merged up to "next". 149 | 150 | * B has finished. It has been fully merged up to "master" and "next", 151 | and is ready to be deleted. 152 | 153 | * C has not merged to "next" at all. 154 | 155 | We would want to allow C to be rebased, refuse A, and encourage 156 | B to be deleted. 157 | 158 | To compute (1): 159 | 160 | git rev-list ^master ^topic next 161 | git rev-list ^master next 162 | 163 | if these match, topic has not merged in next at all. 164 | 165 | To compute (2): 166 | 167 | git rev-list master..topic 168 | 169 | if this is empty, it is fully merged to "master". 170 | -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/hooks/pre-receive.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to make use of push options. 4 | # The example simply echoes all push options that start with 'echoback=' 5 | # and rejects all pushes when the "reject" push option is used. 6 | # 7 | # To enable this hook, rename this file to "pre-receive". 8 | 9 | if test -n "$GIT_PUSH_OPTION_COUNT" 10 | then 11 | i=0 12 | while test "$i" -lt "$GIT_PUSH_OPTION_COUNT" 13 | do 14 | eval "value=\$GIT_PUSH_OPTION_$i" 15 | case "$value" in 16 | echoback=*) 17 | echo "echo from the pre-receive-hook: ${value#*=}" >&2 18 | ;; 19 | reject) 20 | exit 1 21 | esac 22 | i=$((i + 1)) 23 | done 24 | fi 25 | -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/hooks/prepare-commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare the commit log message. 4 | # Called by "git commit" with the name of the file that has the 5 | # commit message, followed by the description of the commit 6 | # message's source. The hook's purpose is to edit the commit 7 | # message file. If the hook fails with a non-zero status, 8 | # the commit is aborted. 9 | # 10 | # To enable this hook, rename this file to "prepare-commit-msg". 11 | 12 | # This hook includes three examples. The first comments out the 13 | # "Conflicts:" part of a merge commit. 14 | # 15 | # The second includes the output of "git diff --name-status -r" 16 | # into the message, just before the "git status" output. It is 17 | # commented because it doesn't cope with --amend or with squashed 18 | # commits. 19 | # 20 | # The third example adds a Signed-off-by line to the message, that can 21 | # still be edited. This is rarely a good idea. 22 | 23 | case "$2,$3" in 24 | merge,) 25 | /usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;; 26 | 27 | # ,|template,) 28 | # /usr/bin/perl -i.bak -pe ' 29 | # print "\n" . `git diff --cached --name-status -r` 30 | # if /^#/ && $first++ == 0' "$1" ;; 31 | 32 | *) ;; 33 | esac 34 | 35 | # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 36 | # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" 37 | -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/hooks/update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to block unannotated tags from entering. 4 | # Called by "git receive-pack" with arguments: refname sha1-old sha1-new 5 | # 6 | # To enable this hook, rename this file to "update". 7 | # 8 | # Config 9 | # ------ 10 | # hooks.allowunannotated 11 | # This boolean sets whether unannotated tags will be allowed into the 12 | # repository. By default they won't be. 13 | # hooks.allowdeletetag 14 | # This boolean sets whether deleting tags will be allowed in the 15 | # repository. By default they won't be. 16 | # hooks.allowmodifytag 17 | # This boolean sets whether a tag may be modified after creation. By default 18 | # it won't be. 19 | # hooks.allowdeletebranch 20 | # This boolean sets whether deleting branches will be allowed in the 21 | # repository. By default they won't be. 22 | # hooks.denycreatebranch 23 | # This boolean sets whether remotely creating branches will be denied 24 | # in the repository. By default this is allowed. 25 | # 26 | 27 | # --- Command line 28 | refname="$1" 29 | oldrev="$2" 30 | newrev="$3" 31 | 32 | # --- Safety check 33 | if [ -z "$GIT_DIR" ]; then 34 | echo "Don't run this script from the command line." >&2 35 | echo " (if you want, you could supply GIT_DIR then run" >&2 36 | echo " $0 )" >&2 37 | exit 1 38 | fi 39 | 40 | if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then 41 | echo "usage: $0 " >&2 42 | exit 1 43 | fi 44 | 45 | # --- Config 46 | allowunannotated=$(git config --bool hooks.allowunannotated) 47 | allowdeletebranch=$(git config --bool hooks.allowdeletebranch) 48 | denycreatebranch=$(git config --bool hooks.denycreatebranch) 49 | allowdeletetag=$(git config --bool hooks.allowdeletetag) 50 | allowmodifytag=$(git config --bool hooks.allowmodifytag) 51 | 52 | # check for no description 53 | projectdesc=$(sed -e '1q' "$GIT_DIR/description") 54 | case "$projectdesc" in 55 | "Unnamed repository"* | "") 56 | echo "*** Project description file hasn't been set" >&2 57 | exit 1 58 | ;; 59 | esac 60 | 61 | # --- Check types 62 | # if $newrev is 0000...0000, it's a commit to delete a ref. 63 | zero="0000000000000000000000000000000000000000" 64 | if [ "$newrev" = "$zero" ]; then 65 | newrev_type=delete 66 | else 67 | newrev_type=$(git cat-file -t $newrev) 68 | fi 69 | 70 | case "$refname","$newrev_type" in 71 | refs/tags/*,commit) 72 | # un-annotated tag 73 | short_refname=${refname##refs/tags/} 74 | if [ "$allowunannotated" != "true" ]; then 75 | echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 76 | echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 77 | exit 1 78 | fi 79 | ;; 80 | refs/tags/*,delete) 81 | # delete tag 82 | if [ "$allowdeletetag" != "true" ]; then 83 | echo "*** Deleting a tag is not allowed in this repository" >&2 84 | exit 1 85 | fi 86 | ;; 87 | refs/tags/*,tag) 88 | # annotated tag 89 | if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 90 | then 91 | echo "*** Tag '$refname' already exists." >&2 92 | echo "*** Modifying a tag is not allowed in this repository." >&2 93 | exit 1 94 | fi 95 | ;; 96 | refs/heads/*,commit) 97 | # branch 98 | if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then 99 | echo "*** Creating a branch is not allowed in this repository" >&2 100 | exit 1 101 | fi 102 | ;; 103 | refs/heads/*,delete) 104 | # delete branch 105 | if [ "$allowdeletebranch" != "true" ]; then 106 | echo "*** Deleting a branch is not allowed in this repository" >&2 107 | exit 1 108 | fi 109 | ;; 110 | refs/remotes/*,commit) 111 | # tracking branch 112 | ;; 113 | refs/remotes/*,delete) 114 | # delete tracking branch 115 | if [ "$allowdeletebranch" != "true" ]; then 116 | echo "*** Deleting a tracking branch is not allowed in this repository" >&2 117 | exit 1 118 | fi 119 | ;; 120 | *) 121 | # Anything else (is there anything else?) 122 | echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 123 | exit 1 124 | ;; 125 | esac 126 | 127 | # --- Finished 128 | exit 0 129 | -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/full_repo/.gitZ/index -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/info/exclude: -------------------------------------------------------------------------------- 1 | # git ls-files --others --exclude-from=.git/info/exclude 2 | # Lines that start with '#' are comments. 3 | # For a project mostly in C, the following would be a good set of 4 | # exclude patterns (uncomment them if you want to use them): 5 | # *.[oa] 6 | # *~ 7 | -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/logs/HEAD: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 e53574e083bfb447086df95ad1214d87b6ae45c4 Bernard 1318672800 +0100 commit (initial): commit1 2 | e53574e083bfb447086df95ad1214d87b6ae45c4 e53574e083bfb447086df95ad1214d87b6ae45c4 Bernard 1318672800 +0100 checkout: moving from master to idea1 3 | e53574e083bfb447086df95ad1214d87b6ae45c4 01ccfc8a7031f57c92a694c727fc16a1b6f6a3c8 Bernard 1318672800 +0100 commit: commit2 4 | 01ccfc8a7031f57c92a694c727fc16a1b6f6a3c8 e53574e083bfb447086df95ad1214d87b6ae45c4 Bernard 1318672800 +0100 checkout: moving from idea1 to master 5 | e53574e083bfb447086df95ad1214d87b6ae45c4 e53574e083bfb447086df95ad1214d87b6ae45c4 Bernard 1318672800 +0100 checkout: moving from master to idea2 6 | e53574e083bfb447086df95ad1214d87b6ae45c4 c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834 Bernard 1318672800 +0100 commit: commit3 7 | c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834 e53574e083bfb447086df95ad1214d87b6ae45c4 Bernard 1318672800 +0100 checkout: moving from idea2 to master 8 | e53574e083bfb447086df95ad1214d87b6ae45c4 c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834 Bernard 1318672800 +0100 merge idea2: Fast-forward 9 | c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834 3447a63ea27b15856a751c3f631546ad6c98d07a Bernard 1318672800 +0100 merge idea1: Merge made by the 'recursive' strategy. 10 | -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/logs/refs/heads/idea1: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 e53574e083bfb447086df95ad1214d87b6ae45c4 Bernard 1318672800 +0100 branch: Created from master 2 | e53574e083bfb447086df95ad1214d87b6ae45c4 01ccfc8a7031f57c92a694c727fc16a1b6f6a3c8 Bernard 1318672800 +0100 commit: commit2 3 | -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/logs/refs/heads/idea2: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 e53574e083bfb447086df95ad1214d87b6ae45c4 Bernard 1318672800 +0100 branch: Created from HEAD 2 | e53574e083bfb447086df95ad1214d87b6ae45c4 c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834 Bernard 1318672800 +0100 commit: commit3 3 | -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/logs/refs/heads/master: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 e53574e083bfb447086df95ad1214d87b6ae45c4 Bernard 1318672800 +0100 commit (initial): commit1 2 | e53574e083bfb447086df95ad1214d87b6ae45c4 c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834 Bernard 1318672800 +0100 merge idea2: Fast-forward 3 | c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834 3447a63ea27b15856a751c3f631546ad6c98d07a Bernard 1318672800 +0100 merge idea1: Merge made by the 'recursive' strategy. 4 | -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/objects/01/ccfc8a7031f57c92a694c727fc16a1b6f6a3c8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/full_repo/.gitZ/objects/01/ccfc8a7031f57c92a694c727fc16a1b6f6a3c8 -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/objects/02/41c07298c9b2bb84793a02418716f302632564: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/full_repo/.gitZ/objects/02/41c07298c9b2bb84793a02418716f302632564 -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/objects/0c/213d3ee3c6dd438e02ea8f465548f8dd56288b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/full_repo/.gitZ/objects/0c/213d3ee3c6dd438e02ea8f465548f8dd56288b -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/objects/23/58d7dee4b70234fb7078d735a835673dc4b45b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/full_repo/.gitZ/objects/23/58d7dee4b70234fb7078d735a835673dc4b45b -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/objects/34/47a63ea27b15856a751c3f631546ad6c98d07a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/full_repo/.gitZ/objects/34/47a63ea27b15856a751c3f631546ad6c98d07a -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/objects/54/378aec6a6ea34638ac687217745e574360285e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/full_repo/.gitZ/objects/54/378aec6a6ea34638ac687217745e574360285e -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/objects/6e/414aa7e6b2bb6f8bd9fcc652ecd7a349547f92: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/full_repo/.gitZ/objects/6e/414aa7e6b2bb6f8bd9fcc652ecd7a349547f92 -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/objects/73/7c972823aec2a30e726cd39821edf8d4b4826b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/full_repo/.gitZ/objects/73/7c972823aec2a30e726cd39821edf8d4b4826b -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/objects/8e/26413dad1890d45502dd31a75f9c403eee2fef: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/full_repo/.gitZ/objects/8e/26413dad1890d45502dd31a75f9c403eee2fef -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/objects/c9/9b3621c6f61f36a468d9da2f2f4cb6c2c7f834: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/full_repo/.gitZ/objects/c9/9b3621c6f61f36a468d9da2f2f4cb6c2c7f834 -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/objects/e0/4c9e73c27f423fecb1d8220c4a4b5ca217f2de: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/full_repo/.gitZ/objects/e0/4c9e73c27f423fecb1d8220c4a4b5ca217f2de -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/objects/e5/3574e083bfb447086df95ad1214d87b6ae45c4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/full_repo/.gitZ/objects/e5/3574e083bfb447086df95ad1214d87b6ae45c4 -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/objects/ec/f7586c0df1003ea0a3efa5a00dc3ceaac570f8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/full_repo/.gitZ/objects/ec/f7586c0df1003ea0a3efa5a00dc3ceaac570f8 -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/objects/f9/f2794a3dae393bdb07affde1719aee32e6c236: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/full_repo/.gitZ/objects/f9/f2794a3dae393bdb07affde1719aee32e6c236 -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/refs/heads/idea1: -------------------------------------------------------------------------------- 1 | 01ccfc8a7031f57c92a694c727fc16a1b6f6a3c8 2 | -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/refs/heads/idea2: -------------------------------------------------------------------------------- 1 | c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834 2 | -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/refs/heads/master: -------------------------------------------------------------------------------- 1 | 3447a63ea27b15856a751c3f631546ad6c98d07a 2 | -------------------------------------------------------------------------------- /tests/data/full_repo/.gitZ/refs/tags/v0.0.1: -------------------------------------------------------------------------------- 1 | c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834 2 | -------------------------------------------------------------------------------- /tests/data/full_repo/dir1/file1.txt: -------------------------------------------------------------------------------- 1 | content file1.txt v2 -------------------------------------------------------------------------------- /tests/data/full_repo/file2.txt: -------------------------------------------------------------------------------- 1 | content file2.txt v2 -------------------------------------------------------------------------------- /tests/data/full_repo/file3.txt: -------------------------------------------------------------------------------- 1 | content file1.txt v1 -------------------------------------------------------------------------------- /tests/data/minimal_repo/.gitGraph/expected.dot: -------------------------------------------------------------------------------- 1 | digraph auto { 2 | graph [bgcolor=transparent] 3 | node [fixedsize=true style=filled width=0.95] 4 | "54378aec6a6ea34638ac687217745e574360285e" [label="54378ae" fillcolor="#9ccc66"] 5 | "6e414aa7e6b2bb6f8bd9fcc652ecd7a349547f92" -> "54378aec6a6ea34638ac687217745e574360285e" 6 | "6e414aa7e6b2bb6f8bd9fcc652ecd7a349547f92" [label="6e414aa" fillcolor="#bc9b8f"] 7 | "737c972823aec2a30e726cd39821edf8d4b4826b" -> "6e414aa7e6b2bb6f8bd9fcc652ecd7a349547f92" 8 | "737c972823aec2a30e726cd39821edf8d4b4826b" -> ecf7586c0df1003ea0a3efa5a00dc3ceaac570f8 9 | "737c972823aec2a30e726cd39821edf8d4b4826b" [label="737c972" fillcolor="#bc9b8f"] 10 | HEAD -> master 11 | HEAD [label=HEAD fillcolor="#e6ccff"] 12 | e53574e083bfb447086df95ad1214d87b6ae45c4 -> "737c972823aec2a30e726cd39821edf8d4b4826b" 13 | e53574e083bfb447086df95ad1214d87b6ae45c4 [label=e53574e fillcolor="#85d5fa"] 14 | ecf7586c0df1003ea0a3efa5a00dc3ceaac570f8 [label=ecf7586 fillcolor="#9ccc66"] 15 | master -> e53574e083bfb447086df95ad1214d87b6ae45c4 16 | master [label=master fillcolor="#9999ff"] 17 | } 18 | -------------------------------------------------------------------------------- /tests/data/minimal_repo/.gitZ/COMMIT_EDITMSG: -------------------------------------------------------------------------------- 1 | commit1 2 | -------------------------------------------------------------------------------- /tests/data/minimal_repo/.gitZ/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/heads/master 2 | -------------------------------------------------------------------------------- /tests/data/minimal_repo/.gitZ/config: -------------------------------------------------------------------------------- 1 | [core] 2 | repositoryformatversion = 0 3 | filemode = false 4 | bare = false 5 | logallrefupdates = true 6 | symlinks = false 7 | ignorecase = true 8 | -------------------------------------------------------------------------------- /tests/data/minimal_repo/.gitZ/description: -------------------------------------------------------------------------------- 1 | Unnamed repository; edit this file 'description' to name the repository. 2 | -------------------------------------------------------------------------------- /tests/data/minimal_repo/.gitZ/hooks/applypatch-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message taken by 4 | # applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. The hook is 8 | # allowed to edit the commit message file. 9 | # 10 | # To enable this hook, rename this file to "applypatch-msg". 11 | 12 | . git-sh-setup 13 | commitmsg="$(git rev-parse --git-path hooks/commit-msg)" 14 | test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"} 15 | : 16 | -------------------------------------------------------------------------------- /tests/data/minimal_repo/.gitZ/hooks/commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message. 4 | # Called by "git commit" with one argument, the name of the file 5 | # that has the commit message. The hook should exit with non-zero 6 | # status after issuing an appropriate message if it wants to stop the 7 | # commit. The hook is allowed to edit the commit message file. 8 | # 9 | # To enable this hook, rename this file to "commit-msg". 10 | 11 | # Uncomment the below to add a Signed-off-by line to the message. 12 | # Doing this in a hook is a bad idea in general, but the prepare-commit-msg 13 | # hook is more suited to it. 14 | # 15 | # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 16 | # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" 17 | 18 | # This example catches duplicate Signed-off-by lines. 19 | 20 | test "" = "$(grep '^Signed-off-by: ' "$1" | 21 | sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { 22 | echo >&2 Duplicate Signed-off-by lines. 23 | exit 1 24 | } 25 | -------------------------------------------------------------------------------- /tests/data/minimal_repo/.gitZ/hooks/post-update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare a packed repository for use over 4 | # dumb transports. 5 | # 6 | # To enable this hook, rename this file to "post-update". 7 | 8 | exec git update-server-info 9 | -------------------------------------------------------------------------------- /tests/data/minimal_repo/.gitZ/hooks/pre-applypatch.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed 4 | # by applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. 8 | # 9 | # To enable this hook, rename this file to "pre-applypatch". 10 | 11 | . git-sh-setup 12 | precommit="$(git rev-parse --git-path hooks/pre-commit)" 13 | test -x "$precommit" && exec "$precommit" ${1+"$@"} 14 | : 15 | -------------------------------------------------------------------------------- /tests/data/minimal_repo/.gitZ/hooks/pre-commit.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed. 4 | # Called by "git commit" with no arguments. The hook should 5 | # exit with non-zero status after issuing an appropriate message if 6 | # it wants to stop the commit. 7 | # 8 | # To enable this hook, rename this file to "pre-commit". 9 | 10 | if git rev-parse --verify HEAD >/dev/null 2>&1 11 | then 12 | against=HEAD 13 | else 14 | # Initial commit: diff against an empty tree object 15 | against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 16 | fi 17 | 18 | # If you want to allow non-ASCII filenames set this variable to true. 19 | allownonascii=$(git config --bool hooks.allownonascii) 20 | 21 | # Redirect output to stderr. 22 | exec 1>&2 23 | 24 | # Cross platform projects tend to avoid non-ASCII filenames; prevent 25 | # them from being added to the repository. We exploit the fact that the 26 | # printable range starts at the space character and ends with tilde. 27 | if [ "$allownonascii" != "true" ] && 28 | # Note that the use of brackets around a tr range is ok here, (it's 29 | # even required, for portability to Solaris 10's /usr/bin/tr), since 30 | # the square bracket bytes happen to fall in the designated range. 31 | test $(git diff --cached --name-only --diff-filter=A -z $against | 32 | LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0 33 | then 34 | cat <<\EOF 35 | Error: Attempt to add a non-ASCII file name. 36 | 37 | This can cause problems if you want to work with people on other platforms. 38 | 39 | To be portable it is advisable to rename the file. 40 | 41 | If you know what you are doing you can disable this check using: 42 | 43 | git config hooks.allownonascii true 44 | EOF 45 | exit 1 46 | fi 47 | 48 | # If there are whitespace errors, print the offending file names and fail. 49 | exec git diff-index --check --cached $against -- 50 | -------------------------------------------------------------------------------- /tests/data/minimal_repo/.gitZ/hooks/pre-push.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # An example hook script to verify what is about to be pushed. Called by "git 4 | # push" after it has checked the remote status, but before anything has been 5 | # pushed. If this script exits with a non-zero status nothing will be pushed. 6 | # 7 | # This hook is called with the following parameters: 8 | # 9 | # $1 -- Name of the remote to which the push is being done 10 | # $2 -- URL to which the push is being done 11 | # 12 | # If pushing without using a named remote those arguments will be equal. 13 | # 14 | # Information about the commits which are being pushed is supplied as lines to 15 | # the standard input in the form: 16 | # 17 | # 18 | # 19 | # This sample shows how to prevent push of commits where the log message starts 20 | # with "WIP" (work in progress). 21 | 22 | remote="$1" 23 | url="$2" 24 | 25 | z40=0000000000000000000000000000000000000000 26 | 27 | while read local_ref local_sha remote_ref remote_sha 28 | do 29 | if [ "$local_sha" = $z40 ] 30 | then 31 | # Handle delete 32 | : 33 | else 34 | if [ "$remote_sha" = $z40 ] 35 | then 36 | # New branch, examine all commits 37 | range="$local_sha" 38 | else 39 | # Update to existing branch, examine new commits 40 | range="$remote_sha..$local_sha" 41 | fi 42 | 43 | # Check for WIP commit 44 | commit=`git rev-list -n 1 --grep '^WIP' "$range"` 45 | if [ -n "$commit" ] 46 | then 47 | echo >&2 "Found WIP commit in $local_ref, not pushing" 48 | exit 1 49 | fi 50 | fi 51 | done 52 | 53 | exit 0 54 | -------------------------------------------------------------------------------- /tests/data/minimal_repo/.gitZ/hooks/pre-rebase.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Copyright (c) 2006, 2008 Junio C Hamano 4 | # 5 | # The "pre-rebase" hook is run just before "git rebase" starts doing 6 | # its job, and can prevent the command from running by exiting with 7 | # non-zero status. 8 | # 9 | # The hook is called with the following parameters: 10 | # 11 | # $1 -- the upstream the series was forked from. 12 | # $2 -- the branch being rebased (or empty when rebasing the current branch). 13 | # 14 | # This sample shows how to prevent topic branches that are already 15 | # merged to 'next' branch from getting rebased, because allowing it 16 | # would result in rebasing already published history. 17 | 18 | publish=next 19 | basebranch="$1" 20 | if test "$#" = 2 21 | then 22 | topic="refs/heads/$2" 23 | else 24 | topic=`git symbolic-ref HEAD` || 25 | exit 0 ;# we do not interrupt rebasing detached HEAD 26 | fi 27 | 28 | case "$topic" in 29 | refs/heads/??/*) 30 | ;; 31 | *) 32 | exit 0 ;# we do not interrupt others. 33 | ;; 34 | esac 35 | 36 | # Now we are dealing with a topic branch being rebased 37 | # on top of master. Is it OK to rebase it? 38 | 39 | # Does the topic really exist? 40 | git show-ref -q "$topic" || { 41 | echo >&2 "No such branch $topic" 42 | exit 1 43 | } 44 | 45 | # Is topic fully merged to master? 46 | not_in_master=`git rev-list --pretty=oneline ^master "$topic"` 47 | if test -z "$not_in_master" 48 | then 49 | echo >&2 "$topic is fully merged to master; better remove it." 50 | exit 1 ;# we could allow it, but there is no point. 51 | fi 52 | 53 | # Is topic ever merged to next? If so you should not be rebasing it. 54 | only_next_1=`git rev-list ^master "^$topic" ${publish} | sort` 55 | only_next_2=`git rev-list ^master ${publish} | sort` 56 | if test "$only_next_1" = "$only_next_2" 57 | then 58 | not_in_topic=`git rev-list "^$topic" master` 59 | if test -z "$not_in_topic" 60 | then 61 | echo >&2 "$topic is already up-to-date with master" 62 | exit 1 ;# we could allow it, but there is no point. 63 | else 64 | exit 0 65 | fi 66 | else 67 | not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"` 68 | /usr/bin/perl -e ' 69 | my $topic = $ARGV[0]; 70 | my $msg = "* $topic has commits already merged to public branch:\n"; 71 | my (%not_in_next) = map { 72 | /^([0-9a-f]+) /; 73 | ($1 => 1); 74 | } split(/\n/, $ARGV[1]); 75 | for my $elem (map { 76 | /^([0-9a-f]+) (.*)$/; 77 | [$1 => $2]; 78 | } split(/\n/, $ARGV[2])) { 79 | if (!exists $not_in_next{$elem->[0]}) { 80 | if ($msg) { 81 | print STDERR $msg; 82 | undef $msg; 83 | } 84 | print STDERR " $elem->[1]\n"; 85 | } 86 | } 87 | ' "$topic" "$not_in_next" "$not_in_master" 88 | exit 1 89 | fi 90 | 91 | exit 0 92 | 93 | ################################################################ 94 | 95 | This sample hook safeguards topic branches that have been 96 | published from being rewound. 97 | 98 | The workflow assumed here is: 99 | 100 | * Once a topic branch forks from "master", "master" is never 101 | merged into it again (either directly or indirectly). 102 | 103 | * Once a topic branch is fully cooked and merged into "master", 104 | it is deleted. If you need to build on top of it to correct 105 | earlier mistakes, a new topic branch is created by forking at 106 | the tip of the "master". This is not strictly necessary, but 107 | it makes it easier to keep your history simple. 108 | 109 | * Whenever you need to test or publish your changes to topic 110 | branches, merge them into "next" branch. 111 | 112 | The script, being an example, hardcodes the publish branch name 113 | to be "next", but it is trivial to make it configurable via 114 | $GIT_DIR/config mechanism. 115 | 116 | With this workflow, you would want to know: 117 | 118 | (1) ... if a topic branch has ever been merged to "next". Young 119 | topic branches can have stupid mistakes you would rather 120 | clean up before publishing, and things that have not been 121 | merged into other branches can be easily rebased without 122 | affecting other people. But once it is published, you would 123 | not want to rewind it. 124 | 125 | (2) ... if a topic branch has been fully merged to "master". 126 | Then you can delete it. More importantly, you should not 127 | build on top of it -- other people may already want to 128 | change things related to the topic as patches against your 129 | "master", so if you need further changes, it is better to 130 | fork the topic (perhaps with the same name) afresh from the 131 | tip of "master". 132 | 133 | Let's look at this example: 134 | 135 | o---o---o---o---o---o---o---o---o---o "next" 136 | / / / / 137 | / a---a---b A / / 138 | / / / / 139 | / / c---c---c---c B / 140 | / / / \ / 141 | / / / b---b C \ / 142 | / / / / \ / 143 | ---o---o---o---o---o---o---o---o---o---o---o "master" 144 | 145 | 146 | A, B and C are topic branches. 147 | 148 | * A has one fix since it was merged up to "next". 149 | 150 | * B has finished. It has been fully merged up to "master" and "next", 151 | and is ready to be deleted. 152 | 153 | * C has not merged to "next" at all. 154 | 155 | We would want to allow C to be rebased, refuse A, and encourage 156 | B to be deleted. 157 | 158 | To compute (1): 159 | 160 | git rev-list ^master ^topic next 161 | git rev-list ^master next 162 | 163 | if these match, topic has not merged in next at all. 164 | 165 | To compute (2): 166 | 167 | git rev-list master..topic 168 | 169 | if this is empty, it is fully merged to "master". 170 | -------------------------------------------------------------------------------- /tests/data/minimal_repo/.gitZ/hooks/pre-receive.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to make use of push options. 4 | # The example simply echoes all push options that start with 'echoback=' 5 | # and rejects all pushes when the "reject" push option is used. 6 | # 7 | # To enable this hook, rename this file to "pre-receive". 8 | 9 | if test -n "$GIT_PUSH_OPTION_COUNT" 10 | then 11 | i=0 12 | while test "$i" -lt "$GIT_PUSH_OPTION_COUNT" 13 | do 14 | eval "value=\$GIT_PUSH_OPTION_$i" 15 | case "$value" in 16 | echoback=*) 17 | echo "echo from the pre-receive-hook: ${value#*=}" >&2 18 | ;; 19 | reject) 20 | exit 1 21 | esac 22 | i=$((i + 1)) 23 | done 24 | fi 25 | -------------------------------------------------------------------------------- /tests/data/minimal_repo/.gitZ/hooks/prepare-commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare the commit log message. 4 | # Called by "git commit" with the name of the file that has the 5 | # commit message, followed by the description of the commit 6 | # message's source. The hook's purpose is to edit the commit 7 | # message file. If the hook fails with a non-zero status, 8 | # the commit is aborted. 9 | # 10 | # To enable this hook, rename this file to "prepare-commit-msg". 11 | 12 | # This hook includes three examples. The first comments out the 13 | # "Conflicts:" part of a merge commit. 14 | # 15 | # The second includes the output of "git diff --name-status -r" 16 | # into the message, just before the "git status" output. It is 17 | # commented because it doesn't cope with --amend or with squashed 18 | # commits. 19 | # 20 | # The third example adds a Signed-off-by line to the message, that can 21 | # still be edited. This is rarely a good idea. 22 | 23 | case "$2,$3" in 24 | merge,) 25 | /usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;; 26 | 27 | # ,|template,) 28 | # /usr/bin/perl -i.bak -pe ' 29 | # print "\n" . `git diff --cached --name-status -r` 30 | # if /^#/ && $first++ == 0' "$1" ;; 31 | 32 | *) ;; 33 | esac 34 | 35 | # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 36 | # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" 37 | -------------------------------------------------------------------------------- /tests/data/minimal_repo/.gitZ/hooks/update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to block unannotated tags from entering. 4 | # Called by "git receive-pack" with arguments: refname sha1-old sha1-new 5 | # 6 | # To enable this hook, rename this file to "update". 7 | # 8 | # Config 9 | # ------ 10 | # hooks.allowunannotated 11 | # This boolean sets whether unannotated tags will be allowed into the 12 | # repository. By default they won't be. 13 | # hooks.allowdeletetag 14 | # This boolean sets whether deleting tags will be allowed in the 15 | # repository. By default they won't be. 16 | # hooks.allowmodifytag 17 | # This boolean sets whether a tag may be modified after creation. By default 18 | # it won't be. 19 | # hooks.allowdeletebranch 20 | # This boolean sets whether deleting branches will be allowed in the 21 | # repository. By default they won't be. 22 | # hooks.denycreatebranch 23 | # This boolean sets whether remotely creating branches will be denied 24 | # in the repository. By default this is allowed. 25 | # 26 | 27 | # --- Command line 28 | refname="$1" 29 | oldrev="$2" 30 | newrev="$3" 31 | 32 | # --- Safety check 33 | if [ -z "$GIT_DIR" ]; then 34 | echo "Don't run this script from the command line." >&2 35 | echo " (if you want, you could supply GIT_DIR then run" >&2 36 | echo " $0 )" >&2 37 | exit 1 38 | fi 39 | 40 | if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then 41 | echo "usage: $0 " >&2 42 | exit 1 43 | fi 44 | 45 | # --- Config 46 | allowunannotated=$(git config --bool hooks.allowunannotated) 47 | allowdeletebranch=$(git config --bool hooks.allowdeletebranch) 48 | denycreatebranch=$(git config --bool hooks.denycreatebranch) 49 | allowdeletetag=$(git config --bool hooks.allowdeletetag) 50 | allowmodifytag=$(git config --bool hooks.allowmodifytag) 51 | 52 | # check for no description 53 | projectdesc=$(sed -e '1q' "$GIT_DIR/description") 54 | case "$projectdesc" in 55 | "Unnamed repository"* | "") 56 | echo "*** Project description file hasn't been set" >&2 57 | exit 1 58 | ;; 59 | esac 60 | 61 | # --- Check types 62 | # if $newrev is 0000...0000, it's a commit to delete a ref. 63 | zero="0000000000000000000000000000000000000000" 64 | if [ "$newrev" = "$zero" ]; then 65 | newrev_type=delete 66 | else 67 | newrev_type=$(git cat-file -t $newrev) 68 | fi 69 | 70 | case "$refname","$newrev_type" in 71 | refs/tags/*,commit) 72 | # un-annotated tag 73 | short_refname=${refname##refs/tags/} 74 | if [ "$allowunannotated" != "true" ]; then 75 | echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 76 | echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 77 | exit 1 78 | fi 79 | ;; 80 | refs/tags/*,delete) 81 | # delete tag 82 | if [ "$allowdeletetag" != "true" ]; then 83 | echo "*** Deleting a tag is not allowed in this repository" >&2 84 | exit 1 85 | fi 86 | ;; 87 | refs/tags/*,tag) 88 | # annotated tag 89 | if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 90 | then 91 | echo "*** Tag '$refname' already exists." >&2 92 | echo "*** Modifying a tag is not allowed in this repository." >&2 93 | exit 1 94 | fi 95 | ;; 96 | refs/heads/*,commit) 97 | # branch 98 | if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then 99 | echo "*** Creating a branch is not allowed in this repository" >&2 100 | exit 1 101 | fi 102 | ;; 103 | refs/heads/*,delete) 104 | # delete branch 105 | if [ "$allowdeletebranch" != "true" ]; then 106 | echo "*** Deleting a branch is not allowed in this repository" >&2 107 | exit 1 108 | fi 109 | ;; 110 | refs/remotes/*,commit) 111 | # tracking branch 112 | ;; 113 | refs/remotes/*,delete) 114 | # delete tracking branch 115 | if [ "$allowdeletebranch" != "true" ]; then 116 | echo "*** Deleting a tracking branch is not allowed in this repository" >&2 117 | exit 1 118 | fi 119 | ;; 120 | *) 121 | # Anything else (is there anything else?) 122 | echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 123 | exit 1 124 | ;; 125 | esac 126 | 127 | # --- Finished 128 | exit 0 129 | -------------------------------------------------------------------------------- /tests/data/minimal_repo/.gitZ/index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/minimal_repo/.gitZ/index -------------------------------------------------------------------------------- /tests/data/minimal_repo/.gitZ/info/exclude: -------------------------------------------------------------------------------- 1 | # git ls-files --others --exclude-from=.git/info/exclude 2 | # Lines that start with '#' are comments. 3 | # For a project mostly in C, the following would be a good set of 4 | # exclude patterns (uncomment them if you want to use them): 5 | # *.[oa] 6 | # *~ 7 | -------------------------------------------------------------------------------- /tests/data/minimal_repo/.gitZ/logs/HEAD: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 e53574e083bfb447086df95ad1214d87b6ae45c4 Bernard 1318672800 +0100 commit (initial): commit1 2 | -------------------------------------------------------------------------------- /tests/data/minimal_repo/.gitZ/logs/refs/heads/master: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 e53574e083bfb447086df95ad1214d87b6ae45c4 Bernard 1318672800 +0100 commit (initial): commit1 2 | -------------------------------------------------------------------------------- /tests/data/minimal_repo/.gitZ/objects/54/378aec6a6ea34638ac687217745e574360285e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/minimal_repo/.gitZ/objects/54/378aec6a6ea34638ac687217745e574360285e -------------------------------------------------------------------------------- /tests/data/minimal_repo/.gitZ/objects/6e/414aa7e6b2bb6f8bd9fcc652ecd7a349547f92: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/minimal_repo/.gitZ/objects/6e/414aa7e6b2bb6f8bd9fcc652ecd7a349547f92 -------------------------------------------------------------------------------- /tests/data/minimal_repo/.gitZ/objects/73/7c972823aec2a30e726cd39821edf8d4b4826b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/minimal_repo/.gitZ/objects/73/7c972823aec2a30e726cd39821edf8d4b4826b -------------------------------------------------------------------------------- /tests/data/minimal_repo/.gitZ/objects/e5/3574e083bfb447086df95ad1214d87b6ae45c4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/minimal_repo/.gitZ/objects/e5/3574e083bfb447086df95ad1214d87b6ae45c4 -------------------------------------------------------------------------------- /tests/data/minimal_repo/.gitZ/objects/ec/f7586c0df1003ea0a3efa5a00dc3ceaac570f8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/minimal_repo/.gitZ/objects/ec/f7586c0df1003ea0a3efa5a00dc3ceaac570f8 -------------------------------------------------------------------------------- /tests/data/minimal_repo/.gitZ/refs/heads/master: -------------------------------------------------------------------------------- 1 | e53574e083bfb447086df95ad1214d87b6ae45c4 2 | -------------------------------------------------------------------------------- /tests/data/minimal_repo/dir1/file1.txt: -------------------------------------------------------------------------------- 1 | content file1.txt v1 -------------------------------------------------------------------------------- /tests/data/minimal_repo/file2.txt: -------------------------------------------------------------------------------- 1 | content file2.txt v1 -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitGraph/expected.dot: -------------------------------------------------------------------------------- 1 | digraph auto { 2 | graph [bgcolor=transparent] 3 | node [fixedsize=true style=filled width=0.95] 4 | "01ccfc8a7031f57c92a694c727fc16a1b6f6a3c8" -> "8e26413dad1890d45502dd31a75f9c403eee2fef" 5 | "01ccfc8a7031f57c92a694c727fc16a1b6f6a3c8" -> e53574e083bfb447086df95ad1214d87b6ae45c4 6 | "01ccfc8a7031f57c92a694c727fc16a1b6f6a3c8" [label="01ccfc8" fillcolor="#85d5fa"] 7 | "0241c07298c9b2bb84793a02418716f302632564" -> "2358d7dee4b70234fb7078d735a835673dc4b45b" 8 | "0241c07298c9b2bb84793a02418716f302632564" -> "54378aec6a6ea34638ac687217745e574360285e" 9 | "0241c07298c9b2bb84793a02418716f302632564" -> f9f2794a3dae393bdb07affde1719aee32e6c236 10 | "0241c07298c9b2bb84793a02418716f302632564" [label="0241c07" fillcolor="#bc9b8f"] 11 | "0c213d3ee3c6dd438e02ea8f465548f8dd56288b" [label="0c213d3" fillcolor="#9ccc66"] 12 | "2358d7dee4b70234fb7078d735a835673dc4b45b" -> "0c213d3ee3c6dd438e02ea8f465548f8dd56288b" 13 | "2358d7dee4b70234fb7078d735a835673dc4b45b" [label="2358d7d" fillcolor="#bc9b8f"] 14 | "3447a63ea27b15856a751c3f631546ad6c98d07a" -> "01ccfc8a7031f57c92a694c727fc16a1b6f6a3c8" 15 | "3447a63ea27b15856a751c3f631546ad6c98d07a" -> "0241c07298c9b2bb84793a02418716f302632564" 16 | "3447a63ea27b15856a751c3f631546ad6c98d07a" -> c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834 17 | "3447a63ea27b15856a751c3f631546ad6c98d07a" [label="3447a63" fillcolor="#85d5fa"] 18 | "54378aec6a6ea34638ac687217745e574360285e" [label="54378ae" fillcolor="#9ccc66"] 19 | "6e414aa7e6b2bb6f8bd9fcc652ecd7a349547f92" -> "54378aec6a6ea34638ac687217745e574360285e" 20 | "6e414aa7e6b2bb6f8bd9fcc652ecd7a349547f92" [label="6e414aa" fillcolor="#bc9b8f"] 21 | "737c972823aec2a30e726cd39821edf8d4b4826b" -> "6e414aa7e6b2bb6f8bd9fcc652ecd7a349547f92" 22 | "737c972823aec2a30e726cd39821edf8d4b4826b" -> ecf7586c0df1003ea0a3efa5a00dc3ceaac570f8 23 | "737c972823aec2a30e726cd39821edf8d4b4826b" [label="737c972" fillcolor="#bc9b8f"] 24 | "8e26413dad1890d45502dd31a75f9c403eee2fef" -> "54378aec6a6ea34638ac687217745e574360285e" 25 | "8e26413dad1890d45502dd31a75f9c403eee2fef" -> "6e414aa7e6b2bb6f8bd9fcc652ecd7a349547f92" 26 | "8e26413dad1890d45502dd31a75f9c403eee2fef" -> f9f2794a3dae393bdb07affde1719aee32e6c236 27 | "8e26413dad1890d45502dd31a75f9c403eee2fef" [label="8e26413" fillcolor="#bc9b8f"] 28 | "origin/HEAD" -> "origin/master" 29 | "origin/HEAD" [label=HEAD fillcolor="#ffbeb3"] 30 | "origin/idea1" -> "01ccfc8a7031f57c92a694c727fc16a1b6f6a3c8" 31 | "origin/idea1" -> idea1 32 | "origin/idea1" [label=idea1 fillcolor="#ffa366"] 33 | "origin/master" -> "3447a63ea27b15856a751c3f631546ad6c98d07a" 34 | "origin/master" -> master 35 | "origin/master" [label=master fillcolor="#ffa366"] 36 | "v0.0.1" -> c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834 37 | "v0.0.1" [label="v0.0.1" fillcolor="#ffc61a"] 38 | "v0.0.2" -> b7cd4c5e269572826551e2358805684d23e9b187 39 | "v0.0.2" [label="v0.0.2" fillcolor="#ffc61a"] 40 | HEAD -> idea1 41 | HEAD [label=HEAD fillcolor="#e6ccff"] 42 | b7cd4c5e269572826551e2358805684d23e9b187 -> "3447a63ea27b15856a751c3f631546ad6c98d07a" 43 | b7cd4c5e269572826551e2358805684d23e9b187 [label=b7cd4c5 fillcolor="#ffdf80"] 44 | c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834 -> e04c9e73c27f423fecb1d8220c4a4b5ca217f2de 45 | c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834 -> e53574e083bfb447086df95ad1214d87b6ae45c4 46 | c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834 [label=c99b362 fillcolor="#85d5fa"] 47 | e04c9e73c27f423fecb1d8220c4a4b5ca217f2de -> "2358d7dee4b70234fb7078d735a835673dc4b45b" 48 | e04c9e73c27f423fecb1d8220c4a4b5ca217f2de -> ecf7586c0df1003ea0a3efa5a00dc3ceaac570f8 49 | e04c9e73c27f423fecb1d8220c4a4b5ca217f2de [label=e04c9e7 fillcolor="#bc9b8f"] 50 | e53574e083bfb447086df95ad1214d87b6ae45c4 -> "737c972823aec2a30e726cd39821edf8d4b4826b" 51 | e53574e083bfb447086df95ad1214d87b6ae45c4 [label=e53574e fillcolor="#85d5fa"] 52 | ecf7586c0df1003ea0a3efa5a00dc3ceaac570f8 [label=ecf7586 fillcolor="#9ccc66"] 53 | f9f2794a3dae393bdb07affde1719aee32e6c236 [label=f9f2794 fillcolor="#9ccc66"] 54 | idea1 -> "01ccfc8a7031f57c92a694c727fc16a1b6f6a3c8" 55 | idea1 [label=idea1 fillcolor="#9999ff"] 56 | idea2 -> c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834 57 | idea2 [label=idea2 fillcolor="#9999ff"] 58 | master -> "3447a63ea27b15856a751c3f631546ad6c98d07a" 59 | master [label=master fillcolor="#9999ff"] 60 | origin -> "origin/HEAD" 61 | origin -> "origin/idea1" 62 | origin -> "origin/master" 63 | origin [label=origin fillcolor="#ff6666"] 64 | } 65 | -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/heads/idea1 2 | -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/config: -------------------------------------------------------------------------------- 1 | [core] 2 | repositoryformatversion = 0 3 | filemode = false 4 | bare = false 5 | logallrefupdates = true 6 | symlinks = false 7 | ignorecase = true 8 | [remote "origin"] 9 | url = https://github.com/hoduche/git-empty 10 | fetch = +refs/heads/*:refs/remotes/origin/* 11 | [branch "master"] 12 | remote = origin 13 | merge = refs/heads/master 14 | [branch "idea1"] 15 | remote = origin 16 | merge = refs/heads/idea1 17 | -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/description: -------------------------------------------------------------------------------- 1 | Unnamed repository; edit this file 'description' to name the repository. 2 | -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/hooks/applypatch-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message taken by 4 | # applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. The hook is 8 | # allowed to edit the commit message file. 9 | # 10 | # To enable this hook, rename this file to "applypatch-msg". 11 | 12 | . git-sh-setup 13 | commitmsg="$(git rev-parse --git-path hooks/commit-msg)" 14 | test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"} 15 | : 16 | -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/hooks/commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message. 4 | # Called by "git commit" with one argument, the name of the file 5 | # that has the commit message. The hook should exit with non-zero 6 | # status after issuing an appropriate message if it wants to stop the 7 | # commit. The hook is allowed to edit the commit message file. 8 | # 9 | # To enable this hook, rename this file to "commit-msg". 10 | 11 | # Uncomment the below to add a Signed-off-by line to the message. 12 | # Doing this in a hook is a bad idea in general, but the prepare-commit-msg 13 | # hook is more suited to it. 14 | # 15 | # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 16 | # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" 17 | 18 | # This example catches duplicate Signed-off-by lines. 19 | 20 | test "" = "$(grep '^Signed-off-by: ' "$1" | 21 | sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { 22 | echo >&2 Duplicate Signed-off-by lines. 23 | exit 1 24 | } 25 | -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/hooks/post-update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare a packed repository for use over 4 | # dumb transports. 5 | # 6 | # To enable this hook, rename this file to "post-update". 7 | 8 | exec git update-server-info 9 | -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/hooks/pre-applypatch.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed 4 | # by applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. 8 | # 9 | # To enable this hook, rename this file to "pre-applypatch". 10 | 11 | . git-sh-setup 12 | precommit="$(git rev-parse --git-path hooks/pre-commit)" 13 | test -x "$precommit" && exec "$precommit" ${1+"$@"} 14 | : 15 | -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/hooks/pre-commit.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed. 4 | # Called by "git commit" with no arguments. The hook should 5 | # exit with non-zero status after issuing an appropriate message if 6 | # it wants to stop the commit. 7 | # 8 | # To enable this hook, rename this file to "pre-commit". 9 | 10 | if git rev-parse --verify HEAD >/dev/null 2>&1 11 | then 12 | against=HEAD 13 | else 14 | # Initial commit: diff against an empty tree object 15 | against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 16 | fi 17 | 18 | # If you want to allow non-ASCII filenames set this variable to true. 19 | allownonascii=$(git config --bool hooks.allownonascii) 20 | 21 | # Redirect output to stderr. 22 | exec 1>&2 23 | 24 | # Cross platform projects tend to avoid non-ASCII filenames; prevent 25 | # them from being added to the repository. We exploit the fact that the 26 | # printable range starts at the space character and ends with tilde. 27 | if [ "$allownonascii" != "true" ] && 28 | # Note that the use of brackets around a tr range is ok here, (it's 29 | # even required, for portability to Solaris 10's /usr/bin/tr), since 30 | # the square bracket bytes happen to fall in the designated range. 31 | test $(git diff --cached --name-only --diff-filter=A -z $against | 32 | LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0 33 | then 34 | cat <<\EOF 35 | Error: Attempt to add a non-ASCII file name. 36 | 37 | This can cause problems if you want to work with people on other platforms. 38 | 39 | To be portable it is advisable to rename the file. 40 | 41 | If you know what you are doing you can disable this check using: 42 | 43 | git config hooks.allownonascii true 44 | EOF 45 | exit 1 46 | fi 47 | 48 | # If there are whitespace errors, print the offending file names and fail. 49 | exec git diff-index --check --cached $against -- 50 | -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/hooks/pre-push.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # An example hook script to verify what is about to be pushed. Called by "git 4 | # push" after it has checked the remote status, but before anything has been 5 | # pushed. If this script exits with a non-zero status nothing will be pushed. 6 | # 7 | # This hook is called with the following parameters: 8 | # 9 | # $1 -- Name of the remote to which the push is being done 10 | # $2 -- URL to which the push is being done 11 | # 12 | # If pushing without using a named remote those arguments will be equal. 13 | # 14 | # Information about the commits which are being pushed is supplied as lines to 15 | # the standard input in the form: 16 | # 17 | # 18 | # 19 | # This sample shows how to prevent push of commits where the log message starts 20 | # with "WIP" (work in progress). 21 | 22 | remote="$1" 23 | url="$2" 24 | 25 | z40=0000000000000000000000000000000000000000 26 | 27 | while read local_ref local_sha remote_ref remote_sha 28 | do 29 | if [ "$local_sha" = $z40 ] 30 | then 31 | # Handle delete 32 | : 33 | else 34 | if [ "$remote_sha" = $z40 ] 35 | then 36 | # New branch, examine all commits 37 | range="$local_sha" 38 | else 39 | # Update to existing branch, examine new commits 40 | range="$remote_sha..$local_sha" 41 | fi 42 | 43 | # Check for WIP commit 44 | commit=`git rev-list -n 1 --grep '^WIP' "$range"` 45 | if [ -n "$commit" ] 46 | then 47 | echo >&2 "Found WIP commit in $local_ref, not pushing" 48 | exit 1 49 | fi 50 | fi 51 | done 52 | 53 | exit 0 54 | -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/hooks/pre-rebase.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Copyright (c) 2006, 2008 Junio C Hamano 4 | # 5 | # The "pre-rebase" hook is run just before "git rebase" starts doing 6 | # its job, and can prevent the command from running by exiting with 7 | # non-zero status. 8 | # 9 | # The hook is called with the following parameters: 10 | # 11 | # $1 -- the upstream the series was forked from. 12 | # $2 -- the branch being rebased (or empty when rebasing the current branch). 13 | # 14 | # This sample shows how to prevent topic branches that are already 15 | # merged to 'next' branch from getting rebased, because allowing it 16 | # would result in rebasing already published history. 17 | 18 | publish=next 19 | basebranch="$1" 20 | if test "$#" = 2 21 | then 22 | topic="refs/heads/$2" 23 | else 24 | topic=`git symbolic-ref HEAD` || 25 | exit 0 ;# we do not interrupt rebasing detached HEAD 26 | fi 27 | 28 | case "$topic" in 29 | refs/heads/??/*) 30 | ;; 31 | *) 32 | exit 0 ;# we do not interrupt others. 33 | ;; 34 | esac 35 | 36 | # Now we are dealing with a topic branch being rebased 37 | # on top of master. Is it OK to rebase it? 38 | 39 | # Does the topic really exist? 40 | git show-ref -q "$topic" || { 41 | echo >&2 "No such branch $topic" 42 | exit 1 43 | } 44 | 45 | # Is topic fully merged to master? 46 | not_in_master=`git rev-list --pretty=oneline ^master "$topic"` 47 | if test -z "$not_in_master" 48 | then 49 | echo >&2 "$topic is fully merged to master; better remove it." 50 | exit 1 ;# we could allow it, but there is no point. 51 | fi 52 | 53 | # Is topic ever merged to next? If so you should not be rebasing it. 54 | only_next_1=`git rev-list ^master "^$topic" ${publish} | sort` 55 | only_next_2=`git rev-list ^master ${publish} | sort` 56 | if test "$only_next_1" = "$only_next_2" 57 | then 58 | not_in_topic=`git rev-list "^$topic" master` 59 | if test -z "$not_in_topic" 60 | then 61 | echo >&2 "$topic is already up-to-date with master" 62 | exit 1 ;# we could allow it, but there is no point. 63 | else 64 | exit 0 65 | fi 66 | else 67 | not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"` 68 | /usr/bin/perl -e ' 69 | my $topic = $ARGV[0]; 70 | my $msg = "* $topic has commits already merged to public branch:\n"; 71 | my (%not_in_next) = map { 72 | /^([0-9a-f]+) /; 73 | ($1 => 1); 74 | } split(/\n/, $ARGV[1]); 75 | for my $elem (map { 76 | /^([0-9a-f]+) (.*)$/; 77 | [$1 => $2]; 78 | } split(/\n/, $ARGV[2])) { 79 | if (!exists $not_in_next{$elem->[0]}) { 80 | if ($msg) { 81 | print STDERR $msg; 82 | undef $msg; 83 | } 84 | print STDERR " $elem->[1]\n"; 85 | } 86 | } 87 | ' "$topic" "$not_in_next" "$not_in_master" 88 | exit 1 89 | fi 90 | 91 | exit 0 92 | 93 | ################################################################ 94 | 95 | This sample hook safeguards topic branches that have been 96 | published from being rewound. 97 | 98 | The workflow assumed here is: 99 | 100 | * Once a topic branch forks from "master", "master" is never 101 | merged into it again (either directly or indirectly). 102 | 103 | * Once a topic branch is fully cooked and merged into "master", 104 | it is deleted. If you need to build on top of it to correct 105 | earlier mistakes, a new topic branch is created by forking at 106 | the tip of the "master". This is not strictly necessary, but 107 | it makes it easier to keep your history simple. 108 | 109 | * Whenever you need to test or publish your changes to topic 110 | branches, merge them into "next" branch. 111 | 112 | The script, being an example, hardcodes the publish branch name 113 | to be "next", but it is trivial to make it configurable via 114 | $GIT_DIR/config mechanism. 115 | 116 | With this workflow, you would want to know: 117 | 118 | (1) ... if a topic branch has ever been merged to "next". Young 119 | topic branches can have stupid mistakes you would rather 120 | clean up before publishing, and things that have not been 121 | merged into other branches can be easily rebased without 122 | affecting other people. But once it is published, you would 123 | not want to rewind it. 124 | 125 | (2) ... if a topic branch has been fully merged to "master". 126 | Then you can delete it. More importantly, you should not 127 | build on top of it -- other people may already want to 128 | change things related to the topic as patches against your 129 | "master", so if you need further changes, it is better to 130 | fork the topic (perhaps with the same name) afresh from the 131 | tip of "master". 132 | 133 | Let's look at this example: 134 | 135 | o---o---o---o---o---o---o---o---o---o "next" 136 | / / / / 137 | / a---a---b A / / 138 | / / / / 139 | / / c---c---c---c B / 140 | / / / \ / 141 | / / / b---b C \ / 142 | / / / / \ / 143 | ---o---o---o---o---o---o---o---o---o---o---o "master" 144 | 145 | 146 | A, B and C are topic branches. 147 | 148 | * A has one fix since it was merged up to "next". 149 | 150 | * B has finished. It has been fully merged up to "master" and "next", 151 | and is ready to be deleted. 152 | 153 | * C has not merged to "next" at all. 154 | 155 | We would want to allow C to be rebased, refuse A, and encourage 156 | B to be deleted. 157 | 158 | To compute (1): 159 | 160 | git rev-list ^master ^topic next 161 | git rev-list ^master next 162 | 163 | if these match, topic has not merged in next at all. 164 | 165 | To compute (2): 166 | 167 | git rev-list master..topic 168 | 169 | if this is empty, it is fully merged to "master". 170 | -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/hooks/pre-receive.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to make use of push options. 4 | # The example simply echoes all push options that start with 'echoback=' 5 | # and rejects all pushes when the "reject" push option is used. 6 | # 7 | # To enable this hook, rename this file to "pre-receive". 8 | 9 | if test -n "$GIT_PUSH_OPTION_COUNT" 10 | then 11 | i=0 12 | while test "$i" -lt "$GIT_PUSH_OPTION_COUNT" 13 | do 14 | eval "value=\$GIT_PUSH_OPTION_$i" 15 | case "$value" in 16 | echoback=*) 17 | echo "echo from the pre-receive-hook: ${value#*=}" >&2 18 | ;; 19 | reject) 20 | exit 1 21 | esac 22 | i=$((i + 1)) 23 | done 24 | fi 25 | -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/hooks/prepare-commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare the commit log message. 4 | # Called by "git commit" with the name of the file that has the 5 | # commit message, followed by the description of the commit 6 | # message's source. The hook's purpose is to edit the commit 7 | # message file. If the hook fails with a non-zero status, 8 | # the commit is aborted. 9 | # 10 | # To enable this hook, rename this file to "prepare-commit-msg". 11 | 12 | # This hook includes three examples. The first comments out the 13 | # "Conflicts:" part of a merge commit. 14 | # 15 | # The second includes the output of "git diff --name-status -r" 16 | # into the message, just before the "git status" output. It is 17 | # commented because it doesn't cope with --amend or with squashed 18 | # commits. 19 | # 20 | # The third example adds a Signed-off-by line to the message, that can 21 | # still be edited. This is rarely a good idea. 22 | 23 | case "$2,$3" in 24 | merge,) 25 | /usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;; 26 | 27 | # ,|template,) 28 | # /usr/bin/perl -i.bak -pe ' 29 | # print "\n" . `git diff --cached --name-status -r` 30 | # if /^#/ && $first++ == 0' "$1" ;; 31 | 32 | *) ;; 33 | esac 34 | 35 | # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 36 | # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" 37 | -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/hooks/update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to block unannotated tags from entering. 4 | # Called by "git receive-pack" with arguments: refname sha1-old sha1-new 5 | # 6 | # To enable this hook, rename this file to "update". 7 | # 8 | # Config 9 | # ------ 10 | # hooks.allowunannotated 11 | # This boolean sets whether unannotated tags will be allowed into the 12 | # repository. By default they won't be. 13 | # hooks.allowdeletetag 14 | # This boolean sets whether deleting tags will be allowed in the 15 | # repository. By default they won't be. 16 | # hooks.allowmodifytag 17 | # This boolean sets whether a tag may be modified after creation. By default 18 | # it won't be. 19 | # hooks.allowdeletebranch 20 | # This boolean sets whether deleting branches will be allowed in the 21 | # repository. By default they won't be. 22 | # hooks.denycreatebranch 23 | # This boolean sets whether remotely creating branches will be denied 24 | # in the repository. By default this is allowed. 25 | # 26 | 27 | # --- Command line 28 | refname="$1" 29 | oldrev="$2" 30 | newrev="$3" 31 | 32 | # --- Safety check 33 | if [ -z "$GIT_DIR" ]; then 34 | echo "Don't run this script from the command line." >&2 35 | echo " (if you want, you could supply GIT_DIR then run" >&2 36 | echo " $0 )" >&2 37 | exit 1 38 | fi 39 | 40 | if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then 41 | echo "usage: $0 " >&2 42 | exit 1 43 | fi 44 | 45 | # --- Config 46 | allowunannotated=$(git config --bool hooks.allowunannotated) 47 | allowdeletebranch=$(git config --bool hooks.allowdeletebranch) 48 | denycreatebranch=$(git config --bool hooks.denycreatebranch) 49 | allowdeletetag=$(git config --bool hooks.allowdeletetag) 50 | allowmodifytag=$(git config --bool hooks.allowmodifytag) 51 | 52 | # check for no description 53 | projectdesc=$(sed -e '1q' "$GIT_DIR/description") 54 | case "$projectdesc" in 55 | "Unnamed repository"* | "") 56 | echo "*** Project description file hasn't been set" >&2 57 | exit 1 58 | ;; 59 | esac 60 | 61 | # --- Check types 62 | # if $newrev is 0000...0000, it's a commit to delete a ref. 63 | zero="0000000000000000000000000000000000000000" 64 | if [ "$newrev" = "$zero" ]; then 65 | newrev_type=delete 66 | else 67 | newrev_type=$(git cat-file -t $newrev) 68 | fi 69 | 70 | case "$refname","$newrev_type" in 71 | refs/tags/*,commit) 72 | # un-annotated tag 73 | short_refname=${refname##refs/tags/} 74 | if [ "$allowunannotated" != "true" ]; then 75 | echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 76 | echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 77 | exit 1 78 | fi 79 | ;; 80 | refs/tags/*,delete) 81 | # delete tag 82 | if [ "$allowdeletetag" != "true" ]; then 83 | echo "*** Deleting a tag is not allowed in this repository" >&2 84 | exit 1 85 | fi 86 | ;; 87 | refs/tags/*,tag) 88 | # annotated tag 89 | if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 90 | then 91 | echo "*** Tag '$refname' already exists." >&2 92 | echo "*** Modifying a tag is not allowed in this repository." >&2 93 | exit 1 94 | fi 95 | ;; 96 | refs/heads/*,commit) 97 | # branch 98 | if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then 99 | echo "*** Creating a branch is not allowed in this repository" >&2 100 | exit 1 101 | fi 102 | ;; 103 | refs/heads/*,delete) 104 | # delete branch 105 | if [ "$allowdeletebranch" != "true" ]; then 106 | echo "*** Deleting a branch is not allowed in this repository" >&2 107 | exit 1 108 | fi 109 | ;; 110 | refs/remotes/*,commit) 111 | # tracking branch 112 | ;; 113 | refs/remotes/*,delete) 114 | # delete tracking branch 115 | if [ "$allowdeletebranch" != "true" ]; then 116 | echo "*** Deleting a tracking branch is not allowed in this repository" >&2 117 | exit 1 118 | fi 119 | ;; 120 | *) 121 | # Anything else (is there anything else?) 122 | echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 123 | exit 1 124 | ;; 125 | esac 126 | 127 | # --- Finished 128 | exit 0 129 | -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/remote_repo/.gitZ/index -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/info/exclude: -------------------------------------------------------------------------------- 1 | # git ls-files --others --exclude-from=.git/info/exclude 2 | # Lines that start with '#' are comments. 3 | # For a project mostly in C, the following would be a good set of 4 | # exclude patterns (uncomment them if you want to use them): 5 | # *.[oa] 6 | # *~ 7 | -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/logs/HEAD: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 3447a63ea27b15856a751c3f631546ad6c98d07a Bernard 1318672800 +0100 clone: from https://github.com/hoduche/git-empty 2 | 3447a63ea27b15856a751c3f631546ad6c98d07a 01ccfc8a7031f57c92a694c727fc16a1b6f6a3c8 Bernard 1318672800 +0100 checkout: moving from master to idea1 3 | -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/logs/refs/heads/idea1: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 01ccfc8a7031f57c92a694c727fc16a1b6f6a3c8 Bernard 1318672800 +0100 branch: Created from origin/idea1 2 | -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/logs/refs/heads/idea2: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834 Bernard 1318672800 +0100 branch: Created from v0.0.1 2 | -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/logs/refs/heads/master: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 3447a63ea27b15856a751c3f631546ad6c98d07a Bernard 1318672800 +0100 clone: from https://github.com/hoduche/git-empty 2 | -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/logs/refs/remotes/origin/HEAD: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 3447a63ea27b15856a751c3f631546ad6c98d07a Bernard 1318672800 +0100 clone: from https://github.com/hoduche/git-empty 2 | -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/objects/01/ccfc8a7031f57c92a694c727fc16a1b6f6a3c8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/remote_repo/.gitZ/objects/01/ccfc8a7031f57c92a694c727fc16a1b6f6a3c8 -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/objects/02/41c07298c9b2bb84793a02418716f302632564: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/remote_repo/.gitZ/objects/02/41c07298c9b2bb84793a02418716f302632564 -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/objects/0c/213d3ee3c6dd438e02ea8f465548f8dd56288b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/remote_repo/.gitZ/objects/0c/213d3ee3c6dd438e02ea8f465548f8dd56288b -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/objects/23/58d7dee4b70234fb7078d735a835673dc4b45b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/remote_repo/.gitZ/objects/23/58d7dee4b70234fb7078d735a835673dc4b45b -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/objects/34/47a63ea27b15856a751c3f631546ad6c98d07a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/remote_repo/.gitZ/objects/34/47a63ea27b15856a751c3f631546ad6c98d07a -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/objects/54/378aec6a6ea34638ac687217745e574360285e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/remote_repo/.gitZ/objects/54/378aec6a6ea34638ac687217745e574360285e -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/objects/6e/414aa7e6b2bb6f8bd9fcc652ecd7a349547f92: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/remote_repo/.gitZ/objects/6e/414aa7e6b2bb6f8bd9fcc652ecd7a349547f92 -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/objects/73/7c972823aec2a30e726cd39821edf8d4b4826b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/remote_repo/.gitZ/objects/73/7c972823aec2a30e726cd39821edf8d4b4826b -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/objects/8e/26413dad1890d45502dd31a75f9c403eee2fef: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/remote_repo/.gitZ/objects/8e/26413dad1890d45502dd31a75f9c403eee2fef -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/objects/b7/cd4c5e269572826551e2358805684d23e9b187: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/remote_repo/.gitZ/objects/b7/cd4c5e269572826551e2358805684d23e9b187 -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/objects/c9/9b3621c6f61f36a468d9da2f2f4cb6c2c7f834: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/remote_repo/.gitZ/objects/c9/9b3621c6f61f36a468d9da2f2f4cb6c2c7f834 -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/objects/e0/4c9e73c27f423fecb1d8220c4a4b5ca217f2de: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/remote_repo/.gitZ/objects/e0/4c9e73c27f423fecb1d8220c4a4b5ca217f2de -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/objects/e5/3574e083bfb447086df95ad1214d87b6ae45c4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/remote_repo/.gitZ/objects/e5/3574e083bfb447086df95ad1214d87b6ae45c4 -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/objects/ec/f7586c0df1003ea0a3efa5a00dc3ceaac570f8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/remote_repo/.gitZ/objects/ec/f7586c0df1003ea0a3efa5a00dc3ceaac570f8 -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/objects/f9/f2794a3dae393bdb07affde1719aee32e6c236: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoduche/git-graph/efa8f3118691347bb1eecff0d9384a285df5ab40/tests/data/remote_repo/.gitZ/objects/f9/f2794a3dae393bdb07affde1719aee32e6c236 -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/packed-refs: -------------------------------------------------------------------------------- 1 | # pack-refs with: peeled fully-peeled 2 | 01ccfc8a7031f57c92a694c727fc16a1b6f6a3c8 refs/remotes/origin/idea1 3 | 3447a63ea27b15856a751c3f631546ad6c98d07a refs/remotes/origin/master 4 | c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834 refs/tags/v0.0.1 5 | b7cd4c5e269572826551e2358805684d23e9b187 refs/tags/v0.0.2 6 | ^3447a63ea27b15856a751c3f631546ad6c98d07a 7 | -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/refs/heads/idea1: -------------------------------------------------------------------------------- 1 | 01ccfc8a7031f57c92a694c727fc16a1b6f6a3c8 2 | -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/refs/heads/idea2: -------------------------------------------------------------------------------- 1 | c99b3621c6f61f36a468d9da2f2f4cb6c2c7f834 2 | -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/refs/heads/master: -------------------------------------------------------------------------------- 1 | 3447a63ea27b15856a751c3f631546ad6c98d07a 2 | -------------------------------------------------------------------------------- /tests/data/remote_repo/.gitZ/refs/remotes/origin/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/remotes/origin/master 2 | -------------------------------------------------------------------------------- /tests/data/remote_repo/dir1/file1.txt: -------------------------------------------------------------------------------- 1 | content file1.txt v1 -------------------------------------------------------------------------------- /tests/data/remote_repo/file2.txt: -------------------------------------------------------------------------------- 1 | content file2.txt v2 -------------------------------------------------------------------------------- /tests/data/remote_repo/file3.txt: -------------------------------------------------------------------------------- 1 | content file1.txt v1 -------------------------------------------------------------------------------- /tests/test_cli.py: -------------------------------------------------------------------------------- 1 | import pathlib 2 | 3 | import pytest 4 | 5 | import git_graph.cli as cli 6 | 7 | tests_data_path = pathlib.Path(__file__).parent / 'data' 8 | 9 | git_repos = ['minimal_repo', 10 | 'full_repo', 11 | 'remote_repo'] 12 | 13 | 14 | @pytest.mark.parametrize("git_repo", git_repos, ids=git_repos) 15 | def test_repo(git_repo): 16 | git_repo_path = tests_data_path / git_repo 17 | 18 | # mandatory as git considers subfolders with .git inside as submodules 19 | old_git = git_repo_path / '.gitZ' 20 | new_git = git_repo_path / '.git' 21 | old_git.rename(new_git) 22 | 23 | args = ['-p', str(git_repo_path), '-c'] 24 | 25 | output_file = cli.main(args) 26 | 27 | git_graph_path = git_repo_path / '.gitGraph' 28 | image_file = git_graph_path / output_file 29 | dot_file = git_graph_path / image_file.stem 30 | 31 | with open(dot_file) as f: 32 | dot_graph = f.readlines() 33 | dot_graph[3:-1] = sorted(dot_graph[3:-1]) 34 | with open(git_graph_path / 'expected.dot') as expected_dot_graph: 35 | assert ''.join(dot_graph) == expected_dot_graph.read() 36 | 37 | pathlib.Path.unlink(image_file) 38 | pathlib.Path.unlink(dot_file) 39 | 40 | new_git.rename(old_git) 41 | -------------------------------------------------------------------------------- /tests/test_dot_graph.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pathlib 3 | import subprocess 4 | 5 | import pytest 6 | 7 | import git_graph.dot_graph as dg 8 | 9 | tests_data_path = pathlib.Path(__file__).parent / 'data' 10 | 11 | git_repo_types = [('minimal_repo', 4), 12 | ('full_repo', 5), 13 | ('remote_repo', 5)] 14 | 15 | author = "Bernard" 16 | email = "author@cie.com" 17 | action_date = "Sat Oct 15 11:00 2011 +0100" 18 | os.environ["GIT_AUTHOR_NAME"] = author 19 | os.environ["GIT_COMMITTER_NAME"] = author 20 | os.environ["GIT_AUTHOR_EMAIL"] = email 21 | os.environ["GIT_COMMITTER_EMAIL"] = email 22 | os.environ["GIT_AUTHOR_DATE"] = action_date 23 | os.environ["GIT_COMMITTER_DATE"] = action_date 24 | 25 | 26 | @pytest.fixture(params=git_repo_types, ids=[i[0] for i in git_repo_types]) 27 | def build_repo(request, tmp_path): 28 | git_repo_type = request.param[0] 29 | nb_files = request.param[1] 30 | if git_repo_type == 'minimal_repo': 31 | build_minimal_repo(tmp_path) 32 | elif git_repo_type == 'full_repo': 33 | build_full_repo(tmp_path) 34 | elif git_repo_type == 'remote_repo': 35 | build_remote_repo(tmp_path) 36 | return git_repo_type, nb_files, tmp_path 37 | 38 | 39 | def test_repo(build_repo): 40 | git_repo_type, nb_files, tmp_path = build_repo 41 | 42 | output_file = dg.DotGraph(tmp_path).persist() 43 | 44 | git_graph_path = tmp_path / '.gitGraph' 45 | image_file = git_graph_path / output_file 46 | dot_file = git_graph_path / image_file.stem 47 | 48 | assert len(list(tmp_path.iterdir())) == nb_files 49 | assert git_graph_path.is_dir() 50 | assert len(list(git_graph_path.iterdir())) == 2 51 | 52 | with open(dot_file) as f: 53 | dot_graph = f.readlines() 54 | dot_graph[3:-1] = sorted(dot_graph[3:-1]) 55 | golden = tests_data_path / git_repo_type / '.gitGraph' / 'expected.dot' 56 | with open(golden) as expected_dot_graph: 57 | assert ''.join(dot_graph) == expected_dot_graph.read() 58 | 59 | 60 | def execute_bash_command(path, command): 61 | try: 62 | subprocess.run(command.split(), cwd=str(path)) 63 | except subprocess.CalledProcessError: 64 | print('Not a bash command') 65 | 66 | 67 | def build_minimal_repo(tmp_path): 68 | dir1 = tmp_path / 'dir1' 69 | dir1.mkdir() 70 | file1 = dir1 / 'file1.txt' 71 | file1.write_text('content file1.txt v1') 72 | assert len(list(tmp_path.iterdir())) == 1 73 | assert file1.is_file() 74 | assert file1.read_text() == 'content file1.txt v1' 75 | file2 = tmp_path / 'file2.txt' 76 | file2.write_text('content file2.txt v1') 77 | assert len(list(tmp_path.iterdir())) == 2 78 | assert file2.is_file() 79 | assert file2.read_text() == 'content file2.txt v1' 80 | execute_bash_command(tmp_path, 'git init') 81 | assert len(list(tmp_path.iterdir())) == 3 82 | assert (tmp_path / '.git').is_dir() 83 | execute_bash_command(tmp_path, 'git add -A') 84 | execute_bash_command(tmp_path, 'git commit -m commit1') 85 | 86 | 87 | def build_full_repo(tmp_path): 88 | execute_bash_command(tmp_path, 'git init') 89 | dir1 = tmp_path / 'dir1' 90 | dir1.mkdir() 91 | file1 = dir1 / 'file1.txt' 92 | file1.write_text('content file1.txt v1') 93 | file2 = tmp_path / 'file2.txt' 94 | file2.write_text('content file2.txt v1') 95 | execute_bash_command(tmp_path, 'git add -A') 96 | execute_bash_command(tmp_path, 'git commit -m commit1') 97 | execute_bash_command(tmp_path, 'git branch idea1') 98 | execute_bash_command(tmp_path, 'git checkout idea1') 99 | file2.write_text('content file2.txt v2') 100 | file3 = tmp_path / 'file3.txt' 101 | file3.write_text('content file1.txt v1') 102 | execute_bash_command(tmp_path, 'git add -A') 103 | execute_bash_command(tmp_path, 'git commit -m commit2') 104 | execute_bash_command(tmp_path, 'git checkout master') 105 | execute_bash_command(tmp_path, 'git checkout -b idea2') 106 | file1.write_text('content file1.txt v2') 107 | execute_bash_command(tmp_path, 'git commit -am commit3') 108 | execute_bash_command(tmp_path, 'git checkout master') 109 | execute_bash_command(tmp_path, 'git merge idea2') 110 | execute_bash_command(tmp_path, 'git tag v0.0.1') 111 | execute_bash_command(tmp_path, 'git merge idea1 --no-edit') 112 | # execute_bash_command(tmp_path, 'git tag v0.0.2 -am "good job"') 113 | 114 | 115 | def build_remote_repo(tmp_path): 116 | # resume from full_repo 117 | # git tag v0.0.2 -am "good job" 118 | # git remote add origin https://github.com/hoduche/git-empty.git 119 | # git push -u origin master 120 | # git checkout idea1 121 | # git push -u origin idea1 122 | # git checkout master 123 | # git push origin --tags 124 | execute_bash_command(tmp_path, 125 | 'git clone https://github.com/hoduche/git-empty .') 126 | execute_bash_command(tmp_path, 'git checkout -b idea1 origin/idea1') 127 | execute_bash_command(tmp_path, 'git branch idea2 v0.0.1') 128 | --------------------------------------------------------------------------------