Python 命令行之旅:使用 click 實(shí)現(xiàn) git 命令
掃描二維碼
隨時(shí)隨地手機(jī)看文章
一、前言
在前面五篇介紹 click的文章中,我們?nèi)媪私饬?click的強(qiáng)大能力。按照慣例,我們要像使用 argparse和 docopt一樣使用 click來(lái)實(shí)現(xiàn) git 命令。
本文的關(guān)注點(diǎn)并不在 git的各種命令是如何實(shí)現(xiàn)的,而是怎么使用 click去打造一個(gè)實(shí)用命令行程序,代碼結(jié)構(gòu)是怎樣的。因此,和 git相關(guān)的操作,將會(huì)使用 gitpython庫(kù)來(lái)簡(jiǎn)單實(shí)現(xiàn)。
為了讓沒(méi)讀過(guò) 使用 xxx 實(shí)現(xiàn) git 命令(xxx指 argparse和 docopt) 的小伙伴也能讀明白本文,我們?nèi)詴?huì)對(duì) git常用命令和 gitpython做一個(gè)簡(jiǎn)單介紹。
本系列文章默認(rèn)使用 Python 3 作為解釋器進(jìn)行講解。
若你仍在使用 Python 2,請(qǐng)注意兩者之間語(yǔ)法和庫(kù)的使用差異哦~
二、git 常用命令
當(dāng)你寫好一段代碼或增刪一些文件后,會(huì)用如下命令查看文件狀態(tài):
git status
確認(rèn)文件狀態(tài)后,會(huì)用如下命令將的一個(gè)或多個(gè)文件(夾)添加到暫存區(qū):
git add [pathspec [pathspec ...]]
然后使用如下命令提交信息:
git commit -m "your commit message"
最后使用如下命令將提交推送到遠(yuǎn)程倉(cāng)庫(kù):
git push
我們將使用 click和 gitpython庫(kù)來(lái)實(shí)現(xiàn)這 4 個(gè)子命令。
三、關(guān)于 gitpython
gitpython 是一個(gè)和 git 倉(cāng)庫(kù)交互的 Python 第三方庫(kù)。我們將借用它的能力來(lái)實(shí)現(xiàn)真正的 git 邏輯。
安裝:
pip install gitpython
四、思考
在實(shí)現(xiàn)前,我們不妨先思考下會(huì)用到 click的哪些功能?整個(gè)程序的結(jié)構(gòu)是怎樣的?
click
git的 4 個(gè)子命令的實(shí)現(xiàn)其實(shí)對(duì)應(yīng)于四個(gè)函數(shù),每個(gè)函數(shù)使用 click的 command來(lái)裝飾。而對(duì)于 git add和 git commit,則分別需要表示參數(shù)的 click.argument和表示選項(xiàng)的 click.option來(lái)裝飾。
程序結(jié)構(gòu)
程序結(jié)構(gòu)上:
-
實(shí)例化 Git對(duì)象,供全局使用
-
定義 cli函數(shù)作為命令組,也就是整個(gè)命令程序的入口
-
定義四個(gè)命令對(duì)應(yīng)的實(shí)現(xiàn)函數(shù) status、add、commit、push
則基本結(jié)構(gòu)如下:
import os import click from git.cmd import Git
git = Git(os.getcwd()) @click.group() def cli(): """
git 命令行
""" pass @cli.command() def status(): """
處理 status 命令
""" pass @cli.command() @click.argument('pathspec', nargs=-1) def add(pathspec): """
處理 add 命令
""" pass @cli.command() @click.option('-m', 'msg') def commit(msg): """
處理 -m命令
""" pass @cli.command() def push(): """
處理 push 命令
""" pass if __name__ == '__main__':
cli()
下面我們將一步步地實(shí)現(xiàn)我們的 git程序。
五、實(shí)現(xiàn)
假定我們?cè)?click-git.py 文件中實(shí)現(xiàn)我們的 git程序。
5.1 status 子命令
status子命令不接受任何參數(shù)和選項(xiàng),因此其實(shí)現(xiàn)函數(shù)只需 cli.command()裝飾。
@cli.command() def status(): """
處理 status 命令
""" cmd = ['git', 'status']
output = git.execute(cmd)
click.echo(output)
不難看出,我們最后調(diào)用了真正的 git status來(lái)實(shí)現(xiàn),并打印了輸出。
5.2 add 子命令
add子命令相對(duì)于 status子命令,需要接受任意個(gè) pathspec 參數(shù),因此增加一個(gè) click.argument裝飾器,并且在 add函數(shù)中需要增加同名的 pathspec入?yún)?。?jīng) click處理后的 pathspec其實(shí)是個(gè)元組,和列表相加前,需要先轉(zhuǎn)換為列表。
@cli.command() @click.argument('pathspec', nargs=-1) def add(pathspec): """
處理 add 命令
""" cmd = ['git', 'add'] + list(pathspec)
output = git.execute(cmd)
click.echo(output)
當(dāng)我們執(zhí)行 python3 click-git.py add --help時(shí),結(jié)果如下:
Usage: click-git.py add [OPTIONS] [PATHSPEC]...
處理 add 命令
Options:
--help Show this message and exit.
既然 git add能接受任意多個(gè) pathspec,那么 add(pathspec)的參數(shù)其實(shí)改為復(fù)數(shù)形式更為合適,但我們又希望幫助信息中是單數(shù)形式,這就需要額外指定 metavar,則有:
@cli.command() @click.argument('pathspecs', nargs=-1, metavar='[PATHSPEC]...') def add(pathspecs): """
處理 add 命令
""" cmd = ['git', 'add'] + list(pathspecs)
output = git.execute(cmd)
click.echo(output)
5.3 commit 子命令
add子命令相對(duì)于 status子命令,需要接受 -m選項(xiàng),因此增加一個(gè) click.option裝飾器,指定選項(xiàng)名稱 msg,并且在 commit函數(shù)中增加同名入?yún)ⅰ?
@cli.command() @click.option('-m', 'msg') def commit(msg): """
處理 -m命令
""" cmd = ['git', 'commit', '-m', msg]
output = git.execute(cmd)
click.echo(output)
5.4 push 子命令
push子命令同 status子命令一樣,不接受任何參數(shù)和選項(xiàng),因此其實(shí)現(xiàn)函數(shù)只需 cli.command()裝飾。
@cli.command() def push(): """
處理 push 命令
""" cmd = ['git', 'push']
output = git.execute(cmd)
click.echo(output)
至此,我們就實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的 git命令行,使用 python click-git.py status便可查詢項(xiàng)目狀態(tài)。
非常方便的是,每個(gè)命令函數(shù)的 docstring都將作為這個(gè)命令的幫助信息,因此,當(dāng)我們執(zhí)行 python3 click-git.py --help會(huì)自動(dòng)生成如下幫助內(nèi)容:
Usage: click-git.py [OPTIONS] COMMAND [ARGS]...
git 命令行
Options:
--help Show this message and exit.
Commands:
add 處理 add 命令
commit 處理 -m命令
push 處理 push 命令
status 處理 status 命令
想看整個(gè)源碼,請(qǐng)戳 click-git.py 。
六、小結(jié)
本文簡(jiǎn)單介紹了日常工作中常用的 git命令,然后提出實(shí)現(xiàn)它的思路,最終一步步地使用 click和 gitpython實(shí)現(xiàn)了 git程序。
對(duì)比 argparse和 click的實(shí)現(xiàn)版本,你會(huì)發(fā)現(xiàn)使用 click來(lái)實(shí)現(xiàn)變得特定簡(jiǎn)單:
-
相較于 argparse,子解析器、參數(shù)類型什么的統(tǒng)統(tǒng)不需要關(guān)心
-
相較于 docopt,參數(shù)解析和命令調(diào)用處理也不需要關(guān)心
這無(wú)疑是 click最大的優(yōu)勢(shì)了。
關(guān)于 click的講解將告一段落,回顧下 click的至簡(jiǎn)之道,你會(huì)愛(ài)上它。
現(xiàn)在,你已學(xué)會(huì)了三個(gè)命令行解析庫(kù)的使用了。但你以為這就夠了嗎?click已經(jīng)夠簡(jiǎn)單了吧,夠直接了吧?但它仍然不是最簡(jiǎn)單的。
在下篇文章中,將為大家介紹一個(gè)由谷歌出品的在 Python 界很火的命令行庫(kù) —— fire。





