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.
$ 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.
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.
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]: ''