重磅干貨,第一時(shí)間送達(dá)
作者:靜默虛空
juejin.im/post/5c8296f85188257e3941b2d4
Git 是一個(gè)開源的分布式版本控制系統(tǒng)。
版本控制是一種記錄一個(gè)或若干文件內(nèi)容變化,以便將來查閱特定版本修訂情況的系統(tǒng)。
介紹分布式版本控制系統(tǒng)前,有必要先了解一下傳統(tǒng)的集中式版本控制系統(tǒng)。
集中化的版本控制系統(tǒng),諸如 CVS,Subversion 等,都有一個(gè)單一的集中管理的服務(wù)器,保存所有文件的修訂版本,而協(xié)同工作的人們都通過客戶端連到這臺服務(wù)器,取出最新的文件或者提交更新。
這么做最顯而易見的缺點(diǎn)是中央服務(wù)器的單點(diǎn)故障。如果宕機(jī)一小時(shí),那么在這一小時(shí)內(nèi),誰都無法提交更新,也就無法協(xié)同工作。要是中央服務(wù)器的磁盤發(fā)生故障,碰巧沒做備份,或者備份不夠及時(shí),就會有丟失數(shù)據(jù)的風(fēng)險(xiǎn)。最壞的情況是徹底丟失整個(gè)項(xiàng)目的所有歷史更改記錄。
分布式版本控制系統(tǒng)的客戶端并不只提取最新版本的文件快照,而是把代碼倉庫完整地鏡像下來。這么一來,任何一處協(xié)同工作用的服務(wù)器發(fā)生故障,事后都可以用任何一個(gè)鏡像出來的本地倉庫恢復(fù)。因?yàn)槊恳淮蔚奶崛〔僮?,?shí)際上都是一次對代碼倉庫的完整備份。
Git 是分布式的。這是 Git 和其它非分布式的版本控制系統(tǒng),例如 svn,cvs 等,最核心的區(qū)別。分布式帶來以下好處:
工作時(shí)不需要聯(lián)網(wǎng)
首先,分布式版本控制系統(tǒng)根本沒有“中央服務(wù)器”,每個(gè)人的電腦上都是一個(gè)完整的版本庫,這樣,你工作的時(shí)候,就不需要聯(lián)網(wǎng)了,因?yàn)榘姹編炀驮谀阕约旱碾娔X上。既然每個(gè)人電腦上都有一個(gè)完整的版本庫,那多個(gè)人如何協(xié)作呢?比方說你在自己電腦上改了文件 A,你的同事也在他的電腦上改了文件 A,這時(shí),你們倆之間只需把各自的修改推送給對方,就可以互相看到對方的修改了。
更加安全
集中式版本控制系統(tǒng),一旦中央服務(wù)器出了問題,所有人都無法工作。
分布式版本控制系統(tǒng),每個(gè)人電腦中都有完整的版本庫,所以某人的機(jī)器掛了,并不影響其它人。
Debian/Ubuntu 環(huán)境安裝
如果你使用的系統(tǒng)是 Debian/Ubuntu , 安裝命令為:
$ apt-get install libcurl4-gnutls-dev libexpat1-dev gettext \
> libz-dev libssl-dev
$ apt-get install git-core
$ git --version
git version 1.8.1.2
Centos/RedHat 環(huán)境安裝
如果你使用的系統(tǒng)是 Centos/RedHat ,安裝命令為:
$ yum install curl-devel expat-devel gettext-devel \
> openssl-devel zlib-devel
$ yum -y install git-core
$ git --version
git version 1.7.1
Windows 環(huán)境安裝
在Git 官方下載地址下載 exe 安裝包。按照安裝向?qū)О惭b即可。
建議安裝 Git Bash 這個(gè) git 的命令行工具。
Mac 環(huán)境安裝
在Git 官方下載地址下載 mac 安裝包。按照安裝向?qū)О惭b即可。
https://git-scm.com/downloads
Git 自帶一個(gè) git config
的工具來幫助設(shè)置控制 Git 外觀和行為的配置變量。這些變量存儲在三個(gè)不同的位置:
/etc/gitconfig
文件: 包含系統(tǒng)上每一個(gè)用戶及他們倉庫的通用配置。如果使用帶有 --system
選項(xiàng)的 git config
時(shí),它會從此文件讀寫配置變量。
~/.gitconfig
或 ~/.config/git/config
文件:只針對當(dāng)前用戶。可以傳遞 --global
選項(xiàng)讓 Git 讀寫此文件。
當(dāng)前使用倉庫的 Git 目錄中的 config
文件(就是 .git/config
):針對該倉庫。
每一個(gè)級別覆蓋上一級別的配置,所以 .git/config
的配置變量會覆蓋 /etc/gitconfig
中的配置變量。
在 Windows 系統(tǒng)中,Git 會查找 $HOME
目錄下(一般情況下是 C:\Users\$USER
)的 .gitconfig
文件。Git 同樣也會尋找 /etc/gitconfig
文件,但只限于 MSys 的根目錄下,即安裝 Git 時(shí)所選的目標(biāo)位置。
當(dāng)安裝完 Git 應(yīng)該做的第一件事就是設(shè)置你的用戶名稱與郵件地址。這樣做很重要,因?yàn)槊恳粋€(gè) Git 的提交都會使用這些信息,并且它會寫入到你的每一次提交中,不可更改:
$ git config --global user.name 'John Doe'
$ git config --global user.email johndoe@example.com
再次強(qiáng)調(diào),如果使用了 --global
選項(xiàng),那么該命令只需要運(yùn)行一次,因?yàn)橹鬅o論你在該系統(tǒng)上做任何事情, Git 都會使用那些信息。當(dāng)你想針對特定項(xiàng)目使用不同的用戶名稱與郵件地址時(shí),可以在那個(gè)項(xiàng)目目錄下運(yùn)行沒有 --global
選項(xiàng)的命令來配置。
很多 GUI 工具都會在第一次運(yùn)行時(shí)幫助你配置這些信息。
.gitignore
文件可能從字面含義也不難猜出:這個(gè)文件里配置的文件或目錄,會自動被 git 所忽略,不納入版本控制。
在日常開發(fā)中,我們的項(xiàng)目經(jīng)常會產(chǎn)生一些臨時(shí)文件,如編譯 Java 產(chǎn)生的 *.class
文件,又或是 IDE 自動生成的隱藏目錄(Intellij 的 .idea
目錄、Eclipse 的 .settings
目錄等)等等。這些文件或目錄實(shí)在沒必要納入版本管理。在這種場景下,你就需要用到 .gitignore
配置來過濾這些文件或目錄。
配置的規(guī)則很簡單,也沒什么可說的,看幾個(gè)例子,自然就明白了。
這里推薦一下 Github 的開源項(xiàng)目:https://github.com/github/gitignore
在這里,你可以找到很多常用的模板,如:Java、Nodejs、C++ 的 .gitignore
模板等等。
個(gè)人認(rèn)為,對于 Git 這個(gè)版本工具,再不了解原理的情況下,直接去學(xué)習(xí)命令行,可能會一頭霧水。所以,本文特意將原理放在命令使用章節(jié)之前講解??梢詤⒖迹?a target="_blank" >Git原理入門解析
當(dāng)你一個(gè)項(xiàng)目到本地或創(chuàng)建一個(gè) git 項(xiàng)目,項(xiàng)目目錄下會有一個(gè)隱藏的 .git
子目錄。這個(gè)目錄是 git 用來跟蹤管理版本庫的,千萬不要手動修改。
Git 中所有數(shù)據(jù)在存儲前都計(jì)算校驗(yàn)和,然后以校驗(yàn)和來引用。這意味著不可能在 Git 不知情時(shí)更改任何文件內(nèi)容或目錄內(nèi)容。這個(gè)功能建構(gòu)在 Git 底層,是構(gòu)成 Git 哲學(xué)不可或缺的部分。若你在傳送過程中丟失信息或損壞文件,Git 就能發(fā)現(xiàn)。
Git 用以計(jì)算校驗(yàn)和的機(jī)制叫做 SHA-1 散列(hash,哈希)。這是一個(gè)由 40 個(gè)十六進(jìn)制字符(0-9 和 a-f)組成字符串,基于 Git 中文件的內(nèi)容或目錄結(jié)構(gòu)計(jì)算出來。SHA-1 哈??雌饋硎沁@樣:
24b9da6552252987aa493b52f8696cd6d3b00373
Git 中使用這種哈希值的情況很多,你將經(jīng)??吹竭@種哈希值。實(shí)際上,Git 數(shù)據(jù)庫中保存的信息都是以文件內(nèi)容的哈希值來索引,而不是文件名。
在 GIt 中,你的文件可能會處于三種狀態(tài)之一:
已修改(modified) - 已修改表示修改了文件,但還沒保存到數(shù)據(jù)庫中。
已暫存(staged) - 已暫存表示對一個(gè)已修改文件的當(dāng)前版本做了標(biāo)記,使之包含在下次提交的快照中。
已提交(committed) - 已提交表示數(shù)據(jù)已經(jīng)安全的保存在本地?cái)?shù)據(jù)庫中。
與文件狀態(tài)對應(yīng)的,不同狀態(tài)的文件在 Git 中處于不同的工作區(qū)域。
工作區(qū)(working) - 當(dāng)你 git clone
一個(gè)項(xiàng)目到本地,相當(dāng)于在本地克隆了項(xiàng)目的一個(gè)副本。工作區(qū)是對項(xiàng)目的某個(gè)版本獨(dú)立提取出來的內(nèi)容。這些從 Git 倉庫的壓縮數(shù)據(jù)庫中提取出來的文件,放在磁盤上供你使用或修改。
暫存區(qū)(staging) - 暫存區(qū)是一個(gè)文件,保存了下次將提交的文件列表信息,一般在 Git 倉庫目錄中。有時(shí)候也被稱作索引
,不過一般說法還是叫暫存區(qū)。
本地倉庫(local) - 提交更新,找到暫存區(qū)域的文件,將快照永久性存儲到 Git 本地倉庫。
遠(yuǎn)程倉庫(remote) - 以上幾個(gè)工作區(qū)都是在本地。為了讓別人可以看到你的修改,你需要將你的更新推送到遠(yuǎn)程倉庫。同理,如果你想同步別人的修改,你需要從遠(yuǎn)程倉庫拉取更新。
國外網(wǎng)友制作了一張 Git Cheat Sheet,總結(jié)很精煉,各位不妨收藏一下。
本節(jié)選擇性介紹 git 中比較常用的命令行場景。
克隆一個(gè)已創(chuàng)建的倉庫:
# 通過 SSH
$ git clone ssh://user@domain.com/repo.git
#通過 HTTP
$ git clone http://domain.com/user/repo.git
創(chuàng)建一個(gè)新的本地倉庫:
$ git init
添加修改到暫存區(qū):
# 把指定文件添加到暫存區(qū)
$ git add xxx
# 把當(dāng)前所有修改添加到暫存區(qū)
$ git add .
# 把所有修改添加到暫存區(qū)
$ git add -A
提交修改到本地倉庫:
# 提交本地的所有修改
$ git commit -a
# 提交之前已標(biāo)記的變化
$ git commit
# 附加消息提交
$ git commit -m 'commit message'
有時(shí),我們需要在同一個(gè)項(xiàng)目的不同分支上工作。當(dāng)需要切換分支時(shí),偏偏本地的工作還沒有完成,此時(shí),提交修改顯得不嚴(yán)謹(jǐn),但是不提交代碼又無法切換分支。這時(shí),你可以使用 git stash
將本地的修改內(nèi)容作為草稿儲藏起來。
官方稱之為儲藏,但我個(gè)人更喜歡稱之為存草稿。
# 1. 將修改作為當(dāng)前分支的草稿保存
$ git stash
# 2. 查看草稿列表
$ git stash list
stash@{0}: WIP on master: 6fae349 :memo: Writing docs.
# 3.1 刪除草稿
$ git stash drop stash@{0}
# 3.2 讀取草稿
$ git stash apply stash@{0}
撤銷本地修改:
# 移除緩存區(qū)的所有文件(i.e. 撤銷上次git add)
$ git reset HEAD
# 將HEAD重置到上一次提交的版本,并將之后的修改標(biāo)記為未添加到緩存區(qū)的修改
$ git reset <commit>
# 將HEAD重置到上一次提交的版本,并保留未提交的本地修改
$ git reset --keep <commit>
# 放棄工作目錄下的所有修改
$ git reset --hard HEAD
# 將HEAD重置到指定的版本,并拋棄該版本之后的所有修改
$ git reset --hard <commit-hash>
# 用遠(yuǎn)端分支強(qiáng)制覆蓋本地分支
$ git reset --hard <remote/branch> e.g., upstream/master, origin/my-feature
# 放棄某個(gè)文件的所有本地修改
$ git checkout HEAD <file>
刪除添加.gitignore
文件前錯(cuò)誤提交的文件:
$ git rm -r --cached .
$ git add .
$ git commit -m 'remove xyz file'
撤銷遠(yuǎn)程修改(創(chuàng)建一個(gè)新的提交,并回滾到指定版本):
$ git revert <commit-hash>
徹底刪除指定版本:
# 執(zhí)行下面命令后,commit-hash 提交后的記錄都會被徹底刪除,使用需謹(jǐn)慎
$ git reset --hard <commit-hash>
$ git push -f
更新:
# 下載遠(yuǎn)程端版本,但不合并到HEAD中
$ git fetch <remote>
# 將遠(yuǎn)程端版本合并到本地版本中
$ git pull origin master
# 以rebase方式將遠(yuǎn)端分支與本地合并
$ git pull --rebase <remote> <branch>
推送:
# 將本地版本推送到遠(yuǎn)程端
$ git push remote <remote> <branch>
# 刪除遠(yuǎn)程端分支
$ git push <remote> :<branch> (since Git v1.5.0)
$ git push <remote> --delete <branch> (since Git v1.7.0)
# 發(fā)布標(biāo)簽
$ git push --tags
顯示工作路徑下已修改的文件:
$ git status
顯示與上次提交版本文件的不同:
$ git diff
顯示提交歷史:
# 從最新提交開始,顯示所有的提交記錄(顯示hash, 作者信息,提交的標(biāo)題和時(shí)間)
$ git log
# 顯示某個(gè)用戶的所有提交
$ git log --author='username'
# 顯示某個(gè)文件的所有修改
$ git log -p <file>
顯示搜索內(nèi)容:
# 從當(dāng)前目錄的所有文件中查找文本內(nèi)容
$ git grep 'Hello'
# 在某一版本中搜索文本
$ git grep 'Hello' v2.5
增刪查分支:
# 列出所有的分支
$ git branch
# 列出所有的遠(yuǎn)端分支
$ git branch -r
# 基于當(dāng)前分支創(chuàng)建新分支
$ git branch <new-branch>
# 基于遠(yuǎn)程分支創(chuàng)建新的可追溯的分支
$ git branch --track <new-branch> <remote-branch>
# 刪除本地分支
$ git branch -d <branch>
# 強(qiáng)制刪除本地分支,將會丟失未合并的修改
$ git branch -D <branch>
切換分支:
# 切換分支
$ git checkout <branch>
# 創(chuàng)建并切換到新分支
$ git checkout -b <branch>
# 給當(dāng)前版本打標(biāo)簽
$ git tag <tag-name>
# 給當(dāng)前版本打標(biāo)簽并附加消息
$ git tag -a <tag-name>
merge 與 rebase 雖然是 git 常用功能,但是強(qiáng)烈建議不要使用 git 命令來完成這項(xiàng)工作。
因?yàn)槿绻霈F(xiàn)代碼沖突,在沒有代碼比對工具的情況下,實(shí)在太艱難了。
你可以考慮使用各種 Git GUI 工具。
合并:
# 將分支合并到當(dāng)前HEAD中
$ git merge <branch>
重置:
# 將當(dāng)前HEAD版本重置到分支中,請勿重置已發(fā)布的提交
$ git rebase <branch>
Github 作為最著名的代碼開源協(xié)作社區(qū),在程序員圈想必?zé)o人不知,無人不曉。關(guān)于Git和Github你不知道的十件事
這里不贅述 Github 的用法,確實(shí)有不會用的新手同學(xué),可以參考官方教程:
https://guides.github.com/
Git 支持三種協(xié)議:HTTPS / SSH / GIT
而 Github 上支持 HTTPS 和 SSH。
HTTPS 這種方式要求你每次 push 時(shí)都要輸入用戶名、密碼,有些繁瑣。
而 SSH 要求你本地生成證書,然后在你的 Github 賬戶中注冊。第一次配置麻煩是麻煩了點(diǎn),但是以后就免去了每次 push 需要輸入用戶名、密碼的繁瑣。
以下介紹一下,如何生成證書,以及在 Github 中注冊。
如前所述,許多 Git 服務(wù)器都使用 SSH 公鑰進(jìn)行認(rèn)證。為了向 Git 服務(wù)器提供 SSH 公鑰,如果某系統(tǒng)用戶尚未擁有密鑰,必須事先為其生成一份。這個(gè)過程在所有操作系統(tǒng)上都是相似的。首先,你需要確認(rèn)自己是否已經(jīng)擁有密鑰。默認(rèn)情況下,用戶的 SSH 密鑰存儲在其 \~/.ssh
目錄下。進(jìn)入該目錄并列出其中內(nèi)容,你便可以快速確認(rèn)自己是否已擁有密鑰:
$ cd ~/.ssh
$ ls
authorized_keys2 id_dsa known_hosts
config id_dsa.pub
我們需要尋找一對以 id_dsa
或 id_rsa
命名的文件,其中一個(gè)帶有 .pub
擴(kuò)展名。 .pub
文件是你的公鑰,另一個(gè)則是私鑰。如果找不到這樣的文件(或者根本沒有 .ssh
目錄),你可以通過運(yùn)行 ssh-keygen
程序來創(chuàng)建它們。在 Linux/Mac 系統(tǒng)中,ssh-keygen
隨 SSH 軟件包提供;在 Windows 上,該程序包含于 MSysGit 軟件包中。
$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/schacon/.ssh/id_rsa):
Created directory '/home/schacon/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/schacon/.ssh/id_rsa.
Your public key has been saved in /home/schacon/.ssh/id_rsa.pub.
The key fingerprint is:
d0:82:24:8e:d7:f1:bb:9b:33:53:96:93:49:da:9b:e3 schacon@mylaptop.local
首先 ssh-keygen
會確認(rèn)密鑰的存儲位置(默認(rèn)是 .ssh/id_rsa
),然后它會要求你輸入兩次密鑰口令。如果你不想在使用密鑰時(shí)輸入口令,將其留空即可。
現(xiàn)在,進(jìn)行了上述操作的用戶需要將各自的公鑰發(fā)送給任意一個(gè) Git 服務(wù)器管理員(假設(shè)服務(wù)器正在使用基于公鑰的 SSH 驗(yàn)證設(shè)置)。他們所要做的就是復(fù)制各自的 .pub
文件內(nèi)容,并將其通過郵件發(fā)送。公鑰看起來是這樣的:
$ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSU
GPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3
Pbv7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK/7XA
t3FaoJoAsncM1Q9x5+3V0Ww68/eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw/Pb0rwert/En
mZ+AW4OZPnTPI89ZPmVMLuayrD2cE86Z/il8b+gw3r3+1nKatmIkjn2so1d01QraTlMqVSsbx
NrRFi9wrf+M7Q== schacon@mylaptop.local
在你的 Github 賬戶中,依次點(diǎn)擊 Settings > SSH and GPG keys > New SSH key
然后,將上面生成的公鑰內(nèi)容粘貼到 Key
編輯框并保存。至此大功告成。
后面,你在克隆你的 Github 項(xiàng)目時(shí)使用 SSH 方式即可。
如果覺得我的講解還不夠細(xì)致,可以參考:https://help.github.com/articles/adding-a-new-ssh-key-to-your-github-account/
詳細(xì)內(nèi)容,可以參考這篇文章:Git 在團(tuán)隊(duì)中的最佳實(shí)踐--如何正確使用 Git Flow
https://www.cnblogs.com/cnblogsfans/p/5075073.html
Git 在實(shí)際開發(fā)中的最佳實(shí)踐策略 Git Flow 可以歸納為以下:
master
分支 - 也就是我們經(jīng)常使用的主線分支,這個(gè)分支是最近發(fā)布到生產(chǎn)環(huán)境的代碼,這個(gè)分支只能從其他分支合并,不能在這個(gè)分支直接修改。
develop
分支 - 這個(gè)分支是我們的主開發(fā)分支,包含所有要發(fā)布到下一個(gè) release 的代碼,這個(gè)分支主要是從其他分支合并代碼過來,比如 feature 分支。
feature
分支 - 這個(gè)分支主要是用來開發(fā)一個(gè)新的功能,一旦開發(fā)完成,我們合并回 develop 分支進(jìn)入下一個(gè) release。
release
分支 - 當(dāng)你需要一個(gè)發(fā)布一個(gè)新 release 的時(shí)候,我們基于 Develop 分支創(chuàng)建一個(gè) release 分支,完成 release 后,我們合并到 master 和 develop 分支。
hotfix
分支 - 當(dāng)我們在 master 發(fā)現(xiàn)新的 Bug 時(shí)候,我們需要創(chuàng)建一個(gè) hotfix, 完成 hotfix 后,我們合并回 master 和 develop 分支,所以 hotfix 的改動會進(jìn)入下一個(gè) release
如果你用 git commit -a
提交了一次變化(changes),而你又不確定到底這次提交了哪些內(nèi)容。你就可以用下面的命令顯示當(dāng)前HEAD
上的最近一次的提交(commit):
(master)$ git show
或者
$ git log -n1 -p
如果你的提交信息(commit message)寫錯(cuò)了且這次提交(commit)還沒有推(push), 你可以通過下面的方法來修改提交信息(commit message):
$ git commit --amend
這會打開你的默認(rèn)編輯器, 在這里你可以編輯信息. 另一方面, 你也可以用一條命令一次完成:
$ git commit --amend -m 'xxxxxxx'
如果你已經(jīng)推(push)了這次提交(commit), 你可以修改這次提交(commit)然后強(qiáng)推(force push), 但是不推薦這么做。
如果這只是單個(gè)提交(commit),修改它:
$ git commit --amend --author 'New Authorname <authoremail@mydomain.com>'
如果你需要修改所有歷史, 參考 'git filter-branch'的指南頁.
通過下面的方法,從一個(gè)提交(commit)里移除一個(gè)文件:
$ git checkout HEAD^ myfile
$ git add -A
$ git commit --amend
這將非常有用,當(dāng)你有一個(gè)開放的補(bǔ)丁(open patch),你往上面提交了一個(gè)不必要的文件,你需要強(qiáng)推(force push)去更新這個(gè)遠(yuǎn)程補(bǔ)丁。
如果你需要刪除推了的提交(pushed commits),你可以使用下面的方法??墒牵@會不可逆的改變你的歷史,也會搞亂那些已經(jīng)從該倉庫拉取(pulled)了的人的歷史。簡而言之,如果你不是很確定,千萬不要這么做。
$ git reset HEAD^ --hard
$ git push -f [remote] [branch]
如果你還沒有推到遠(yuǎn)程, 把 Git 重置(reset)到你最后一次提交前的狀態(tài)就可以了(同時(shí)保存暫存的變化):
(my-branch*)$ git reset --soft HEAD@{1}
這只能在沒有推送之前有用. 如果你已經(jīng)推了, 唯一安全能做的是 git revert SHAofBadCommit
, 那會創(chuàng)建一個(gè)新的提交(commit)用于撤消前一個(gè)提交的所有變化(changes);或者, 如果你推的這個(gè)分支是 rebase-safe 的 (例如:其它開發(fā)者不會從這個(gè)分支拉), 只需要使用 git push -f
;更多, 請參考 the above section。
https://juejin.im/post/5c8296f85188257e3941b2d4
同樣的警告:不到萬不得已的時(shí)候不要這么做.
$ git rebase --onto SHA1_OF_BAD_COMMIT^ SHA1_OF_BAD_COMMIT
$ git push -f [remote] [branch]
或者做一個(gè) 交互式 rebase 刪除那些你想要刪除的提交(commit)里所對應(yīng)的行。
To https://github.com/yourusername/repo.git
! [rejected] mybranch -> mybranch (non-fast-forward)
error: failed to push some refs to 'https://github.com/tanay1337/webmaker.org.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
注意, rebasing(見下面)和修正(amending)會用一個(gè)新的提交(commit)代替舊的, 所以如果之前你已經(jīng)往遠(yuǎn)程倉庫上推過一次修正前的提交(commit),那你現(xiàn)在就必須強(qiáng)推(force push) (-f
)。注意 – 總是 確保你指明一個(gè)分支!
(my-branch)$ git push origin mybranch -f
一般來說, 要避免強(qiáng)推. 最好是創(chuàng)建和推(push)一個(gè)新的提交(commit),而不是強(qiáng)推一個(gè)修正后的提交。后者會使那些與該分支或該分支的子分支工作的開發(fā)者,在源歷史中產(chǎn)生沖突。
如果你意外的做了 git reset --hard
, 你通常能找回你的提交(commit), 因?yàn)?Git 對每件事都會有日志,且都會保存幾天。
(master)$ git reflog
你將會看到一個(gè)你過去提交(commit)的列表, 和一個(gè)重置的提交。選擇你想要回到的提交(commit)的 SHA,再重置一次:
(master)$ git reset --hard SHA1234
這樣就完成了。
(my-branch*)$ git commit --amend
一般來說, 如果你想暫存一個(gè)文件的一部分, 你可這樣做:
$ git add --patch filename.x
-p
簡寫。這會打開交互模式, 你將能夠用 s
選項(xiàng)來分隔提交(commit);然而, 如果這個(gè)文件是新的, 會沒有這個(gè)選擇, 添加一個(gè)新文件時(shí), 這樣做:
$ git add -N filename.x
然后, 你需要用 e
選項(xiàng)來手動選擇需要添加的行,執(zhí)行 git diff --cached
將會顯示哪些行暫存了哪些行只是保存在本地了。
git add
會把整個(gè)文件加入到一個(gè)提交. git add -p
允許交互式的選擇你想要提交的部分.
這個(gè)有點(diǎn)困難, 我能想到的最好的方法是先 stash 未暫存的內(nèi)容, 然后重置(reset),再 pop 第一步 stashed 的內(nèi)容, 最后再 add 它們。
$ git stash -k
$ git reset --hard
$ git stash pop
$ git add -A
$ git checkout -b my-branch
$ git stash
$ git checkout my-branch
$ git stash pop
如果你只是想重置源(origin)和你本地(local)之間的一些提交(commit),你可以:
## one commit
(my-branch)$ git reset --hard HEAD^
## two commits
(my-branch)$ git reset --hard HEAD^^
## four commits
(my-branch)$ git reset --hard HEAD~4
## or
(master)$ git checkout -f
重置某個(gè)特殊的文件, 你可以用文件名做為參數(shù):
$ git reset filename
如果你想丟棄工作拷貝中的一部分內(nèi)容,而不是全部。
簽出(checkout)不需要的內(nèi)容,保留需要的。
$ git checkout -p
## Answer y to all of the snippets you want to drop
另外一個(gè)方法是使用 stash
, Stash 所有要保留下的內(nèi)容, 重置工作拷貝, 重新應(yīng)用保留的部分。
$ git stash -p
## Select all of the snippets you want to save
$ git reset --hard
$ git stash pop
或者, stash 你不需要的部分, 然后 stash drop。
$ git stash -p
## Select all of the snippets you don't want to save
$ git stash drop
這是另外一種使用 git reflog
情況,找到在這次錯(cuò)誤拉(pull) 之前 HEAD 的指向。
(master)$ git reflog
ab7555f HEAD@{0}: pull origin wrong-branch: Fast-forward
c5bc55a HEAD@{1}: checkout: checkout message goes here
重置分支到你所需的提交(desired commit):
$ git reset --hard c5bc55a
完成。
先確認(rèn)你沒有推(push)你的內(nèi)容到遠(yuǎn)程。
git status
會顯示你領(lǐng)先(ahead)源(origin)多少個(gè)提交:
(my-branch)$ git status
## On branch my-branch
## Your branch is ahead of 'origin/my-branch' by 2 commits.
## (use 'git push' to publish your local commits)
#
一種方法是:
(master)$ git reset --hard origin/my-branch
在 master 下創(chuàng)建一個(gè)新分支,不切換到新分支,仍在 master 下:
(master)$ git branch my-branch
把 master 分支重置到前一個(gè)提交:
(master)$ git reset --hard HEAD^
HEAD^
是 HEAD^1
的簡寫,你可以通過指定要設(shè)置的HEAD
來進(jìn)一步重置。
或者, 如果你不想使用 HEAD^
, 找到你想重置到的提交(commit)的 hash(git log
能夠完成), 然后重置到這個(gè) hash。使用git push
同步內(nèi)容到遠(yuǎn)程。
例如, master 分支想重置到的提交的 hash 為a13b85e
:
(master)$ git reset --hard a13b85e
HEAD is now at a13b85e
簽出(checkout)剛才新建的分支繼續(xù)工作:
(master)$ git checkout my-branch
假設(shè)你正在做一個(gè)原型方案(原文為 working spike (see note)), 有成百的內(nèi)容,每個(gè)都工作得很好。現(xiàn)在, 你提交到了一個(gè)分支,保存工作內(nèi)容:
(solution)$ git add -A && git commit -m 'Adding all changes from this spike into one big commit.'
當(dāng)你想要把它放到一個(gè)分支里 (可能是feature
, 或者 develop
), 你關(guān)心是保持整個(gè)文件的完整,你想要一個(gè)大的提交分隔成比較小。
假設(shè)你有:
分支 solution
, 擁有原型方案, 領(lǐng)先 develop
分支。
分支 develop
, 在這里你應(yīng)用原型方案的一些內(nèi)容。
我去可以通過把內(nèi)容拿到你的分支里,來解決這個(gè)問題:
(develop)$ git checkout solution -- file1.txt
這會把這個(gè)文件內(nèi)容從分支 solution
拿到分支 develop
里來:
## On branch develop
## Your branch is up-to-date with 'origin/develop'.
## Changes to be committed:
## (use 'git reset HEAD <file>...' to unstage)
#
## modified: file1.txt
然后, 正常提交。
Note: Spike solutions are made to analyze or solve the problem. These solutions are used for estimation and discarded once everyone gets clear visualization of the problem. ~ Wikipedia.
假設(shè)你有一個(gè)master
分支, 執(zhí)行git log
, 你看到你做過兩次提交:
(master)$ git log
commit e3851e817c451cc36f2e6f3049db528415e3c114
Author: Alex Lee <alexlee@example.com>
Date: Tue Jul 22 15:39:27 2014 -0400
Bug #21 - Added CSRF protection
commit 5ea51731d150f7ddc4a365437931cd8be3bf3131
Author: Alex Lee <alexlee@example.com>
Date: Tue Jul 22 15:39:12 2014 -0400
Bug #14 - Fixed spacing on title
commit a13b85e984171c6e2a1729bb061994525f626d14
Author: Aki Rose <akirose@example.com>
Date: Tue Jul 21 01:12:48 2014 -0400
First commit
讓我們用提交 hash(commit hash)標(biāo)記 bug (e3851e8
for #21, 5ea5173
for #14).
首先, 我們把master
分支重置到正確的提交(a13b85e
):
(master)$ git reset --hard a13b85e
HEAD is now at a13b85e
現(xiàn)在, 我們對 bug #21 創(chuàng)建一個(gè)新的分支:
(master)$ git checkout -b 21
(21)$
接著, 我們用 cherry-pick 把對 bug #21 的提交放入當(dāng)前分支。這意味著我們將應(yīng)用(apply)這個(gè)提交(commit),僅僅這一個(gè)提交(commit),直接在 HEAD 上面。
(21)$ git cherry-pick e3851e8
這時(shí)候, 這里可能會產(chǎn)生沖突, 參見交互式 rebasing 章 沖突節(jié) 解決沖突.
再者, 我們?yōu)?bug #14 創(chuàng)建一個(gè)新的分支, 也基于master
分支
(21)$ git checkout master
(master)$ git checkout -b 14
(14)$
最后, 為 bug #14 執(zhí)行 cherry-pick
:
(14)$ git cherry-pick 5ea5173
一旦你在 github 上面合并(merge)了一個(gè) pull request, 你就可以刪除你 fork 里被合并的分支。如果你不準(zhǔn)備繼續(xù)在這個(gè)分支里工作, 刪除這個(gè)分支的本地拷貝會更干凈,使你不會陷入工作分支和一堆陳舊分支的混亂之中。
$ git fetch -p
如果你定期推送到遠(yuǎn)程, 多數(shù)情況下應(yīng)該是安全的,但有些時(shí)候還是可能刪除了還沒有推到遠(yuǎn)程的分支。讓我們先創(chuàng)建一個(gè)分支和一個(gè)新的文件:
(master)$ git checkout -b my-branch
(my-branch)$ git branch
(my-branch)$ touch foo.txt
(my-branch)$ ls
README.md foo.txt
添加文件并做一次提交
(my-branch)$ git add .
(my-branch)$ git commit -m 'foo.txt added'
(my-branch)$ foo.txt added
1 files changed, 1 insertions(+)
create mode 100644 foo.txt
(my-branch)$ git log
commit 4e3cd85a670ced7cc17a2b5d8d3d809ac88d5012
Author: siemiatj <siemiatj@example.com>
Date: Wed Jul 30 00:34:10 2014 +0200
foo.txt added
commit 69204cdf0acbab201619d95ad8295928e7f411d5
Author: Kate Hudson <katehudson@example.com>
Date: Tue Jul 29 13:14:46 2014 -0400
Fixes #6: Force pushing after amending commits
現(xiàn)在我們切回到主(master)分支,‘不小心的’刪除my-branch
分支
(my-branch)$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
(master)$ git branch -D my-branch
Deleted branch my-branch (was 4e3cd85).
(master)$ echo oh noes, deleted my branch!
oh noes, deleted my branch!
在這時(shí)候你應(yīng)該想起了reflog
, 一個(gè)升級版的日志,它存儲了倉庫(repo)里面所有動作的歷史。
(master)$ git reflog
69204cd HEAD@{0}: checkout: moving from my-branch to master
4e3cd85 HEAD@{1}: commit: foo.txt added
69204cd HEAD@{2}: checkout: moving from master to my-branch
正如你所見,我們有一個(gè)來自刪除分支的提交 hash(commit hash),接下來看看是否能恢復(fù)刪除了的分支。
(master)$ git checkout -b my-branch-help
Switched to a new branch 'my-branch-help'
(my-branch-help)$ git reset --hard 4e3cd85
HEAD is now at 4e3cd85 foo.txt added
(my-branch-help)$ ls
README.md foo.txt
看! 我們把刪除的文件找回來了。Git 的 reflog
在 rebasing 出錯(cuò)的時(shí)候也是同樣有用的。
刪除一個(gè)遠(yuǎn)程分支:
(master)$ git push origin --delete my-branch
你也可以:
(master)$ git push origin :my-branch
刪除一個(gè)本地分支:
(master)$ git branch -D my-branch
首先, 從遠(yuǎn)程拉取(fetch) 所有分支:
(master)$ git fetch --all
假設(shè)你想要從遠(yuǎn)程的daves
分支簽出到本地的daves
(master)$ git checkout --track origin/daves
Branch daves set up to track remote branch daves from origin.
Switched to a new branch 'daves'
(--track
是 git checkout -b [branch] [remotename]/[branch]
的簡寫)
這樣就得到了一個(gè)daves
分支的本地拷貝, 任何推過(pushed)的更新,遠(yuǎn)程都能看到.
你可以合并(merge)或 rebase 了一個(gè)錯(cuò)誤的分支, 或者完成不了一個(gè)進(jìn)行中的 rebase/merge。Git 在進(jìn)行危險(xiǎn)操作的時(shí)候會把原始的 HEAD 保存在一個(gè)叫 ORIG_HEAD 的變量里, 所以要把分支恢復(fù)到 rebase/merge 前的狀態(tài)是很容易的。
(my-branch)$ git reset --hard ORIG_HEAD
不幸的是,如果你想把這些變化(changes)反應(yīng)到遠(yuǎn)程分支上,你就必須得強(qiáng)推(force push)。是因你快進(jìn)(Fast forward)了提交,改變了 Git 歷史, 遠(yuǎn)程分支不會接受變化(changes),除非強(qiáng)推(force push)。這就是許多人使用 merge 工作流, 而不是 rebasing 工作流的主要原因之一, 開發(fā)者的強(qiáng)推(force push)會使大的團(tuán)隊(duì)陷入麻煩。使用時(shí)需要注意,一種安全使用 rebase 的方法是,不要把你的變化(changes)反映到遠(yuǎn)程分支上, 而是按下面的做:
(master)$ git checkout my-branch
(my-branch)$ git rebase -i master
(my-branch)$ git checkout master
(master)$ git merge --ff-only my-branch
更多, 參見 this SO thread.
http://stackoverflow.com/questions/11058312/how-can-i-use-git-rebase-without-requiring-a-forced-push
假設(shè)你的工作分支將會做對于 master
的 pull-request。一般情況下你不關(guān)心提交(commit)的時(shí)間戳,只想組合 所有 提交(commit) 到一個(gè)單獨(dú)的里面, 然后重置(reset)重提交(recommit)。確保主(master)分支是最新的和你的變化都已經(jīng)提交了, 然后:
(my-branch)$ git reset --soft master
(my-branch)$ git commit -am 'New awesome feature'
如果你想要更多的控制, 想要保留時(shí)間戳, 你需要做交互式 rebase (interactive rebase):
(my-branch)$ git rebase -i master
如果沒有相對的其它分支, 你將不得不相對自己的HEAD
進(jìn)行 rebase。例如:你想組合最近的兩次提交(commit), 你將相對于HEAD\~2
進(jìn)行 rebase, 組合最近 3 次提交(commit), 相對于HEAD\~3
, 等等。
(master)$ git rebase -i HEAD~2
在你執(zhí)行了交互式 rebase 的命令(interactive rebase command)后, 你將在你的編輯器里看到類似下面的內(nèi)容:
pick a9c8a1d Some refactoring
pick 01b2fd8 New awesome feature
pick b729ad5 fixup
pick e3851e8 another fix
## Rebase 8074d12..b729ad5 onto 8074d12
#
## Commands:
## p, pick = use commit
## r, reword = use commit, but edit the commit message
## e, edit = use commit, but stop for amending
## s, squash = use commit, but meld into previous commit
## f, fixup = like 'squash', but discard this commit's log message
## x, exec = run command (the rest of the line) using shell
#
## These lines can be re-ordered; they are executed from top to bottom.
#
## If you remove a line here THAT COMMIT WILL BE LOST.
#
## However, if you remove everything, the rebase will be aborted.
#
## Note that empty commits are commented out
所有以 #
開頭的行都是注釋, 不會影響 rebase.
然后,你可以用任何上面命令列表的命令替換 pick
, 你也可以通過刪除對應(yīng)的行來刪除一個(gè)提交(commit)。
例如, 如果你想 單獨(dú)保留最舊(first)的提交(commit),組合所有剩下的到第二個(gè)里面, 你就應(yīng)該編輯第二個(gè)提交(commit)后面的每個(gè)提交(commit) 前的單詞為 f
:
pick a9c8a1d Some refactoring
pick 01b2fd8 New awesome feature
f b729ad5 fixup
f e3851e8 another fix
如果你想組合這些提交(commit) 并重命名這個(gè)提交(commit), 你應(yīng)該在第二個(gè)提交(commit)旁邊添加一個(gè)r
,或者更簡單的用s
替代 f
:
pick a9c8a1d Some refactoring
pick 01b2fd8 New awesome feature
s b729ad5 fixup
s e3851e8 another fix
你可以在接下來彈出的文本提示框里重命名提交(commit)。
Newer, awesomer features
## Please enter the commit message for your changes. Lines starting
## with '#' will be ignored, and an empty message aborts the commit.
## rebase in progress; onto 8074d12
## You are currently editing a commit while rebasing branch 'master' on '8074d12'.
#
## Changes to be committed:
# modified: README.md
#
如果成功了, 你應(yīng)該看到類似下面的內(nèi)容:
(master)$ Successfully rebased and updated refs/heads/master.
--no-commit
執(zhí)行合并(merge)但不自動提交, 給用戶在做提交前檢查和修改的機(jī)會。 no-ff
會為特性分支(feature branch)的存在過留下證據(jù), 保持項(xiàng)目歷史一致。
(master)$ git merge --no-ff --no-commit my-branch
(master)$ git merge --squash my-branch
有時(shí)候,在將數(shù)據(jù)推向上游之前,你有幾個(gè)正在進(jìn)行的工作提交(commit)。這時(shí)候不希望把已經(jīng)推(push)過的組合進(jìn)來,因?yàn)槠渌丝赡芤呀?jīng)有提交(commit)引用它們了。
(master)$ git rebase -i @{u}
這會產(chǎn)生一次交互式的 rebase(interactive rebase), 只會列出沒有推(push)的提交(commit), 在這個(gè)列表時(shí)進(jìn)行 reorder/fix/squash 都是安全的。
檢查一個(gè)分支上的所有提交(commit)是否都已經(jīng)合并(merge)到了其它分支, 你應(yīng)該在這些分支的 head(或任何 commits)之間做一次 diff:
(master)$ git log --graph --left-right --cherry-pick --oneline HEAD...feature/120-on-scroll
這會告訴你在一個(gè)分支里有而另一個(gè)分支沒有的所有提交(commit), 和分支之間不共享的提交(commit)的列表。另一個(gè)做法可以是:
(master)$ git log master ^feature/120-on-scroll --no-merges
如果你看到的是這樣:
noop
這意味著你 rebase 的分支和當(dāng)前分支在同一個(gè)提交(commit)上, 或者 領(lǐng)先(ahead) 當(dāng)前分支。你可以嘗試:
檢查確保主(master)分支沒有問題
rebase HEAD~2
或者更早
如果你不能成功的完成 rebase, 你可能必須要解決沖突。
首先執(zhí)行 git status
找出哪些文件有沖突:
(my-branch)$ git status
On branch my-branch
Changes not staged for commit:
(use 'git add <file>...' to update what will be committed)
(use 'git checkout -- <file>...' to discard changes in working directory)
modified: README.md
在這個(gè)例子里面, README.md
有沖突。打開這個(gè)文件找到類似下面的內(nèi)容:
<<<<<<< HEAD
some code
=========
some code
>>>>>>> new-commit
你需要解決新提交的代碼(示例里, 從中間==
線到new-commit
的地方)與HEAD
之間不一樣的地方.
有時(shí)候這些合并非常復(fù)雜,你應(yīng)該使用可視化的差異編輯器(visual diff editor):
(master*)$ git mergetool -t opendiff
在你解決完所有沖突和測試過后, git add
變化了的(changed)文件, 然后用git rebase --continue
繼續(xù) rebase。
(my-branch)$ git add README.md
(my-branch)$ git rebase --continue
如果在解決完所有的沖突過后,得到了與提交前一樣的結(jié)果, 可以執(zhí)行git rebase --skip
。
任何時(shí)候你想結(jié)束整個(gè) rebase 過程,回來 rebase 前的分支狀態(tài), 你可以做:
(my-branch)$ git rebase --abort
$ git clone --recursive git://github.com/foo/bar.git
如果已經(jīng)克隆了:
$ git submodule update --init --recursive
$ git tag -d <tag_name>
$ git push <remote> :refs/tags/<tag_name>
如果你想恢復(fù)一個(gè)已刪除標(biāo)簽(tag), 可以按照下面的步驟: 首先, 需要找到無法訪問的標(biāo)簽(unreachable tag):
$ git fsck --unreachable | grep tag
記下這個(gè)標(biāo)簽(tag)的 hash,然后用 Git 的 update-ref:
$ git update-ref refs/tags/<tag_name> <hash>
這時(shí)你的標(biāo)簽(tag)應(yīng)該已經(jīng)恢復(fù)了。
如果某人在 GitHub 上給你發(fā)了一個(gè) pull request, 但是然后他刪除了他自己的原始 fork, 你將沒法克隆他們的提交(commit)或使用 git am
。在這種情況下, 最好手動的查看他們的提交(commit),并把它們拷貝到一個(gè)本地新分支,然后做提交。
做完提交后, 再修改作者,參見變更作者。然后, 應(yīng)用變化, 再發(fā)起一個(gè)新的 pull request。
(master)$ git mv --force myfile MyFile
(master)$ git rm --cached log.txt
在 OS X 和 Linux 下, 你的 Git 的配置文件儲存在 \~/.gitconfig
。我在[alias]
部分添加了一些快捷別名(和一些我容易拼寫錯(cuò)誤的),如下:
[alias]
a = add
amend = commit --amend
c = commit
ca = commit --amend
ci = commit -a
co = checkout
d = diff
dc = diff --changed
ds = diff --staged
f = fetch
loll = log --graph --decorate --pretty=oneline --abbrev-commit
m = merge
one = log --pretty=oneline
outstanding = rebase -i @{u}
s = status
unpushed = log @{u}
wc = whatchanged
wip = rebase -i @{u}
zap = fetch -p
你可能有一個(gè)倉庫需要授權(quán),這時(shí)你可以緩存用戶名和密碼,而不用每次推/拉(push/pull)的時(shí)候都輸入,Credential helper 能幫你。
$ git config --global credential.helper cache
## Set git to use the credential memory cache
$ git config --global credential.helper 'cache --timeout=3600'
## Set the cache to timeout after 1 hour (setting is in seconds)
你把事情搞砸了:你 重置(reset)
了一些東西, 或者你合并了錯(cuò)誤的分支, 亦或你強(qiáng)推了后找不到你自己的提交(commit)了。有些時(shí)候, 你一直都做得很好, 但你想回到以前的某個(gè)狀態(tài)。
這就是 git reflog
的目的, reflog
記錄對分支頂端(the tip of a branch)的任何改變, 即使那個(gè)頂端沒有被任何分支或標(biāo)簽引用。基本上, 每次 HEAD 的改變, 一條新的記錄就會增加到reflog
。遺憾的是,這只對本地分支起作用,且它只跟蹤動作 (例如,不會跟蹤一個(gè)沒有被記錄的文件的任何改變)。
(master)$ git reflog
0a2e358 HEAD@{0}: reset: moving to HEAD\~2
0254ea7 HEAD@{1}: checkout: moving from 2.2 to master
c10f740 HEAD@{2}: checkout: moving from master to 2.2
上面的 reflog 展示了從 master 分支簽出(checkout)到 2.2 分支,然后再簽回。那里,還有一個(gè)硬重置(hard reset)到一個(gè)較舊的提交。最新的動作出現(xiàn)在最上面以 HEAD@{0}
標(biāo)識.
如果事實(shí)證明你不小心回移(move back)了提交(commit), reflog 會包含你不小心回移前 master 上指向的提交(0254ea7)。
$ git reset --hard 0254ea7
然后使用 git reset 就可以把 master 改回到之前的 commit,這提供了一個(gè)在歷史被意外更改情況下的安全網(wǎng)。
最后,放一張總結(jié)的腦圖總結(jié)一下以上的知識點(diǎn)。
官方資源
Git 官網(wǎng):https://git-scm.com/
Git Github:https://github.com/git/git
模板
gitignore 模板 - .gitignore 文件模板
gitattributes 模板 - .gitattributes 文件模板
github-cheat-sheet - git 命令簡略圖表
Git 書
Git 官方推薦教程 - Scott Chacon 的 Git 書。
Git 教程
Git 中文教程:https://github.com/geeeeeeeeek/git-recipes
廖雪峰的 Git 教程:https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000
有關(guān) git 的學(xué)習(xí)資源:https://github.com/xirong/my-git
文章
Git Cookbook:https://github.com/k88hudson/git-flight-rules/blob/master/README_zh-CN.md
Git 奇技淫巧:https://github.com/521xueweihan/git-tips
Git 風(fēng)格指南:https://github.com/aseaday/git-style-guide
Git 在團(tuán)隊(duì)中的最佳實(shí)踐--如何正確使用 Git Flow:http://www.cnblogs.com/cnblogsfans/p/5075073.html
Git 工具
guis - Git 官網(wǎng)展示的客戶端工具列表。
gogs - 極易搭建的自助 Git 服務(wù)。
gitflow - 應(yīng)用 fit-flow 模型的工具。
firstaidgit.io 一個(gè)可搜索的最常被問到的 Git 的問題
git-extra-commands - 一堆有用的額外的 Git 腳本
git-extras - GIT 工具集 -- repo summary, repl, changelog population, author commit percentages and more
git-fire - git-fire 是一個(gè) Git 插件,用于幫助在緊急情況下添加所有當(dāng)前文件, 做提交(committing), 和推(push)到一個(gè)新分支(阻止合并沖突)。
git-tips - Git 小提示
git-town - 通用,高級 Git 工作流支持!http://www.git-town.com
GUI 客戶端(GUI Clients)
GitKraken - 豪華的 Git 客戶端 Windows, Mac & Linux
git-cola - 另外一個(gè) Git 客戶端 Windows & OS X
GitUp - 一個(gè)新的 Git 客戶端,在處理 Git 的復(fù)雜性上有自己的特點(diǎn)
gitx-dev - 圖形化的 Git 客戶端 OS X
Source Tree - 免費(fèi)的圖形化 Git 客戶端 Windows & OS X
Tower - 圖形化 Git 客戶端 OS X(付費(fèi))
git cheat sheet
github-git-cheat-sheet:https://services.github.com/on-demand/downloads/github-git-cheat-sheet.pdf