花下貓語: 今天,我在查閱如何用 Python 操作 Gitlab 的時候,看到這篇文章,覺得還不錯,特分享給大家。文中還提到了其它幾種操作 Git 的方法,后續(xù)有機會的話,再陸續(xù)分享之~~
作者:匿蟒
出處:https://note.qidong.name/2018/01/gitpython
有時,需要做復(fù)雜的 Git 操作,并且有很多中間邏輯。 用 Shell 做復(fù)雜的邏輯運算與流程控制就是一個災(zāi)難。 所以,用 Python 來實現(xiàn)是一個愉快的選擇。 這時,就需要在 Python 中操作 Git 的庫。
GitPython 簡介
GitPython是一個與Git庫交互的Python庫,包括底層命令(Plumbing)與高層命令(Porcelain)。 它可以實現(xiàn)絕大部分的Git讀寫操作,避免了頻繁與Shell交互的畸形代碼。 它并非是一個純粹的Python實現(xiàn),而是有一部分依賴于直接執(zhí)行g(shù)it命令,另一部分依賴于GitDB。
GitDB也是一個Python庫。 它為.git/objects建立了一個數(shù)據(jù)庫模型,可以實現(xiàn)直接的讀寫。 由于采用流式(stream)讀寫,所以運行高效、內(nèi)存占用低。
GitPython安裝
pip install GitPython
其依賴GitDB會自動安裝,不過可執(zhí)行的git命令需要額外安裝。
基本用法
init
import git repo = git.Repo.init(path='.')
這樣就在當(dāng)前目錄創(chuàng)建了一個Git庫。 當(dāng)然,路徑可以自定義。
由于git.Repo實現(xiàn)了__enter__與__exit__,所以可以與with聯(lián)合使用。
with git.Repo.init(path='.') as repo: # do sth with repo
不過,由于只是實現(xiàn)了一些清理操作,關(guān)閉后仍然可以讀寫,所以使用這種形式的必要性不高。 詳見附錄。
clone
clone分兩種。 一是從當(dāng)前庫clone到另一個位置:
new_repo = repo.clone(path='../new')
二是從某個URL那里clone到本地某個位置:
new_repo = git.Repo.clone_from(url='git@github.com:USER/REPO.git', to_path='../new')
commit
with open('test.file', 'w') as fobj: fobj.write('1st linen') repo.index.add(items=['test.file']) repo.index.commit('write a line into test.file') ? with open('test.file', 'aw') as fobj: fobj.write('2nd linen') repo.index.add(items=['test.file']) repo.index.commit('write another line into test.file')
status
GitPython并未實現(xiàn)原版git status,而是給出了部分的信息。
>>> repo.is_dirty() False >>> with open('test.file', 'aw') as fobj: >>> fobj.write('dirty linen') >>> repo.is_dirty() True >>> repo.untracked_files [] >>> with open('untracked.file', 'w') as fobj: >>> fobj.write('') >>> repo.untracked_files ['untracked.file']
checkout(清理所有修改)
>>> repo.is_dirty() True >>> repo.index.checkout(force=True) <generator object <genexpr> at 0x7f2bf35e6b40> >>> repo.is_dirty() False
branch
獲取當(dāng)前分支:
head = repo.head
新建分支:
new_head = repo.create_head('new_head', 'HEAD^')
切換分支:
new_head.checkout() head.checkout()
刪除分支:
git.Head.delete(repo, new_head) # or git.Head.delete(repo, 'new_head')
merge
以下演示如何在一個分支(other),merge另一個分支(master)。
master = repo.heads.master other = repo.create_head('other', 'HEAD^') other.checkout() repo.index.merge_tree(master) repo.index.commit('Merge from master to other')
remote, fetch, pull, push
創(chuàng)建remote:
remote = repo.create_remote(name='gitlab', url='git@gitlab.com:USER/REPO.git')
遠(yuǎn)程交互操作:
remote = repo.remote() remote.fetch() remote.pull() remote.push()
刪除remote:
repo.delete_remote(remote) # or repo.delete_remote('gitlab')
其它
其它還有Tag、Submodule等相關(guān)操作,不是很常用,這里就不介紹了。
GitPython的優(yōu)點是在做讀操作時可以方便地獲取內(nèi)部信息,缺點是在做寫操作時感覺很不順手,隔靴搔癢。 當(dāng)然,它還支持直接執(zhí)行g(shù)it操作。
git = repo.git git.status() git.checkout('HEAD', b="my_new_branch") git.branch('another-new-one') git.branch('-D', 'another-new-one')
這……感覺又回到了老路,而且仍然感覺怪怪的。
其它操作Git的方法
subprocess
這就是所謂『老路』。 在另一個進(jìn)程,執(zhí)行Shell命令,并通過stdio來解析返回結(jié)果。
import subprocess subprocess.call(['git', 'status'])
dulwich
dulwich是一個純Python實現(xiàn)的Git交互庫,以后有空再研究吧。
官方網(wǎng)站:https://www.dulwich.io/
pygit2
pygit2是基于libgit2實現(xiàn)的一個Python庫。 底層是C,而上層Python只是接口,運行效率應(yīng)該是最高的,然而孤還是放棄了。 其缺點是,需要環(huán)境中預(yù)先安裝libgit2。 相比之下,GitPython只需要環(huán)境預(yù)置Git,簡單多了。
官方網(wǎng)站:http://www.pygit2.org/
參考
- 《GitPython Documentation》
- 《Welcome to GitDB’s documentation!》
- 《Git - 底層命令 (Plumbing) 和高層命令 (Porcelain)》
- 《GitPython | Hom》
附錄
在git.Repo中對context相關(guān)接口的實現(xiàn)如下:
def __enter__(self): return self ? def __exit__(self, exc_type, exc_value, traceback): self.close() ? def __del__(self): try: self.close() except: pass ? def close(self): if self.git: self.git.clear_cache() gc.collect() gitdb.util.mman.collect() gc.collect()
可見只是一些清理操作,關(guān)閉的必要性不高。 即使關(guān)閉,也仍然可以對這個git.Repo的instance進(jìn)行讀寫操作。