一、Git簡介
Git是Linux之父Linus與2005年用C語言編寫的分布式控制系統。 Git的分布式區別于SVN等集中式版本控制系統的地方有哪些呢?
- 集中式:有一個集中管理的“中央服務器”,保存所有的修訂版本。
- 缺點是版本控制器是放在服務器中,必須聯網才能協同工作,保存數據。
- 如果服務器損壞,將丟失所有數據。
- 分布式:分布式沒有“中央服務器”。每一個人電腦上都有一個獨立且完整的版本庫。
- 工作不一定需要聯網,每次可以提交到本地版本庫,需要的時候在同步到網絡或其他人的版本庫中
- 不擔心數據的丟失,數據丟失了,可以隨時在其他人電腦中拷貝數據
- 工作中很少有相互之間推送版本庫,往往還是會有一臺電腦充當“中央服務器”,方便大家交換數據,或者使用github等網絡服務器
- git還擁有分支管理等技術
- 幾個專用名詞
Workspace 工作區:數據所在的目錄
Reponsitory 版本庫:本地版本庫,隱藏文件.git
Index/Stage 暫存區: 版本庫中緩存修改的數據
Remote 遠程倉庫: 服務器中的版本庫,一般為GitHub/BitBucket/GitLab
等
二、配置 Git
Git安裝完成后需要配置Git環境。Git自帶 git config
工具幫助設置控制Git倉庫的外觀和行為的配置變量。這些配置變量分別存儲在三個不同的位置:
/etc/gitconfig
文件:包含系統上每一個用戶及他們倉庫的通用配置。如果使用帶有--system
選項的git config
時, 它會從文件讀寫配置變量~/.gitconfig
或~/.config/git/config
文件: 只針對當前用戶。可以傳遞--global
選項讓Git讀寫此文件- 當前使用倉庫的Git目錄中的
config
文件(就是.git/config
): 針對該倉庫。
每一個界別覆蓋上一級別的配置,所以 .git/config
的配置變量會覆蓋 /etc/gitconfig
中的配置變量。
用戶信息:
當安裝完Git應該做的第一件事就是配置用戶名與郵件地址。這很重要,因為每一個Git的提交都會使用這些信息,并且它會寫到你的每一次提交中,不可更改
$ git config --global user.name "zcy"
$ git config --global user.email "zcy@xx.com"
如果使用了 --global
選項,那么該名字只需要運行一次,因為之后無論你在該系統上做任何事,Git都會使用那些信息。如果你想針對特定的項目使用不同的用戶名稱和郵箱,可以在該項目目錄下運行沒有 --global
選項的命令。
檢查配置信息:
$ git config --list
user.name = xx
user.email = xx@xx.com
color.xx = xx
......
如果看到重復的變量名,是因為Git會從不同的文件中讀取同一個配置(例如 /etc/gitconfig
與 ~/.gitconfig
)。這種情況下,Git會使用它找到每一個變量的最后一個配置。
也可以通過輸入 git config <key>
來檢查某一項配置:
$ git config user.name
獲取幫助
$ git help
$ git help <key> //例如:$ git help config
三、獲取Git倉庫
有兩種獲取Git項目倉庫的方法。
- 從服務器克隆一個現有的Git倉庫
- 創建一個Git倉庫,將現有項目或目錄導入所有的文件到Git中
1. 克隆現有的倉庫
$ git clone https://xxx.com/A //克隆A到A
$ git clone https://xxx.com/A FileA //克隆A到FileA中
2. 創建Git倉庫
- 創建空文件目錄(可省略,自己手動創建一個空文件也可以)
$ mkdir NewFile //在當前目錄下創建NewFile文件夾
$ cd NewFile //進入到空文件目錄
$ pwd //查看當前具體路徑
- 初始化git
$ git init //將當前目錄變成Git可以管理的倉庫
- 添加文件
$ git add *.* //添加具體文件,注意,可反復多次使用,添加多個文件;
$ git add . //也可以使用這句,添加全部修改文件
$ git commit -m "提交說明" //把修改提交到倉庫
git add
是將工作區數據添加到暫存區
git commit
是將暫存區數據提交到分支
四、Git常用命令
1. 狀態檢查
$ git status // 查看當前版本庫的狀態
Untracked files: 下面的文件代表未跟蹤的文件,Git不會自動跟蹤,如果需要跟蹤,需要
git add xx
Changes to be commited: 下面的文件代表已暫存狀態。如果提交,那么該文件的版本將會被保存。
Changes not staged for commit: 說明已跟蹤文件內容發生了變化,但是沒有放到暫存區。
如果要暫存這次更新,需要運行 git add xx
。
2. 狀態簡覽
git status
命令的輸出十分詳細,如果需要查看簡單格式,可以使用
$ git status -s
$ git status -short
其中,未跟蹤的文件前面有 ??
標記;新添加到暫存區的文件面前有 A
的標;修改過的文件前面有 M
的標記。
出現在右面的話表示該文件被修改了,但是還沒有放到暫存區。
出現在左面的話表示該文件修改了,并放入了暫存區。
如果出現了兩個則表示,在工作區中修改了并提交到了暫存區后又被修改了,還沒有存到暫存區。
3. 查看當前相對修改的內容
如果覺得 git status
不夠詳細,想查看尚未暫存的文件具體修改的地方,可以用
$ git diff
$ git diff HEAD --xxx
此命令比較的是工作區和版本庫中文件的差異。
如果要查看已暫存未提交的內容,可以用
$ git diff -cached
$ git diff -staged // Git 1.6.1 及更高版本可以用
4. 提交更新
如果覺得使用暫存區較為繁瑣,Git 提供了一個跳過使用暫存區的方式,只要在提交的時候給 git commit
加上 -a
選項,Git就會自動把所有已經跟蹤過的文件暫存起來并一起提交,從而跳過 git add
步驟:
$ git commit -a -m "commit describe"
5. 撤銷
- 漏掉文件沒有添加,或提交信息寫錯可以使用帶有
--amend
的提交命令
$ git commit -m "init"
$ git add xxx
$ git commit -amend
第二次提交將會代替第一次提交的結果。使用這個技巧需要注意,它會修改 SHA1
的值,類似于一個小小的變基。如果已經推送了最后一次提交,就不要用這個方法了。
- 將已經加入暫存區撤回工作區
$ git reset HEAD <file>
- 撤銷對文件的修改
如果修改的文件還沒有到暫存區,想還原成上次提交的樣子,即放棄此次對工作區內容的修改。可以使用
$ git checkout -- <file>
注意:一定要帶著 --
, 如果沒有 --
, 就變成了 ++切換另一個分支++ 命令。
6. 查看提交歷史
在提交了若干更新后,可以回顧提交歷史,使用
$ git log // 按照由近到遠的提交時間列出所有更新
$ git log -p // 按補丁格式顯示每次提交內容的差異
$ git log --graph // 顯示ASCII圖形表示分支合并歷史(很有用)
$ git log --stat // 額外查看每次提交的簡略統計信息
$ git log --shortstat // 僅顯示--stat中最后行數的修改統計
$ git log --name-only // 僅顯示已修改的文件清單
$ git log --name-status // 顯示新增、修改、刪除的文件清單
$ git log --abbrev-commit // 顯示簡短且唯一的哈希字串(默認7個字符,有時會增加到8-10個)
$ git log --relative-date // 使用較短的時間顯示
$ git log --pretty=oneline // 當行顯示簡易log, 不顯示很多凌亂的信息(online 可替換成 short,full和fuller)
$ q // 如果顯示log版本信息有很多,使用q鍵停止查看
--pretty
后面還可以接 format
,可以定制要顯示的格式。
$ git log --pretty=format:"%h - %an, %ar: %s"
git log --pretty=format:xx
常用選項
選項 | 說明
%H | 提交對象(commit)的完成哈希字串
%h | 提交對象的簡短哈希字串
%T | 樹對象(tree)的完整哈希字串
%t | 樹對象的簡短哈希字串
%P | 父對象(parent)的完整哈希字串
%p | 父對象的簡短哈希字串
%an | 作者名字
%ae | 作者電子郵件
%ad | 作者修訂日期(可以用 --date= 選項定制格式)
%ar | 作者修訂日期,按多久前顯示
%cn | 提交者(commiter)的名字
%ce | 提交者電子郵件地址
%cd | 提交日期
%cr | 提交日期,按多久前顯示
%s | 提交說明
限制輸出長度
除了定制輸出格式選項外,git log
還有其他非常使用的限制輸出長度的選項。比如
$ git log -2 // 顯示最新兩條提交
// 顯示指定時間點之后
$ git log --since=2.weeks
$ git log --since=2008-01-15
$ git log --since="2 years 1 day 3 minutes ago"
$ git log --after=2.weeks
// 顯示指定時間點之前
$ git log --until=3
$ git log --before=3
$ git log --author xx // 指定作者
$ git log --committer xx // 指定提交者
$ git log --grep xx // 搜索提交說明中的關鍵字
$ git log -s xxx // 檢索關于增加或移除xxx字符串的提交(方法、函數名、實例等)
$ git log -g master // 查看引用日志(引用日志的概念查看本節末尾)
// 查看在 `develop` 中而不在 `master` 中的提交,即 develop 尚未推送的提交,也可以反過來查未拉取的提交。以下命令是等價的
$ git log master..develop // 兩點
$ git log ^master develop
$ git log develop --not master
// 查看 `master` 中沒有的提交
$ git log developA developB ^master
$ git log developA developB --not ^master
// 查看包含但不是兩者共有的提交
$ git log master...develop // 三點
$ git log --left-right master...develop // 顯示分別歸屬哪一個分支
注意: 如果是多條件組合搜索,需要同時滿足的話,需要添加 --all-match
, 否則,滿足任意一個條件的提交都會被匹配出來
綜合例子:檢查Git倉庫中,2008年10月,ZCY提交的但是未合并的測試文件
$ git log --pretty="%h - %s" --author=zcy --since="2008-10-01" \ --befor="2008-11-01" --no-merges -- t/
7. 移動文件(或者說是重命名文件)
$ git mv file_from file_to
8. 刪除文件
$ rm xxx // 刪除本地文件
$ git rm xxx // 刪除暫存區文件
$ git rm --cached xx // 停止追蹤指定文件,但該文件會保留在工作區
$ git rm *.* // 刪除文件也可以用glob模式
刪除本地文件后,需要執行 git rm
命令也同時刪除掉緩存區中的文件。如果誤刪了,執行 git checkout
撤銷命令,即可恢復。
注意:無法恢復從來沒有被添加到版本庫就被刪除的文件
9. 版本回退
Git中如果想要版本回退,必須要知道回退的版本。在Git中 HEAD
表示當前版本,上個版本是 HEAD^
,上上個版本是 HEAD^^
,前100個版本是 HEAD~100
。
$ git reset --hard HEAD^ // 項目回退第一父提交(合并時所在的分支或非合并時上一版本)
$ git reset --hard HEAD^2 // 項目回退第二父提交(僅適用于合并的提交)
$ git reset --hard HEAD~2 // 項目回退到第一父提交的第一父提交(與 `HEAD^^` 是等價的,與 `HEAD^2` 不一樣)
$ git reset --hard SHA1_id // 指定版本號的回退
HEAD
指向哪個版本號,你就把當前版本定位在哪。
如果回退后悔了,并且已經關掉了終端,可以使用命令 git reflog
查看引用日志(reflog)。引用日志記錄了最近幾個月你的 HEAD
和分支引用所指向的歷史。每次 HEAD
所指向的位置發生了變化,引用日志都會記錄下來。通過這些數據,你可以很輕松的獲取之前的提交歷史。然后可以使用 git show HEAD@{2}
來引用 reflog
中輸出的記錄。
10. 搜索
- 內容搜索
Git提供了一個 git grep
搜索命令,你可以很方便的在提交歷史或工作目錄中查到一個字符串或者正則。
$ git grep // 搜索 xx,
$ git grep -n zcy // 顯示所在行數
------
fileD:5:zcy
fileD:7:zcyzcy
fileD:12:zcysdjfkldsj
------
- 日志搜索
如果想查看某個文件什么時候引入的及修改的可以調用命令
$ git log -s fileD // 顯示提交信息
$ git log -s --oneline fileD // 單行顯示簡要信息
------
b8db0f4 (HEAD -> master) C6 // 有修改
9f9a151 (develop) C4 // 有修改
bf7bb79 C2 // 這里引入
------
五、遠程倉庫
遠程倉庫指的是托管在其他網絡中你的項目的版本庫。(GitHub/BitBucket/GitLab/私有服務器等)
1. 查看遠程倉庫
$ git remote // 查看已經配置的遠程倉庫服務器
$ git remote -v // 會顯示對應的URL
$ git remote show [remote-name] // 顯示更多信息
2. 添加遠程倉庫
// 先有遠程倉庫
$ git clone <遠程倉庫地址/url> // 從服務器拉取項目到本地 命名與遠程一樣
$ git clone <url> xxx // 將項目放到xxx文件中(如果沒有,創建xxx)
// 先有本地倉
$ git remote add origin <遠程倉庫地址> // 關聯一個新的遠程Git倉庫
$ git push -u origin master // 第一次推送master分支的所有內容到遠程倉庫
$ git push origin master // 本地推送到遠程主分支,`master` 可以切換分支名
$ git push // 本地推送到遠程(不考慮分支等情況或明確自動推送的分支)
第一次推送master分支時,加上了 -u
參數,Git不但會把本地的master
分支內容推送的遠程的 master
分支,還會關聯本地的 master
分支和遠程的 master
分支,在以后的推送或者拉取時就可以簡化命令。
3. 拉取更新的內容
$ git fetch [remote-name] // 拉取還沒有的數據(該命令不會自動合并并修改當前的工作,需要手動合并)
$ git pull origin master // 拉取遠程分支未同步的數據
$ git pull // 拉取還沒有的數據(自動合并并修改當前的工作)
4. 遠程倉庫移除與重命名
- 重命名引用名字可以執行命令:
$ git remote rename current_name new_name // 此命令同時會修改遠程分支名
- 移除遠程倉庫可以執行命令:
$ git remote rm <分支>
六、標簽管理
Git可以給歷史中的某一個提交上打標簽。比如標記發布版本(v1.0)或重大更新等。
1. 列出標簽
$ git tag // 顯示所有標簽,以字母順序排列
$ git tag -l 'v1.8*' // 只列出 1.8 版本相關的標簽
2. 添加標簽
Git標簽分兩種:輕量標簽、附屬標簽
- 輕量標簽
輕量只做一個特定的引用作用,輕量標簽本質上是將提交校驗和存儲到一個文件夾中,沒有其他任何信息。
$ git tag v1.4
$ git tag v1.5 -ll
- 附屬標簽
存儲在Git中的一個完整對象。它們是可以被校驗的,包括打標簽者的名字、郵箱、日期時間等。并且可以使用 GNU Privacy Guard(GPG)
簽名與驗證。要添加附屬標簽的只要在運行 tag
命令時執行 -a
選項:
$ git tag -a v1.4 -m 'add version v1.4'
-m
指定了一條會存儲在標簽中的信息。
通過 git show <tag>
可以查看到標簽信息與對應的提交信息。
- 后期添加標簽
如果之前提交的版本需要添加標簽,或者忘記給剛剛提交的版本添加標簽,我們也可以后期提交:
$ git tag -a v1.5 1234567 // 其中1234567是commit_id SHA1的前幾位
3. 共享標簽
默認情況下,git push
不會將標簽推送到遠程服務器上。如果要推送到遠程,需要運行:
$ git push origin v1.4 // 推送v1.4的標簽到遠程
$ git push --tags // 將遠程沒有的標簽全部推送到遠程
4. 刪除標簽
如果標簽只在本地的話,要刪除某個標簽只需要執行:
$ git tag -d v1.4
如果標簽已在遠程,要執行:
$ git tag -d v1.4 // 刪除本地標簽
$ git push origin:refs/tags/v1.4 // 刪除遠程的標簽
5. 根據標簽檢出數據
在特定的標簽上創建一個新分支,可以用:
$ git checkout -b newVersion2 v1.9.0
再次修改并提交,newVersion2
分支會因為改動向前移動,此時,newVersion2
與v1.9.0
開始不同,這點需要注意。
七、分支管理
1. 簡介
使用分支意味著你可以把你的工作從開發主線上分離出來,即使開發過程中出現問題也不會影響到主線。在開發完成后,將支線上完成的功能合并到主線即可。
在進行提交操作時,Git會保存一個提交對象。該提交對象會包含一個指向暫存內容快照的指針,但不僅僅是這樣,還包括作者的姓名、郵箱和提交時輸入的信息以及指向它父對象的指針。首次提交時沒有父對象,普通提交操作產生的提交對象有一個父對象,由多個分支合并產生的提交對象有多個父對象。
假設有一個工作目錄,里面有三個將要暫存和提交的文件。當使用 git commit
進行提交時,Git會先校驗,然后將將校驗和保存為樹對象。隨后,Git會創建一個提交對象,它除了上面說到的信息外,還會包括樹對象的指針。
現在Git中包含五個對象:三個blob對象(保存文件快照),一個數對象(記錄目錄結構和對象索引),一個提交對象(包含指向前述樹對象的指針和所有提交信息)
Git的分支是包含所指對象校驗和(長度為40的SHA-1值字符串)的文件,本質上是指向提交對象的可變指針。 它的創建、合并和銷毀都只是切換一個41字節(包含換行符)的指針的指向,所以異常高效。
Git默認分支(主分支)名字是 master
。master
分支會在每次 git commit
操作中自動向前移動,形成一條主分支時間線。master
分支并不是一條特殊的分支,跟其他的分支沒有區別。只是因為 git init
默認創建 master
分支,絕大多數人不會去改動它,所以基本每個倉庫都有,并默認作為主分支。
HEAD
是當前分支引用的指針,它默認總是指向該分支最后一次提交。嚴格來說 HEAD
指針并不是直接指向提交,而是默認指向 master
分支, 再用 master
指向最新提交,就能確定當前分支及當前分支的提交點。
2. 創建分支
查看當前已有分支
$ git branch // 顯示所有分支,并在當前分支前加*號
創建Git分支就是創了一個可以移動的新的指針。創建分支執行以下命令:
$ git branch testing // testing 為新分支名
當我們創了新的分支 testing
,這會在當前所提交的對象上創建一個 testing
指針。此時,HEAD
仍然指向 master
分支。因為 git branch xx
命令僅僅是創建了一個新的分支,不會自動切換到新分支上。
可以使用 git log --oneline --decorate
查看當前各個分支當前所指向的對象。
3. 切換分支
要切換到一個已存在的分支,需要執行以下命令:
$ git checkout testing // testing 為已存在的分支
$ git checkout -b testing // 上述如果加上 -b 表示創建并切換分支,等價于下面兩條命令
$ git branch testing
$ git checkout testing
這樣 HEAD
分支就會指向新創建的 testing
分支。
假設此時最新提交對象為 a
。再次提交,testing
分支會向前移動,指向新對象 b
。
此時再次切換回 master
分支,會發現 master
分支內容沒有改變,分支仍然是指向對象 a
。
如果再次修改并提交,那么這個項目的提交歷史就會產生分叉。master
分支會指向 c
,而 testing
分支會指向 b
。
使用 git log --oneline --decorate --graph --all
輸入提交歷史、各個分支的指向及項目的分叉情況。
3. 分支的創建與合并
合并分支命令
$ git merge <分支名> // 合并分支,指定分支名的分支合并到當前分支
當需要合并時的整個流程是怎么樣的呢?舉個栗子:
1. 假設正在開發一個版本,為了實現一個新需求,創建了一個分支 `A`, 在該分支上工作。
2. 此時來了一個問題需要緊急修復,切換到線上分支。
3. 為緊急任務創建新分支 `B`, 并修復問題。
4. 測試通過后,切換回線上分支,合并 `B` 分支,將改動推動到線上分支。
5. 切換回 `A` 分支,繼續開發工作。
整體流程如下:
// 1
$ git checkout -b A // 創建分支 `A`
update
// 2
$ git commit -a -m "update" // 更新工作內容,并且提交到分支 `A`
$ git checkout master // 切換到線上主分支
// 3
$ git checkout -b B // 創建新分支 `B`
// 4
update
$ git commit -a -m "fix bug" // 修復完成,提交修改
$ git checkout master // 切換到線上主分支
$ git merge B // 合并分支 `B` 中修改的內容到 `master` 分支
$ git branch -d B // 刪除分支 `B`,因為此時已經不需要它了
// 5
$ git checkout A // 切換到分支 `A`,要注意此時不帶 `-b`
在合并的時候,會看到“快進(fast-forward)”這個詞。由于當前
master
分支所指向的當前提交(有關B
提交)的直接上游。所以Git只是簡單的將指針向前移動。當試圖合并兩個分支時,如果順著分支走下去就能到達另一個分支,那么Git在合并時只會簡單的將指針向前推進,因為這種情況下合并操作沒有需要解決的分歧——這就叫快進。
此時在分支 B
中的修改并沒有包含到分支 A
中。如果需要拉取 B
分支中修改的內容,可以先執行 git merge master
合并 master
分支中的內容到分支 A
中。
假設此時已經完成 A
的開發并且已經提交 commit
,并且打算合并到 master
分支。需要執行:
$ git checkout master
$ git merge A
此時,合并的時候并沒有看到 fast-forward
。因為這種情況下,開發歷史從一個更早的地方有了分叉。master
分支所在的提交不是 A
的直接祖先,Git需要使用兩個分支的末端和兩個分支的共同的一個祖先分支做一個三方合并,創建一個新的提交并且指向它。這個被稱為一次合并提交。Git會自行決定選取哪個提交作為共同祖先,并以此為合并的基礎。
此時,已經不再需要 A
分支了,可以刪除這個分支了:
$ git branch -d A
刪除包含未合并工作的代碼,刪除會失敗并報錯。如果還是想要刪除分支,并放棄那些已修改工作,可以使用 -D
強制刪除。
通常在合并分支時,Git會采用 fast-forward
模式,在這種模式下,刪除分支后,會一起刪除掉分支的信息。如果禁用掉該模式,在合并的時候使用 --no-ff
,Git在合并的時候會生成一個新的 commit
。這樣在歷史分支流程就能看到該分支的信息。
$ git merge --no-ff -m "merge A without fast-forward" A
4. 遇到沖突的分支合并
合并操作并不是總是能順利完成的。如果在兩個不同分支內或者多人協作對同一文件的同一地方進行了不同的修改,修改就會發生沖突。Git做了合并,但是沒有自動的創建一個新的合并提交。Git會暫停,等待解決沖突。你可以使用 git status
查看未合并(unmerged)的文件。
任何因包含合并沖突而有待解決的文件,都會以未合并狀態表示出來。Git會在有沖突的文件中加入標準的沖突解決標記。
<<<<<<<< HEAD
master code
========
A code
>>>>>>>>
這表示 HEAD
所示的版本(當前分支 master
)在這個區段的上半部分 (<<<
到 ===
),而 A
分支所指示的版本在這個區段的下半部分(===
到 >>>
)。為了解決沖突,需要選擇其中一個版本的內容,或者自行合并這些內容。修改完成后刪除掉 <<<
, ===
, >>>
。
在修改完成后,使用 git add
來標記文件沖突以解決。暫存這些原本有沖突的文件,Git會將他們標記為沖突已解決。你可以再次運行 git status
來確認所有的沖突已解決。
如果遇到沖突,但是不想處理沖突這種情況,你可以使用 git merge --abort
來退出合并。該命令會嘗試恢復到你運行合并之前的狀態。但是,當運行命令前,在工作目錄有未儲藏、未提交的修改時,它不能完美的處理,即有可能有代碼丟失的風險。
如果發現合并后出現了沖突,因為一些特殊情況弄的你很混亂,而本地的代碼如果修改不是很多,很重要。那么可以嘗試 git reset --hard HEAD
回到初始狀態。這個命令會清空工作目錄中的所有修改。
5. 分支列表
$ git branch -v // 查看每一個分支的最后一次提交
$ git branch --megred // 過濾這個列表中已經合并的分支
$ git branch --no-megred // 過濾這個列表中有未合并工作的分支
6. 分支開發工作流
長期分支:
master
分支只保留完全穩定的代碼,可能只是已經發布或即將發布的代碼。開發工作在develop
/next
或其他平行分支上進行,在開發完成后在合并到master
分支。特性分支:特性分支是一種短期分支,它被用來實現某一單一特性或相關工作。在工作完成,并合并到需要的分支上后,再將其刪除掉。這樣既不用擔心其破壞工程的穩定性,又不會耽誤開發,如果發現有問題或停止更新該功能,可以隨時廢棄。每個人的每項工作都可以創建一個特性分支,在開發完成后合并到自己的分支上。
遠程分支:遠程引用是對遠程倉庫的引用(指針),包括分支、標簽等。可以通過
git ls-remote
來顯示的獲取遠程引用的完整列表。或者git remote show
獲取遠程分支的更多信息。一個更常見的做法是利用遠程跟蹤分支。在你做任何網絡通信操作時,它們會自動移動。
遠程倉庫的
origin
并沒有特殊含義,和master
一樣都是默認創建的名字而已。只不過,master
是在git init
時創建的默認分支名;而origin
是在git clone
時默認的遠程倉庫名字。如果在git clone
時運行git clone -o zzz
, 那么你默認遠程分支名就是zzz/master
。
7. 跟蹤分支
從一個遠程跟蹤分支檢出一個本地分支會自動創建一個叫做跟蹤分支(有時候也叫做上游分支)。跟蹤分支是與遠程分支有直接關系的本地分支。如果再跟蹤分支上輸入 git pull
,Git能自動識別到哪個服務器上拉、合并到哪個分支。
當克隆一個倉庫時,它通常會自動創建個跟蹤 origin/master
的 master
分支。如果你想設置其他遠程倉庫的跟蹤分支,運行:
$ git checkout --track origin/serverfix // 創建跟蹤分支
或
$ git checkout -b sf origin/serverfix // 將本地分支與遠程分支設置不同的名字
設置已有的本地分支跟蹤一個剛剛拉取下來的遠程分支,或者想要修改正在跟蹤的上游分支。可以添加 -u
或 --set-upstream-to
來執行
$ git branch -u origin/serverfix
查看所有的跟蹤分支可以執行命令
$ git fetch --all // 抓取所有的遠程服務器
$ git branch -vv // 查看所有的跟蹤分支
反饋結果中,
ahead
表示本地還有提交未推送到服務器,hebind
表示本地有未并入的服務器內容。需要注意的是,查看所有跟蹤分支的名利置灰告訴你關于本地緩存的服務器數據。所以在統計最新的數據之前,需要先抓取所有的遠程倉庫。
8. 刪除遠程分支
如果已經通過遠程分支完成了所有的工作(完成了一個特性并合并到了 master
分支)。可以將遠程分支刪除。運行命令:
$ git push origin --delete serverfix
9. 變基
寫在前頭:不要對在你的倉庫外有副本的分支執行變基!(個人理解:不要變基提交到 master
或多人共同提交工作的分支,不要變基已經提交過的歷史。只用于完全屬于個人的分支或者尚未推送到公用倉庫的提交上)
在Git中整合不同分支的修改主要有兩種方法合并 merge
與變基 rebase
。
首先,對比下合并與變基的流程差異。開發分支 develop
與提交分支 master
。
- 使用合并:分叉了歷史,它會把兩個分支的最新快照及最近的共同祖先進行三方合并成一個最新的快照并提交。
- 使用變基:在當前開發分支引入提交分支的更新,再開發。再將開發分支并入提交分支。變基的實質是丟棄一些現有的提交,然后相對應的創建一些內容一樣但是實際上不同的提交。
舉個栗子對比下:
- 合并
// 1 創建合并測試git
$ cd /Users/zcy/Documents/Project
$ mkdir mergeTest // 也可以手動創建
$ cd mergeTest // 也可以手動創建
$ git init
// 2 master 提交兩次
$ vim fileM
update
$ git add .
$ git commit -m "C0"
$ vim fileM
update
$ git add .
$ git commit -m "C1"
// 3 創建 develop 分支并提交
$ git checkout -b develop
$ vim fileD
update
$ git add .
$ git commit -m "C2"
// 4 切換 master 并提交
$ git checkout master
$ vim fileM
update
$ git add .
$ git commit -m "C3"
// 5 切換 develop 分支并提交
$ git checkout -b develop
$ vim fileD
update
$ git add .
$ git commit -m "C4"
// 6 切換 master 合并,輸出結果
$ git checkout master
$ git merge develop
commit "C5"
$ git log --graph --pretty=oneline --abbrev-commit
------
* dffc0cc (HEAD -> master) C5
|\
| * 9f9a151 (develop) C4
| * bf7bb79 C2
* | 7012753 C3
|/
* e453181 C1
* 1c27cc4 C0
------
- 變基
// 重復合并前五步創建新倉庫 rebaseTest
// 6. 變基,輸出結果
// 當前在 develop 分支
$ git rebase master
$ git log --graph --pretty=oneline --abbrev-commit
------
* 7f9e86d (HEAD -> develop) C4
* b088e32 C2
* 2a4a7b5 (master) C3
* 4397a6b C1
* ab9683f C0
------
// 7. 將 develop 分支上的代碼合并到 master 輸出結果
$ git checkout master
$ git merge develop
$ git log --graph --pretty=oneline --abbrev-commit
------
* 7f9e86d (HEAD -> master, develop) C4
* b088e32 C2
* 2a4a7b5 C3
* 4397a6b C1
* ab9683f C0
------
對比合并與變基的輸出結果,可以得到總結:
1. 變基把分叉的提交歷史整理成為一條直線,相對于合并可以得到一個更加簡潔的提交歷史。
2. 變基比合并少一個合并提交節點。
3. 合并的分支是按照時間倒序排列的。而變基中 `master` 分支的時間線是錯亂的,可能會影響到提交歷史的查看。
4. 無論是變基還是合并,整合的最終指向提交都是一樣的。只是提交歷史不同而已。
5. 個人建議:倉庫的提交歷史是對實際發生過什么的歷史記錄。它是針對歷史的文檔,本身就非常的有價值且很重要。它可以供開發者或者后來者查閱,盡可能的不要亂改。如果一定要改動的話,相對于重要的分支或長期分支也不建議使用變基,對提交歷史沒有影響或特性分支可以采用變基,用來使提交歷史更簡潔。
補充:萬一真遇到有人對 master
下手了,執行了變基操作,可以讓團隊中的每個人都執行 git pull --rebase
嘗試稍微挽救一下。
10. 暫存文件
Git自帶一些腳本可以使部分工作更簡單。如果想將文件的特定組合部分組合成提交可以暫存對應文件。在調用 git add
命令時添加 -i
或 --interactive
,進入終端模式。
$ git add -i
$ git add --interactive
-----
$
staged unstaged path
1: unchanged +1/-0 fileD
*** Commands ***
1: status 2: update 3: revert 4: add untracked
5: patch 6: diff 7: quit 8: help
What now>
------
這個命令展示了與 git status
類似的信息,但是更簡明。暫存的在左側,未暫存的在右側。
輸入 2
或 u
,腳本會提示要暫存那個文件。輸入前面的數字,文件面前會帶 *
。
What now> 2
staged unstaged path
* 1: unchanged +1/-0 fileD
Update>>
如果此時不輸入任何東西,直接回車,將會暫存選中的文件并提示 updated 1 path
。輸入 1
或 s
可以看到已被暫存:
staged unstaged path
1: +1/-0 nothing fileD
*** Commands ***
1: status 2: update 3: revert 4: add untracked
5: patch 6: diff 7: quit 8: help
What now>
要取消暫存文件,輸入 3
或 revert
,執行與暫存相同操作。再次查看,會發現已取消暫存。
11. 儲藏
當開發中突然遇到問題需要緊急修復,又不能放棄當前的更改,當前的修改還在報錯又不能提交。
這種情況經常會發生,在這種情況下我們可以先將手里的工作儲藏起來,存儲在棧上。執行命令 git stash
或 git stash save
。待其他工作完成后,回來再繼續。
$ git status
------
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: fileD
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: fileM
------
$ git stash // 或 `git stash save`
$ git status
------
On branch master
nothing to commit, working tree clean
------
可以看到,工作目錄清空了,未暫存的和已暫存的都消失了。這時我們就可以去搞別的事情了。
那么,當別的工作完成了回到了原來的開發分支,該怎么繼續呢?使用 git stash list
查看儲藏列表(可以儲藏很多)。
$ git stash list
------
stash@{0}: WIP on master: 7f9e86d C4
------
獲取到儲藏列表以后,就可以取出來繼續使用了。
$ git stash apply // 獲取最新的儲藏
$ git stash apply stash@{0} // 獲取指定的儲藏
可以不再原來的開發分支獲取儲藏,即如果在 A
分支儲藏的工作內容,在 B
分支也可以獲取,但是要注意有可能會合并沖突。
使用上述獲取儲藏命令獲取完成后,儲藏列表不會自動刪除已儲藏內容,需要執行命令刪除:
$ git stash drop // 刪除最新儲藏
$ git stash drop stash@{0} // 刪除執行儲藏
或者在獲取到儲藏列表后直接運行 git stash pop
來獲取儲藏并自動刪除。
補充
1. 忽略文件
總有一些文件不需要納入到Git的管理中,也不希望它們總是出現在未跟蹤文件列表。一般都是自動生成的文件,比如日志文件。在這種情況下,可以創建一個名為.gitignore
的文件,列出要忽略的文件模式。
$ touch .gitignore
$ vim .gitignore
*.[oa]
*~
第一行告訴Git忽略所有.o或.a結尾的文件。
第二行告訴Git忽略所有以波浪符~結尾的文件。
文件 .gitignore
的格式規范如下:
- 所有空行或者以#開頭的行都會被Git忽略
- 可以使用標準的glob模式匹配
- 匹配模式可以以
/
開頭防止遞歸 - 匹配模式可以以
/
結尾指定目錄 - 要忽略指定模式以外的文件或目錄,可以在模式前加上
!
取反
所謂的glob模式是指shell所使用的簡化了的正則表達式。
*
匹配另個或多個任意字符
**
表示匹配任意中間目錄,如a/**/z
可以匹配a/z
,a/b/z
,a/b/c/d/z
[abc]
匹配任何一個列在方括號中的字符(要么匹配a
,要么b
,要么c
)
?
只匹配一個任意字符
[0-9]
表示所有0到9的數字;
舉個栗子:
# no .a file
*.a
# but lib.a
!lib.a
# only ignore the TODO file in the current directory, not subdir /TODO
/TODO
# ignore all files in the build/ directory
build/
# ignore doc/notes.txt, but not doc/server/arch.txt
doc/*.txt
# ignore all .pdf file in the doc/directory
doc/**/*.pdf
.gitignore
在修改完成并保存后后,也需要提交到Git。
有一些特殊的文件,雖然是被 .gitignore
文件忽略掉了,但是我們需要把這個特殊文件添加到Git,又不希望改 .gitignore
的話,可以執行命令:
$ git add -f filename.class
如果想修改 .gitignore
文件來取消篩選,而又不知道該取消哪一行時,可以執行以下命令來查找相關信息:
$ git check-ignore -v filename.class
2. Git命令別名
Git并不會在你輸入部分命令時自動推斷出你想要的命令。
如果不想每次都輸入完成的Git命令的話,可以通過 config
文件來輕松的為每一個命令設置一個別名。
比如:
$ git config --global alias.co checkout
$ git config --global alias.br branch
$ git config --global alias.ci commit
$ git config --global alias.st status
這意味著當想要出入 git commit
時,只要輸入 git ci
即可。
為了解決取消暫存文件的易用性問題,可以添加取消暫存別名:
$ git config --global alias.unstage 'reset HEAD'
這會使下面的兩個命令等價:
$ git unstage fileA // 實際執行下面一步
$ git reset HEAD fileA
通常也會加一個 last
命令:
$ git config --global alias.last 'log -1 HEAD'
這樣就可以使用命令 git last
查看最后一次提交。
如果想直接修改文件,也可以到當前用戶的目錄下的 ~/.gitconfig
或者項目目錄下的 .git/config
中修改。
參考網友提供的很6的一個方法,命令 git lg
:
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
Git的內容太多了,還有很多未能記錄的東西(服務器上的Git、分布式Git、內部原理等)。如果有需要,查看《Pro Git》吧或者以后再慢慢添加。