LoginSignup
40
33

More than 5 years have passed since last update.

GitPythonを使う

この記事は Git AdventCalendar 2016 17日目の記事です。

GitPythonはGitの操作を行うためのPythonのライブラリです。
今回はよく行うGitの操作をGitPythonで行います。

環境は以下を使います。

  • macOS-0.12.1
  • Python-3.5.2
  • GitPython-2.0.10.dev0

インストール

$ pip install GitPython

このドキュメント作成時にはGitPythonの内部を知りたかったのでGithubからソースをcloneしてeditable install しました。

$ git clone git@github.com:gitpython-developers/GitPython.git
$ cd GitPython
$ pip install -e .

pip install -e .-e オプションはエディタブルインストールをするもので、
これを実行するとPyPIからパッケージを取得するのではなく、
指定したディレクトリにあるソースを使用してライブラリを利用できます。開発時に便利です。

内部を見るために

GitPythonはgitコマンドをsubprocessで実行する実装でした(一部そうじゃないところもあります)。
subprocess.Popen()にコマンドを渡す前にprintで表示すれば、
挙動がわかりやすそうだったのでソースに手を加えました。

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,

この状態でGitPythonのコマンドを実行すると以下のようになります。

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

[EXECUTE] の所は実際に実行されたgitコマンドですね。今回はこれで行きます。

repo.git

repo.gitを使ってCLI操作と同等のことができます。

git reset --hard HEAD は以下のようになります。

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 は以下のようになります。

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

cherry-pick は名前に - が付いていてどうやって指定すればいいのかと思いますが
(Pythonでは関数名に - は使えないですよね)、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'

ならば全てこのやり方で良いように思えます。
ただ、repo.indexなどが持つメソッドには意味のある引数名が付いていたり、
--dry-run を事前に実行するものがあったりと、
素でやるよりも考慮されているところがあります。

よく使う操作

ここからは repo.git でオプションを直接渡す以外の方法がどのようなコマンドを
発行するのかみて行きます。

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

ブランチを作成するのはrepo.indexだと難しかったので、repo.gitを使いました。

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', 'git@github.com:TakesxiSximada/testing.git')
[EXECUTE] git remote add origin git@github.com: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 は出力を解析しやすい形式にするためのオプションですね。

git clone

cloneはgit.Git.clone())を使います。

In [13]: git.Git().clone('git@github.com:TakesxiSximada/testing.git')
[EXECUTE] git clone git@github.com:TakesxiSximada/testing.git
Out[13]: ''

40
33
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
40
33