目錄:
- 0x01 基礎教程
- 0x02 使用.gitignore忽略不應該提交到git倉庫的文件
- 0x03 分支流程
- 0x04 提交日志
- 0x05 變化比較
- 0x06 團隊協作
- 0x07 高級用法
- 0x08 常見問題(FAQ)
- 0x09 掌握Git的秘訣
0x01 基礎教程
首先,這是一組我根據鏈接難度和相關性組織的懶人圖解學習包:
其次,需要推薦Git Data Transport Command里的這張圖,一圖讀懂Git的數據傳輸流程,對於通過理解來記憶命令非常有好處。為了便於修訂和添加,我重新制作了這張圖:
最后,@SoftwareTeacher提到了上圖中的workspace/index/local repository
三者都是用戶在自己本機上的代碼,比較讓初學者困惑。我們可以對照下中英文術語解釋下:
- workspace,中文術語:工作區。就是本地代碼目錄下除去
.git
文件夾外的文件,這部分代碼的任何變動通過git status
可以看到是紅色標示的。 - index,也叫stage area,中文術語:暫存區。暫存區域是一個文件,一般在.git目錄中,保存了下次將提交的文件列表信息。在暫存區,還未提交到版本庫的文件,通過
git status
可以看到是綠色標示的。 - local repository,中文術語:本地版本庫。就是代碼目錄下的.git目錄。本地版本庫和暫存區的元數據都存儲在.git目錄下。本地版本庫里含有本地創建的各個分支索引,以及遠程版本庫分支的索引信息。在local repository上的操作默認是針對當前分支。
- remote repository,中文術語:遠程版本庫。就是Github/Gitlab或者自己搭建的代碼托管服務器上的代碼倉庫。
把本地代碼分為三個區,是git的一個特色,是一種解決本地復雜代碼修改變動管理的方案,Git的基本操作在這三個區之間:
- 在工作目錄中修改文件。
- 暫存文件,將文件的快照放入暫存區域。
- 提交更新,找到暫存區域的文件,將快照永久性存儲到 Git 倉庫目錄。
0x02 使用.gitignore忽略不應該提交到git倉庫的文件
git源碼目錄下的每個文件夾下都可以添加名為.gitignore
的文本文件,在這個名字的文件里使用文件路徑通配符寫上需要忽略的文件,這些忽略的文件不會提交到git。常見的不應該提交到git的文件有這些:
- 某個平台特定的系統索引文件,例如MacOS下的
.DS_Store
文件 - 緩存文件,例如
.*.swp
- IDE/編輯器的配置文件,例如VisualStudioCode的
.vscode
文件 - 編譯器生成的bin目錄,obj目錄等
- 某種語言的第三方依賴庫,例如nodejs的
node_modules
目錄 - 某種前端框架的編譯輸出目錄,例如
dist
目錄 - 某種格式文件的緩存文件,例如Word文件的
~$*.docx
除了自己編寫,你也可以使用通用的.gitignore
文件,例如,這個網站gitignore.io上,你可以輸入平台、語言的名字生成默認的忽略文件,操作如圖所示:
點擊Create
,生成包含Windows
,C++
,Java
三種環境的基本忽略文件:
一個常見的問題是:如果不小心添加了文件並提交到git倉庫了怎么辦?,操作步驟如下:
- 首先添加路徑到.gitignore文件
- 其次將其從git里刪除,刪除有兩種,一種是刪除目錄,一種是刪除文件,分別添加-r或者-f選型,其中--cached表示從暫存區刪除,如果不加--cached,則本地工作區里文件也被刪除,參考上一節的分區圖片。
# 刪除目錄用-r選項
git rm --cached path_to_dir -r
# 刪除文件用-f選項
git rm --cached path_to_file -f
# 添加日志並生成一個git commit
git commit -m 'delete remote somefile'
# 提交到遠程倉庫
git push
0x03 分支流程
- 分支操作的基本命令:
git checkout -b <name>
可以創建出一個名為name
的分支git checkout <name>
可以切換到名為name
的分支git branch
,用來顯示本地有哪些分支,用*
號標示的就是當前分支。當你在分支之間做git merge
或者git rebase
,以及在遠程版本庫和本地版本庫之間做git pull
或者git pull --rebase
時,遇到沖突的時候,查看狀態最重要的兩個命令就是git status
和git branch
。git branch -a
,用來顯示本地的分支以及已經同步的遠程分支列表。通過git fetch
可以把還沒有同步到本地的遠程分支索引同步下來,再通過git branch -a
就可以看到。
- 圖解Github的PullRequest開發方式:Understanding the GitHub Flow
- 一種成功的git分支策略:A successful Git branching model
- 在線交互式的方式學習Git的分支,只需要鼠標點擊即可學習:learn git branching
- 一種成功的git分支策略:A successful Git branching model
0x04 提交日志
- 基本命令:
- git commit -m 'message'
- 如何寫好git的提交日志:How to Write a Git Commit Message
0x05 比較|可視化
- Visual Studio Code編輯器對Git Diff十分友好:配置
- SublimeMerge用來管理查看多分支狀態,以及合並/rebase也十分直觀方便
- 通過
git bisect
二分排除法來定位哪次提交出現問題: git bisect
- 通過
- SublimeMerge用來管理查看多分支狀態,以及合並/rebase也十分直觀方便
- 使用GIT自帶的GITK可視化,命令:
gitk
0x06 團隊協作
0x07 高級用法
- 最佳實踐、本文略晦澀、慎入:Commit Often, Perfect Later, Publish Once: Git Best Practices
- git內部如何工作,十分簡潔地把git內部的Objects和Refs的關系講解清楚
0x08 常見問題(FAQ)
狀態查看:
Q:為什么git status
的使用頻率很高?
A:git status是最高頻的命令,用來查看當前狀態以確認發生了什么,主要是下面三種常見情況:
- 紅色的是workspace里新發生的變動。
- 綠色的是已經執行過git add,從workspace添加到index的變動。
- 解決沖突中當前沖突解決的狀態
日志:
Q: 如何diff文件的最后一次變動?
A: git log -1 -p -- filename
Q: 如何查看兩個分支之間的提交差異,例如branckA分支有的,而branchB分支沒有的commit?
A: git log branchB..branchA --oneline
Q: 如何查看最新1條日志,-ni
表示最新i條日志,默認全部展示太多了:
A: git log -n1
Q: 如何查看某個成員的所有日志:
A: git log --oneline --author=feilong
Q: 如何過濾日志:
A: git log --grep keyword
Q: 如何查看某個文件被那條.gitignore忽略規則所忽略?
A: git check-ignore -v filename
Q: 如何方便的查看變動日志?
A: 如果機子上裝了lighttpd或者webrick則git instaweb
或者git instaweb --httpd=webrick
Q: 如何同時查看每個分支的最頂端的commit?
A: git branch -vv
Q: 如何查找哪些文件里含有一個字符串,例如函數名?
A: git grep -l monitor
分支:
Q:如何查看所有分支
A:git branch -a
Q:如何切換到別的分支
A: git checkout other_branch_name
Q: 如何從別到分支合並到當前分支
A: git merge other_branch_name
Q: 如何以別的分支為基准在當前分支上重做
A:git rebase other_branch_name
提交:
Q:代碼修改一半,需要臨時更新代碼修改一個BUG怎么辦?
A:把當前代碼臨時放到stash stack里:
- 入棧:
git stash
- 更新,修改並提交:
git pull --rebase
- do something
git add .
git commit -m 'fix bug'
git push origin master
- 出棧:
git stash pop
沖突:
Q: 遇到沖突的時候,如何只使用自己的版本?
A: 取決於是merge還是rebase,在merge中--ours
表示自己的版本,--theirs
表示對方的版本,而rebase時相反,rebase顧名思義指以對方的版本為基准版本。
- merge:
git merge other_branch_name
- 在merge中,使用自己的版本是:
git checkout --ours
,使用對方的版本是git checkout --theirs
,兩者都需要則要通過diff工具比對合並。 git merge --continue
- rebase:
git rebase other_branch_name
- 在merge中,使用自己的版本是:
git checkout --theirs
,使用對方的版本是git checkout --ours
,兩者都需要則要通過diff工具比對合並。 git rebase --continue
- merge和rebase哪個好?
- 觀點:rebase是有害的一種流行的說法是
git rebase
可以讓歷史記錄顯的更干凈,但是這篇文章揭示了rebase本質上是merge后把被merge節點的來源忽略了,文章認為這實際上會使得commit節點的歷史記錄丟失了,讓歷史記錄顯的更干凈的做法應該是提供一個更好的顯示策略和工具,而不是直接在索引里把元數據丟掉。 - merge和rebase的節點變動對比:
- 觀點:rebase是有害的一種流行的說法是
Q: git pull
和git pull --rebase
的區別是什么?
A:git pull=git fetch+git merge
,而git pull --rebase=git fetch+git rebase
,所以在兩種情況下的沖突的解決同merge
和rebase
Q:git cherry_pick
怎么用?
A:如果你想把分支A上的一個commit提取出來添加到分支B上,基本步驟是:
- 通過
git checkout A
切換到A分支- 通過
git log --oneline -n10
查看A分支上最近N條記錄(這里是查看了10條),復制你需要提取的那個commit的哈希例如是7f6eb072b7e。
- 通過
- 通過
git checkout B
切換到B分支- 通過
git cherry_pick 7f6eb072b7e
將哈希值7f6eb072b7e對應的那個commit作用到當前分支,也就是分支B。 - 如果有沖突,你需要解決下沖突。
- 通過
0x09 掌握Git的秘訣
首先,我學習 git/svn/latex 之類的,基本做法是理解背后的原理,然后使用的過程中隨時查手冊,久了就通過肌肉記憶自然熟悉。像這里是latex的索引,但是我現在編輯latex比較少,有時候排版書籍的時候用的時候會去更新下。
其次,堅持使用git命令行來處理日常工作,學習git的原子指令。如果使用GUI,會習慣GUI的操作,但是對於git本身的常用操作會生疏,甚至不理解一個GUI復合了哪些原子的操作,出現問題的時候到底是哪個環節出問題。
最后,工欲善其事,必先利其器,即使是使用git命令本身,也有利器輔助。我用了2年多Mac系統,有一個歷史命令自動完成的工具,叫fishshell,通過fish,我只要敲前面幾個字母,后面就會灰色自動完成,上下鍵可以切換同類的敲過的高頻命令,按向右的方向鍵自動完成。通過這種方式,我又比別人減少了大量輸入錯誤導致的命令學習障礙,極大增加成功率。