Use GitPython

Use GitPython

This article is the 17th day of Git AdventCalendar 2016.

GitPython is a Python library for manipulating Git. This time, we will use Git Python for common Git operations.

The environment uses the following.

Installation

$ pip install GitPython

At the time of writing this document, I wanted to know the inside of GitPython, so I cloned the source from Github and did an editable install.

$ git clone [email protected]:gitpython-developers/GitPython.git
$ cd GitPython
$ pip install -e .

The -e option of pip install -e . is for an editable installation. Doing this instead of getting the package from PyPI You can use the library with the source in the specified directory. Useful during development.

To see inside

GitPython was an implementation that executes git commands in a subprocess (some are not). If you print it before passing the command to subprocess.Popen (), The behavior seemed to be easy to understand, so I modified the source.

diff --git a/git/cmd.py b/git/cmd.py
index 1481ac8..8f47895 100644
--- a/git/cmd.py
+++ b/git/cmd.py
@@ -564,6 +564,7 @@ class Git(LazyMixin):
         log.debug("Popen(%s, cwd=%s, universal_newlines=%s, shell=%s)",
                   command, cwd, universal_newlines, shell)
         try:
+            print('[EXECUTE] {}'.format(' '.join(command)))
             proc = Popen(command,
                          env=env,
                          cwd=cwd,

If you execute the GitPython command in this state, it will be as follows.

In [2]: git.Repo.init('fish')
[EXECUTE] git init
Out[2]: <git.Repo "/working/git-python-example/fish/.git">

The place of [EXECUTE] is the git command actually executed. This time I will go with this.

repo.git

You can use repo.git to do the equivalent of CLI operations.

git reset --hard HEAD looks like this:

In [62]: repo.git.reset('--hard', 'HEAD')
[EXECUTE] git reset --hard HEAD
Out[62]: 'HEAD is now at 72a4b4c add b'

git checkout -b fish looks like this:

In [63]: repo.git.checkout('-b', 'fish')
[EXECUTE] git checkout -b fish
Out[63]: ''

I wonder how to specify cherry-pick because it has - in its name. (In Python, you can't use - in the function name, right?), Just use cherry_pick.

In [65]: repo.git.cherry_pick('7465612')
[EXECUTE] git cherry-pick 7465612
Out[65]: '[master d7382d6] add d\n Date: Sat Dec 17 15:10:40 2016 +0900\n 1 file changed, 0 insertions(+), 0 deletions(-)\n create mode 100644 D'

Then everything seems to be fine this way. However, the methods of repo.index etc. have meaningful argument names, Some run --dry-run in advance, There are some things that are considered rather than doing it plainly.

Frequently used operations

From here on, what commands are available other than passing options directly in repo.git? I will see if it will be issued.

git init

In [2]: repo = git.Repo.init()
[EXECUTE] git init

git add

In [3]: !touch README

In [4]: repo.index.add(['README'])
Out[4]: [(100644, e69de29bb2d1d6434b8b29ae775ad8c2e48c5391, 0, README)]

git commit

In [6]: repo.index.commit('initial commit')
[EXECUTE] git cat-file --batch-check
[EXECUTE] git cat-file --batch
Out[6]: <git.Commit "c1f08a997733cea1124bceefeef67f8bbfdcdd0a">

git reset

In [14]: repo.index.reset()
[EXECUTE] git read-tree --index-output=/working/git-python-example/repo2/.git/06yx3zdq HEAD
Out[14]: <git.index.base.IndexFile at 0x104959458>

git checkout

In [21]: repo.index.checkout(['README'], force=True)
[EXECUTE] git checkout-index --index --force --stdin
Out[21]: ['README']

git checkout -b

I used repo.git because it was difficult to create a branch with repo.index.

In [28]: repo.git.checkout('master', b='test')
[EXECUTE] git checkout -b test master
Out[28]: ''

git merge

In [35]: repo.index.merge_tree('test').commit('merged')
[EXECUTE] git read-tree --aggressive -i -m test
Out[35]: <git.Commit "13b98e27f44bd5a40524fe9c8573c40cc7d71168">

git pull

In [36]: repo.create_remote('origin', '[email protected]:TakesxiSximada/testing.git')
[EXECUTE] git remote add origin [email protected]:TakesxiSximada/testing.git
Out[36]: <git.Remote "origin">

git push

In [72]: remote.push('master')
[EXECUTE] git push --porcelain origin master
Out[72]: [<git.remote.PushInfo at 0x104a04eb8>]

--porcelain is an option to make the output easier to parse.

git clone

clone uses git.Git.clone ()).

In [13]: git.Git().clone('[email protected]:TakesxiSximada/testing.git')
[EXECUTE] git clone [email protected]:TakesxiSximada/testing.git
Out[13]: ''

Recommended Posts

Use GitPython
Use DeepLabCut
Use pycscope
Use collections.Counter
Use: Django-MySQL
Use Pygments.rb
use pandas-ply
Use Miniconda
Use Invariant TSC
[C] Use qsort ()
Use JIRA API
Use weak references
Use django-debug-toolbar non-locally
Use combinatorial optimization
use go module