Git (wiki: en chs )是一個免費開源的分散式版本控制系統,由linux核心作者linus Torvalds開發,大型開源專案linux kernel、Android、chromium、mono、dotnet、UE4等都使用Git管理專案
著名github網站使用Git託管所有專案程式碼,Git的程式碼也託管在github上,連結為:github.com/git
與集中式版本控制系統(開源軟體:SVN;免費軟體:CVS;商業軟體:微軟的VSS、IBM的Rational ClearCase)相比
Git優點:
① 本地是版本庫的完整映象,因此支援離線工作
② 絕大多數操作都只需要訪問本地檔案和資源,而且與每個提交都是所有檔案的完整副本,因此速度非常快
注:SVN等集中式版本控制系統儲存每個檔案與初始化版本的差異
注:Git每個提交都是所有檔案的完整副本,使得Git在回溯到某個提交時,不會對所有檔案執行差異計算還原,因此速度會非常快
③ 強大快捷的分支功能,非常適合非線性開發過程
Git缺點:
① 只能全量整體,而不能以子目錄和分支為單位進行更新、提交等操作
② 子目錄和分支不能單獨進行許可權控制
③ 由於每個提交都是所有檔案的完整副本,因此更佔磁碟空間
這使得原始碼、配置檔案等更適合用Git來管理,而資源等較大的二進位制檔案則容易導致版本庫體積膨脹
在專案實踐中,對於資源等較大的二進位制檔案可以採用Git-LFS來管理,UE4則是使用自己開發的GitDependencies來管理
基本概念
origin:預設遠端版本庫名
master:預設分支名
origin/master:遠端預設分支名
HEAD:當前分支頂端Commit的別名,即當前分支最近的一個提交的SHA-1雜湊值
ORIG_HEAD:上次HEAD指標的位置。注:當執行git reset/git pull/git merge命令時,git會把老的HEAD拷貝到檔案.git/ORIG_HEAD中,在後續命令中可以使用ORIG_HEAD引用這個提交
commit(提交):每個commit都是全部檔案的完整快照,並用一個 commitID(基於檔案的內容或目錄結構計算出來的40位十六進位制的SHA-1雜湊值) 來唯一標誌。從某個角度上來說,Git維護的就是一個commitID有向無環圖
detached HEAD:HEAD沒有指向任何分支的狀態。一般有以下幾種情況會出現這種情況:
① 使用checkout命令跳到某個沒有分支指著的commit時
② rease處理衝突時所處的狀態
③ 切換到某個遠端分支cache上時
在Git中,在執行命令時,一定要清楚:你在哪?對誰執行這個命令?
本文使用git版本為:git version 2.13.0.windows
執行命令列建議使用:git bash(可通過右鍵選單 Git Bash here來啟動),主要有3個原因:
① 在windows的cmd下執行git log等需要顯示多頁內容的命令時,會導致cmd卡死(有時按Q鍵也沒法退出)
② git bash中可以使用MinGW中自帶的linux環境下常用的命令工具
③ git bash著色做得更好,利於閱讀
圖解常見操作
Working Directory:即工作區。作業系統層面的目錄樹結構,也可以理解為一個tree目錄物件
Stage(Index):即暫存區,為等待Commit的檔案列表。是以扁平的檔案清單實現的,不過從理解層面上也可以理解為tree目錄物件
Local Repository(History):本地版本庫。有向無環圖,其每一個節點都是一個tree目錄物件
Remote Repository:遠端版本庫。有向無環圖,其每一個節點都是一個tree目錄物件
注:圖中git checkout -- <file>①②步驟的含義是當在暫存區中有修改時,優先使用暫存區中的修改覆蓋工作區
svn命令對比一覽
svn | git | 說明 |
svn checkout | git clone | 檢出專案 |
svn update | git fetch git pull | 更新 |
svn commit | git commit git push | 提交 |
svn add | git add | 新增 |
svn mv | git mv | 移動 |
svn rm | git rm | 刪除 |
svn status | git status | 檢視狀態 |
svn log | git log | 檢視log |
svn diff | git diff | 檢視差異 |
svn revert | git checkout git reset git revert | 撤銷、丟棄修改 |
svn copy | git checkout -b/-B git branch | 建立分支 |
svn switch | git checkout | 切換分支 |
svn copy | git tag | 建立tag |
svn merge | git merge git rebase | 分支合併 |
檔案儲存機制
Git儲存使用的是一個內容定址的檔案系統,其核心部分是一個簡單的鍵值對(key-value)資料庫,當向資料庫中插入任意型別的內容,它會返回一個40位十六進位制的SHA-1雜湊值用作索引。
在版本庫中,Git維護的資料結構有:以下4種物件及索引,並通過儲存commitID有向無環圖的log日誌來維護與管理專案的修訂版本和歷史資訊。
blob -- 1個blob儲存1個檔案的1個版本的資料
tree -- 表示1個目錄,記錄著目錄裡所有檔案blob雜湊值、檔名子目錄名及其他後設資料。通過遞迴引用其他目錄樹,從而建立一個包含檔案和子目錄的完整層次結構
commit -- 1個提交物件儲存版本庫中每一次變化的後設資料,每個提交物件指向一個版本的git目錄樹物件
tag -- 分為輕量標籤和附註標籤。輕量標籤實際上是一個特定提交的引用,附註標籤是儲存在git中的一個完整可被校驗的物件(儲存在.git/refs/tags中),還包含打標籤者的名字、e-mail、日誌、註釋等資訊
git使用zlib將頭部資訊(物件型別:blob或tree或commit + 1個空格 + 資料內容長度 + 1個空位元組)和物件資料拼接一起的內容進行壓縮儲存成一個檔案
壓縮的檔案被十六進位制的SHA-1雜湊值命名,該檔案可以用pigz.exe -dz < 檔案路徑來解壓檢視。注:windows版的pigz.exe可以從這兒 下載
40位十六進位制的SHA-1雜湊值 = sha1("blob/tree/commit " + filesize + "\0" + data) 如:sha1("blob 7\0foobar\n") = "323fae03f4606ea9991df8befbb2fca795e648fa" 注:\n的二進位制為0a
底層命令 -- 剖析Git物件
find .git/objects -type f // 用find命令檢視.git/objects目錄(遞迴子目錄)中的所有檔案
git rev-list --objects --all // 檢視所有git物件的SHA-1雜湊值與檔名的對應關係
git rev-list --objects --all | grep 83c4fbc43a6f187d4e8a247a1c9aced872b2315d // 檢視SHA-1雜湊值為83c4fbc43a6f187d4e8a247a1c9aced872b2315d的檔名
echo "Hello World!" | git hash-object --stdin // 計算內容為Hello World!檔案的SHA-1雜湊值
echo "Hello World!" | git hash-object -w --stdin // 計算內容為Hello World!檔案的SHA-1雜湊值並寫入到當前git本地版本庫中
git hash-object README.txt // 檢視README.txt的SHA-1雜湊值
git hash-object -w README.txt // 檢視README.txt的SHA-1雜湊值並寫入到當前git本地版本庫中
git cat-file -p master^^{tree} // 檢視master分支HEAD指標git目錄(tree物件)下的各子目錄(tree物件)和檔案(blob物件)的SHA-1雜湊值
100644 blob 7abd3a56703ad4a7120571967f5d06607b5e5502 README.txt
040000 tree 9f448c40e684dc38109574007c661277c815fb7e ss
注:040000:表示目錄 100644:表示一般檔案 100755:表示可執行檔案 120000:表示符號連結
git cat-file -p 7abd3a56703ad4a7120571967f5d06607b5e5502 // 檢視SHA-1雜湊值為7abd3a56703ad4a7120571967f5d06607b5e5502檔案的內容
git show 7abd3a56703ad4a7120571967f5d06607b5e5502 // 檢視SHA-1雜湊值為7abd3a56703ad4a7120571967f5d06607b5e5502檔案的內容
git cat-file -t f3961f5 // 檢視f3961f5提交物件的型別:顯示為commit
git cat-file -p f3961f5 // 檢視f3961f5提交物件的資訊:包含git目錄(tree物件)、上次提交物件的SHA-1雜湊值及提交時Author、Date和註釋資訊
tree ead34240822030a3f71df4fc351057d80d7d83f8
parent 33d5bbc5d61b024aab5078e40548c4e3da808e0e
author nicochen <nicochen@tencent.com> 1537258258 +0800
committer nicochen <nicochen@tencent.com> 1537258258 +0800
123 desc txt
git cat-file -p tag1.0 // 檢視輕量標籤或附註標籤tag1.0資訊
git cat-file tag tag1.0 // 檢視附註標籤tag1.0資訊
git ls-tree ead34240822030a3f71df4fc351057d80d7d83f8 // 檢視tree目錄物件ead34240822030a3f71df4fc351057d80d7d83f8中包含的blob檔案物件和tree目錄物件
git ls-tree HEAD // 檢視HEAD所指向tree目錄物件中包含的blob檔案物件和tree目錄物件
git verify-pack -v .git/objects/pack/pack-a9282552b62cbe3f255fbb20374695a17c1ba2a2.idx // 檢視pack-a9282552b62cbe3f255fbb20374695a17c1ba2a2.pack壓縮包中的內容
git update-index n.txt // 將修改狀態的n.txt檔案新增到暫存區
git update-index --add n.txt // 將未追蹤狀態或修改狀態的n.txt檔案新增到暫存區
git update-index --add --cacheinfo 100644 5d11580eed65ffd34b6786274a60460b3582aa7d n.txt // 使用型別為100644、SHA-1雜湊值為5d11580eed65ffd34b6786274a60460b3582aa7d的資訊將追蹤狀態或修改狀態的n.txt新增到暫存區
git write-tree // 將整個暫存區內容生成一個tree物件,並輸出其SHA-1雜湊值
echo "add n.txt" | git commit-tree 31b7ca405196ca9e8fb4d5404b315bef9f2c841f -p HEAD // 用git write-tree得到的31b7ca405196ca9e8fb4d5404b315bef9f2c841f樹物件建立一個註釋為add n.txt的提交物件,並將提交物件的父親設定為當前HEAD
git update-ref refs/heads/master 372aa8e425b57ca30e2974b8e7737133caaa0b7f // 若當前分支為master,更新HEAD指向上面git commit-tree命令得到的372aa8e425b57ca30e2974b8e7737133caaa0b7f提交物件,此時用git log就可以看到這條commit記錄
git write-tree --prefix=ss // 將暫存區中ss目錄下的內容 生成一個tree物件,並輸出其SHA-1雜湊值
git update-ref -d refs/remotes/origin/v1.0 // 刪除v1.0遠端分支cache
git update-index --chmod=+x engine_mac.sh // 為engine_mac.sh增加可執行許可權(linux、unix、mac os x系統上需要)
命令大全
檢視命令幫助
git config --help // 檢視git config命令詳細用法
git help config // 功能同上
配置
git config --global user.name "kekec" // 配置提交使用者名稱
git config --global user.email "kekec@qq.com" // 配置e-mail資訊
git config --global core.editor vim // 配置預設文字編輯器,當Git 需要你輸入資訊時會呼叫它
git config --global alias.st status // 為status配置別名st,這樣git status就可以寫成git st
git config --list // 檢視當前倉庫的所有配置資訊(包括分支相關的資訊)
git config user.name // 檢視當前倉庫的使用者名稱資訊
git config -e --global // 編輯全域性配置檔案(使用者名稱和e-mail資訊就記錄在其中) 所在目錄:c:/users/<使用者名稱>/.gitconfig
git config -e // 編輯當前倉庫的配置檔案 所在目錄:.git\config
建立版本庫
git init // 在當前目錄建立一個空的git程式碼庫
git init MyGame // 在當前目錄建立一個名為MyGame的資料夾,然後在其中建立一個空的git程式碼庫
.git目錄結構如下:
hooks:不同操作時執行的hook指令碼
info/exclude:與.gitignore檔案(該檔案需放在.git資料夾的同級目錄中,windows下可通過命令列type nul > .gitignore來建立)一樣,用作檔案過濾。不同的是:該檔案不會提交到版本庫,因此過濾只對本地生效,不影響其他人
# 忽略所有.so 結尾的檔案
*.so
# 但 game.so 除外
!game.so
# 僅僅忽略專案根目錄下的 README.md 檔案,不包括 subdir/README.md
/README.md
# 忽略 .svn/ 目錄下的所有檔案
.svn/
# 會忽略 doc/notes.txt 但不包括 doc/server/arch.txt
doc/*.txt
# 忽略 doc/ 目錄下所有副檔名為 txt 的檔案
doc/**/*.txt複製程式碼
logs/refs/heads:各個本地分支的版本log記錄
logs/refs/remotes:各個遠端分支cache的log記錄
logs/refs/stash:儲藏區資料
logs/HEAD:git操作記錄
objects:2級檔案索引(把SHA-1雜湊值拆成了:2位+38位),儲存commit資料、blob檔案資料和tree目錄資料
objects/pack:pack檔案為儲存commit、tree目錄及blob檔案的壓縮資料;idx檔案為pack檔案中各資料物件的索引
objects/info/packs:該檔案記錄所有git庫的pack檔案列表
refs/heads:各個本地分支HEAD
refs/remotes:各個遠端分支cache的HEAD
refs/tags:各個附註標籤的資訊
COMMIT_EDITMSG:上一次提交的註釋
config:版本庫相關的配置資訊
description:倉庫描述資訊,供gitweb程式使用
index:暫存區相關的資訊
HEAD:指向當前分支的最近提交(如:ref: refs/heads/master)
ORIG_HEAD:執行git merge/git pull/git reset操作時,會把調整為新值之前的先前版本的HEAD記錄到OERG_HEAD中,用於恢復或回滾之前的狀態
FETCH_HEAD:git fech將所有抓取分支的HEAD記錄到.git/FETCH_HEAD中
MERGEHEAD:正在合併進HEAD的commit id
packed-refs:遠端版本庫cache和遠端標籤cache
日誌與檔案狀態
git reflog // 檢視操作記錄 注:每條操作記錄使用HEAD@{n}來標識
git show HEAD@{5} // 檢視索引為5的操作記錄的詳細資訊
git status // 檢視當前所處的分支暫存區和工作區的檔案(會顯示當前所處分支)
注1:處於暫存區的檔案狀態::staged(已暫存);處於工作區的檔案狀態::untrack(未跟蹤)、modified(已修改)
注2:工作區中的空目錄不會被git追蹤
git status -s --ignored // 以簡潔模式檢視暫存區和工作區的檔案(全部顯示,不執行檔案過濾)
git status -uno // 檢視暫存區和工作區的非untrack(未跟蹤)狀態檔案
git status -uall // 檢視暫存區和工作區的狀態檔案(遞迴子目錄顯示出裡面的檔案)
git log // 檢視本地版本庫提交記錄(會顯示當前所處分支,HEAD指標指向哪個分支的哪條提交)
git log --stat // 檢視本地版本庫提交記錄(會顯示當前所處分支,HEAD指標指向哪個分支的哪條提交和每次提交的檔案變更簡略統計資訊)
git log -- README.md // 檢視README.md檔案的本地版本庫提交記錄
git log --graph -- README.md // 以圖形化方式檢視README.md檔案的本地版本庫提交記錄
git log -p README.md // 檢視README.md檔案的本地版本庫提交記錄(顯示出每次的修改內容)
git log --grep "test" // 顯示註釋中含有test字串的提交
git log --author=kekec // 檢視本地版本庫中作者為kekec的提交記錄
git log -S "SplitPath(FString& str)" // 檢視SplitPath(FString& str)內容是什麼時候加到專案中那些檔案中去的
git log --since=2.weeks // 檢視最近2周的提交記錄
git log --since="2 weeks 3 days 2 hours 30 minutes 59 seconds ago" // 檢視2周3天2小時30分59秒以前的提交記錄
git log --after="2018-10-7" --before="2018-10-12" // 檢視2018.10.7~2018.10.12之間的提交記錄
git log --since="2018-10-7" --until="2018-10-12" // 功能同上:git log --after="2018-10-7" --before="2018-10-12"
注:--since、--until 標記和 --after、--before 標記分別是等價的
git whatchanged README.md // 檢視README.md檔案的本地版本庫提交記錄(包括檔案改名)
git log --follow README.md // 功能同上:git whatchanged README.md
git log -3 // 檢視最近3條本地版本庫提交記錄
git log -3 --pretty --oneline // 檢視最近3條本地版本庫提交記錄(簡潔模式,一行顯示一個提交)
git log --graph --oneline // 以圖形化簡潔模式檢視當前分支的本地版本庫提交記錄
git log release --graph --oneline // 以圖形化簡潔模式檢視release分支的本地版本庫提交記錄
git log --graph --oneline --no-merges // 以圖形化簡潔模式檢視當前分支的本地版本庫提交記錄(過濾merge過來的提交)
git log --graph --oneline --merges // 以圖形化簡潔模式檢視當前分支的本地版本庫提交記錄(只顯示有2個及以上父親節點的提交)
git log --graph --oneline --name-only // 以圖形化簡潔模式檢視當前分支的本地版本庫提交記錄(並顯示每次提交的檔名稱清單)
git log --graph --oneline --name-status // 以圖形化簡潔模式檢視當前分支的本地版本庫提交記錄(並顯示每次提交的檔案狀態、名稱清單)
git log --graph --oneline --stat // 以圖形化簡潔模式檢視當前分支的本地版本庫提交記錄(並顯示每次提交的檔案變化統計、各檔名及增刪記錄)
git log --graph --oneline --shortstat // 以圖形化簡潔模式檢視當前分支的本地版本庫提交記錄(並顯示每次提交的檔案變化統計及增刪記錄)
git log --graph --oneline --decorate --all // 以圖形化簡潔模式檢視所有分支的本地版本庫提交記錄樹
git log --graph --pretty=format:"%H - %an, %ad : %s" // 自定義格式圖形化檢視所有分支的本地版本庫提交記錄樹
%H 提交物件(commit)的完整雜湊字串
%h 提交物件的簡短雜湊字串
%T 樹物件(tree)的完整雜湊字串
%t 樹物件的簡短雜湊字串
%P 父物件(parent)的完整雜湊字串
%p 父物件的簡短雜湊字串
%an 作者(author)的名字
%ae 作者的電子郵件地址
%ad 作者修訂日期(可以用 --date= 選項定製格式)
%ar 作者修訂日期,按多久以前的方式顯示
%cn 提交者(committer)的名字
%ce 提交者的電子郵件地址
%cd 提交日期
%cr 提交日期,按多久以前的方式顯示
%s 提交說明
git log master..v5.0 // 檢視v5.0分支還未合併到master分支上的提交記錄列表
git log v5.0..master // 檢視master分支還未合併到v5.0分支上的提交記錄列表
git log master...v5.0 // git log master..v5.0 + git log v5.0..master
git shortlog -sn // 統計各個提交者的次數
git blame README.md // 顯示README.md最近一次的修改資訊
git show 3a6c702376168aa15a2f3d7bc98000d07a70d023 README.md // 檢視README.md檔案的3a6c702376168aa15a2f3d7bc98000d07a70d023提交的修改內容
git show HEAD // 檢視最近一次提交的修改內容
git show --name-only HEAD // 檢視最近一次提交的檔案列表(不顯示具體的修改內容)
標籤(檢視/新建/切換/刪除)
git tag // 列出所有的標籤
git tag -l 'tag1*' // 列出所有tag1開頭的標籤
git tag tag1.0 // 建立名為tag1.0的輕量標籤
git tag -a tag1.0 -m "tag1.0 desc" // 新增tag1.0 desc註釋並建立名為tag1.0的附註標籤
git tag tag2.0 abffefc5d82078cbaea7fcbb5106ab0c21cbeba9 // 在abffefc5d82078cbaea7fcbb5106ab0c21cbeba9提交處建立名為tag2.0的輕量標籤
git tag -a tag2.0 -m "tag2.0 desc" abffefc // 在abffefc提交處建立名為tag2.0的附註標籤
git tag -d tag2.0 // 刪除名為tag2.0的標籤
git show tag1.0 // 檢視名為tag1.0相關的資訊
git ls-remote --tags // 檢視所有遠端的標籤
分支(檢視/新建/切換/刪除)
git branch // 列出所有本地分支
git branch -r // 列出所有遠端分支cache
git branch -a // 列出所有本地分支和遠端分支cache
git branch -av // 列出所有本地分支和遠端分支cache(含簡單說明)
git branch -vv // 檢視所有本地分支和遠端分支cache之間的追蹤關係
git branch v1.0 // 在當前分支的HAED指標下建立名為v1.0的分支(建立完不會切到v1.0分支上)
git branch --track v1.0 origin/v1.0 // 若v1.0分支不存在則先新建,然後將其與遠端分支origin/v1.0建立追蹤關係 ① 遠端分支origin/v1.0要存在,否則命令執行失敗 ② 執行完不會切到v1.0分支上
git branch v2.0 372aa8e425b57ca30e2974b8e7737133caaa0b7f // 在372aa8e425b57ca30e2974b8e7737133caaa0b7f提交處建立名為v2.0的分支(建立完不會切到v2.0分支上)
git branch -m v1.0 x1.0 // 將分支v1.0重新命名為x1.0
git checkout v1.0 // 切換到v1.0分支上(v1.0分支不存在則命令執行失敗)
git checkout -b v1.0 // 建立並切換到v1.0分支上(v1.0分支存在則命令執行失敗)
git checkout -B v1.0 // 不存在則建立,並切換到v1.0分支上
git checkout -b v1.0 5a95f2d // 在5a95f2d提交處建立並切換到v1.0的分支上
git checkout -b v1.0 tag1.0 // 在標籤tag1.0處建立並切換到v1.0的分支上
git checkout -t origin/v1.0 // 建立並切換到origin/v1.0遠端分支cache的名為v1.0本地分支上,並建立兩者追蹤關係(本地分支v1.0存在則命令執行失敗)
git checkout -b x1.0 -t origin/v1.0 // 建立並切換到origin/v1.0遠端分支cache的名為x1.0本地分支上,並建立兩者追蹤關係(本地分支x1.0存在則命令執行失敗)
注1:切換分支前,必須處理工作區(未追蹤的檔案不用處理)和暫存區的修改才能切換成功
注2:切換成功後,工作區會被設定成分支的內容
注3:不允許在遠端分支cache上提交,需要建立對應關聯的本地分支,然後在本地分支上進行提交
git checkout -f v1.0 // 強制切換到v1.0分支上,丟棄暫存區和工作區中的所有檔案的修改(工作區中未追蹤的檔案不受影響)
git checkout -f -B v1.0 origin/v1.0 // 不存在則建立,強制切換到v1.0分支上,丟棄暫存區和工作區中的所有檔案的修改,並將HEAD指向origin/v1.0處(工作區中未追蹤的檔案不受影響)
git checkout - // 切換到上一次分支
git branch -d v2.0 // 刪除名為v2.0的分支(必須先切到其他分支上才能執行刪除操作)
git branch -D v2.0 // 強制刪除名為v2.0的分支(必須先切到其他分支上才能執行刪除操作)
git branch -dr origin/v2.0 // 刪除遠端分支origin/v2.0 cache
檔案(增加/刪除/提交/撤銷)
git add README.md // 將當前目錄下的README.md檔案加入到暫存區
git add . // 將當前目錄下(遞迴子目錄)所有檔案加入到暫存區
git add -u . // 將當前目錄下(遞迴子目錄)所有追蹤狀態的檔案加入到暫存區
git add Doc/\*.txt // 將當前目錄的Doc資料夾下(遞迴子目錄)所有txt字尾的檔案加入到暫存區
git rm README.md // 刪除工作區檔案,並且將這次刪除放入暫存區(若README.md在工作區或暫存區中有修改,命令會執行失敗)
git rm -f README.md // 強制刪除工作區檔案,並且將這次刪除放入暫存區(即使README.md在工作區或暫存區中有修改,也會執行刪除操作)
git rm --cached README.md // 不刪除工作區對應的檔案,只將README.md刪除放入暫存區以供提交
git mv README.md test.md // 將README.md改名為test.md,並且將這個改名放入暫存區
git commit -m "desc" // 新增desc註釋並將暫存區中的所有修改提交到本地倉庫
git commit README.md -m "desc" // 新增desc註釋並將暫存區中的README.md的修改提交到本地倉庫
git commit --amend -m "desc" // 新增desc註釋使用當前提交覆蓋上一次的提交(若上一次提交包含1.txt和2.txt的修改,當前提交只包含1.txt的修改;執行命令後,本地版本庫中為本次的1.txt和上一次2.txt)。若沒有提交內容,則用來改寫上一次提交的日誌資訊
git commit -m "desc" --amend README.txt // 新增desc註釋使用README.txt的當前提交覆蓋上一次的提交
git commit -a -m "desc" // 新增desc註釋並將工作區和暫存區中的所有修改提交到本地倉庫
git commit -am "desc" // 功能同上
git commit -c b5cad94d229e72bd7aff5fe2c6f022b29c30e7a8 // 拿372aa8e425b57ca30e2974b8e7737133caaa0b7f提交的資訊(作者、提交者、註釋、時間戳等)來提交當前修改
git reset -- README.md // 丟棄暫存區中的README.md檔案的修改
git reset README.md // 功能如上 丟棄暫存區中的README.md檔案的修改
git reset b5cad94 README.md // 使用本地版本庫b5cad94提交處的README.md版本覆蓋暫存區中的README.md
git reset // 丟棄暫存區中的所有檔案的修改(工作區不受影響)
git reset --mixed // --mixed為預設引數,命令與上面git reset一樣
git reset --hard // 丟棄暫存區和工作區中的所有檔案的修改(工作區中未追蹤的檔案不受影響)
git reset --soft b5cad94d229e72bd7aff5fe2c6f022b29c30e7a8 // 僅將當前分支的HEAD指向372aa8e425b57ca30e2974b8e7737133caaa0b7f提交(暫存區和工作區中的所有檔案的修改都不丟棄)
git reset --soft HEAD~ // 僅將當前分支的HEAD指向上一次提交(暫存區和工作區中的所有檔案的修改都不丟棄)
git reset --soft HEAD~2 // 僅將當前分支的HEAD指向上兩次提交(暫存區和工作區中的所有檔案的修改都不丟棄)
git reset --merge <commit> // 在被汙染的工作區中回滾merge或者pull
$ git pull (1)
Auto-merging nitfol
Merge made by recursive.
nitfol | 20 +++++----
...
$ git reset --merge ORIG_HEAD (2)複製程式碼
(1) 即便你已經在本地更改了一些你的工作區,你也可安全的git pull,前提是你知道將要pull的內容不會覆蓋你的工作區中的內容。
(2) git pull完後,你發現這次pull下來的修改不滿意,想要回滾到pull之前的狀態,我們可以執行git reset --hard ORIG_HEAD,但是這個命令有個副作用就是清空你的工作區,即丟棄你的本地未add的那些改變。
為了避免丟棄工作區中的內容,可以使用git reset --merge ORIG_HEAD,注意其中的--hard 換成了 --merge,這樣就可以避免在回滾時清除工作區。
git reset --keep <commit> // 保留工作區並丟棄一些之前的提交
假設你正在編輯一些檔案,並且已經提交,接著繼續工作,但是現在你發現當前在工作區中的內容應該屬於另一個分支,與之前的提交沒有什麼關係。此時,可以開啟一個新的分支,並且保留著工作區中的內容。
$ git tag start
$ git checkout -b branch1
$ edit
$ git commit ... (1)
$ edit
$ git checkout -b branch2 (2)
$ git reset --keep start (3)複製程式碼
(1) 這次是把在branch1中的改變提交了。
(2) 此時發現,之前的提交不屬於這個分支,此時新建了branch2分支,並切換到了branch2上。
(3) 此時可以用reset --keep把在start之後的提交清除掉,但是保持工作區不變。
git checkout -- README.md // -- 符號非常重,否則就變成了切換到README.md分支了
// 當README.md在暫存區中有修改時,使用暫存區中的修改覆蓋工作區中的README.md
// 當README.md不在暫存區中時,使用本地版本庫中的HEAD指標處的修改覆蓋工作區中的README.md
git checkout -- . // 使用暫存區和本地版本庫來恢復當前目錄(遞迴子目錄)下的所有檔案 注:若暫存區中有修改,優先使用暫存區
git checkout HEAD README.md // 使用本地版本庫中的HEAD處提交覆蓋暫存區和工作區中的README.md
git checkout 9a387f22ff949fa16336508adc2284384bd6a890 README.md // 使用本地版本庫中的9a387f22ff949fa16336508adc2284384bd6a890修改覆蓋暫存區和工作區中的README.md
git checkout -b v2.0 tag2.0 // 在名為tag2.0的提交處建立並切換到v2.0分支上(v2.0分支存在則命令執行失敗)
git revert --no-edit 3a6c702376168aa15a2f3d7bc98000d07a70d023 // 回滾3a6c702376168aa15a2f3d7bc98000d07a70d023提交,然後提交到本地倉庫
git revert HEAD~ // 回滾HEAD的上一次提交,然後會彈出vim環境編輯註釋(輸入:q直接使用預設註釋內容、輸入:q!放棄修改使用預設註釋內容、輸入:x或:wq儲存當前修改的註釋內容),然後提交到本地倉庫
git revert -n HEAD~3 // 回滾掉HEAD~3處的提交,不自動提交到本地倉庫
git revert -n HEAD~2..HEAD // 回滾掉(HEAD~2, HEAD]之間的2次提交,不自動提交到本地倉庫
注:git reset是把HEAD向後移動來刪除提交,而git revert是用一次新的提交來回滾之前的提交(HEAD會繼續前進)
檢視差異
git diff README.md // 檢視當前目錄下的README.md在工作區和暫存區之間的差異
git diff --cached README.md // 檢視當前目錄下的README.md在暫存區和本地倉庫最後一次提交之間的差異
git diff --cached 372aa8e425b57ca30e2974b8e7737133caaa0b7f README.md // 檢視當前目錄下的README.md在暫存區和本地倉庫的372aa8e425b57ca30e2974b8e7737133caaa0b7f提交之間的差異
git diff HEAD README.md // 檢視當前目錄下的README.md在工作區和本地倉庫HEAD指標處提交之間的差異
git diff 372aa8e425b57ca30e2974b8e7737133caaa0b7f README.md // 檢視當前目錄下的README.md在工作區和本地倉庫的372aa8e425b57ca30e2974b8e7737133caaa0b7f提交之間的差異
git diff 372aa8e425b57ca30e2974b8e7737133caaa0b7f HEAD README.md // 檢視當前目錄下的README.md在本地倉庫的372aa8e425b57ca30e2974b8e7737133caaa0b7f提交和最後一次提交之間的差異
git diff 372aa8e425b57ca30e2974b8e7737133caaa0b7f HEAD // 檢視本地倉庫的372aa8e425b57ca30e2974b8e7737133caaa0b7f提交和最後一次提交之間的差異
git diff 372aa8e b5cad94 README.md // 檢視當前目錄下的README.md在本地倉庫的372aa8e提交和b5cad94提交之間的差異
注:可以將git diff換成git difftool來使用外部diff工具(可以在c:/users/<使用者名稱>/.gitconfig檔案配置beyond compare作為預設的difftool和mergetool)來檢視差異
[diff]
tool = bc3
[difftool]
prompt = false
[difftool "bc3"]
cmd = "\"e:/program files (x86)/beyond compare 3/bcomp.exe\" \"$LOCAL\" \"$REMOTE\""複製程式碼
分支合併
git merge-base Master Feature // 檢視Master和Feature分支的最優共同commit父節點
git merge Feature // 將Feature分支merge合併到當前分支Master(無衝突時會直接提交)
git merge -m "merge test" Feature // 將Feature分支merge合併到當前分支Master(無衝突時使用merge test註釋直接提交)
git merge --no-commit Feature // 將Feature分支merge合併到當前分支Master(不自動提交)
git rebase Feature // 將Feature分支rebase合併到當前分支Master
注1:git rebase會先找出共同的祖先節點,從祖先節點把Feature分支的提交記錄全都剪下下來,然後合到Master 分支(合併前後commitID會不一樣)
注2:相對來說,git merge處理衝突更直接,但會增加一些冗餘的提交記錄;而git rebase能夠保證清晰線性的提交記錄,但這也將合併的操作沒有被記錄下來
注3:最好是用git rebase合併遠端分支到本地,git merge合併Feature分支到Master分支
注4:在合併Feature分支到Master分支前,務必先執行git pull -r origin Feature來進行遠端分支與本地分支的rebase合併
注5:處於衝突狀態(conflict)的檔案為UU(可通過git status -s --ignored來查詢),手動處理完衝突後,然後使用git add該檔案,最後繼續執行git merge/rebase --continue來完成合並的提交工作
注6:README.md檔案衝突內容如下
<<<<<<< HEAD
123 456 789 000 111 222 333 444 555 ss // 當前分支的內容
=======
123 456 789 000 ss tt // Feature分支的內容
>>>>>>> Feature
注7:可以使用git mergetool來使用外部merge工具(可以在c:/users/<使用者名稱>/.gitconfig檔案配置beyond compare作為預設的mergetool)來處理衝突。
修改完當前檔案後,可再次呼叫git mergetool來處理下一個衝突,直至全部處理完畢,然後使用git add該檔案,最後繼續執行git merge/rebase --continue來完成合並的提交工作
[merge]
tool = bc3
[mergetool]
prompt = false
[mergetool "bc3"]
cmd = "\"e:/program files (x86)/beyond compare 3/bcomp.exe\" \"$LOCAL\" \"$REMOTE\" \"$BASE\" \"$MERGED\""複製程式碼
git rebase /i Feature // 將Feature分支採用手動互動方式rebase合併到當前分支Master
pick 07c5abd Introduce OpenPGP and teach basic usage
pick de9b1eb Fix PostChecker::Post#urls
pick 3e7ee36 Hey kids, stop all the highlighting
pick fa20af3 git interactive rebase, squash, amend
# Rebase 8db7e8b..fa20af3 onto 8db7e8b
#
# 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
git merge/rebase --abort // 撤銷當前merge或rebase操作
git merge/rebase --skip // 強制使用Feature分支的內容
git merge/rebase --continue // 手動處理完衝突後使用git add該檔案,最後繼續執行git merge/rebase --continue來完成合並的提交工作
git merge origin/master // fetch完之後,可以將遠端分支cache master分支merge合併到當前分支上
git rebase origin/master // fetch完之後,可以將遠端分支cache master分支rebase合併到當前分支上
git rebase --onto master 76cada~ // 將當前分支從[76cada, HEAD]區間段的提交ebase合併到master上
git cherry-pick 9a341e // 將9a341e提交合入當前分支。若不衝突,則直接使用9a341e的提交資訊進行commit,否則要先進行衝突處理,然後繼續執行git cherry-pick --continue來完成合並的提交工作
git cherry-pick 371c2…971209 // 將(371c2, 971209]提交合入當前分支(每個提交都會在當前分支上建立一個commit)
git cherry-pick 371c2~…971209 // 將 [371c2, 971209] 提交合入當前分支(每個提交都會在當前分支上建立一個commit)
git cherry-pick -n 9a341e d2f99e // 將9a341e和d2f99e提交合入當前分支(不提交),後續需要手動commit
git cherry-pick --abort // 撤銷當前cherry-pick操作
git cherry-pick --quit // 清理當前操作狀態,不撤銷修改強制退出cherry-pick操作過程
git cherry-pick --continue // 手動處理完衝突後,最後繼續執行git cherry-pick --continue來完成合並的提交工作
檢視遠端版本庫
git remote -v // 顯示遠端倉庫的URL 注:由於git是分散式的,所有遠端倉庫可能有很多個
origin https://github.com/kekec/Test.git (fetch)
origin https://github.com/kekec/Test.git (push)
git remote-ls // 檢視遠端倉庫URL和分支資訊
From https://github.com/kekec/Test.git
fae0fc82d711425daa897a63137d7e1af09512ba HEAD
fae0fc82d711425daa897a63137d7e1af09512ba refs/heads/master
git remote // 檢視遠端倉庫名稱 一般為origin
git remote rename origin test // 將遠端倉庫名稱從origin修改為test
git remote show origin // 顯示遠端倉庫的資訊
* remote origin
Fetch URL: https://github.com/kekec/Test.git
Push URL: https://github.com/kekec/Test.git
HEAD branch: master
Remote branches:
master tracked
v3.1 tracked
Local branch configured for 'git pull':
master merges with remote master
Local refs configured for 'git push':
master pushes to master (fast-forwardable)
v3.1 pushes to v3.1 (up to date)
git remote rm origin // 刪除.git/config檔案中新增remote origin相關的資訊
git remote add origin https://github.com/kekec/Test.git // 在.git/config檔案中新增remote origin指向的遠端倉庫URL(若已存在,則命令執行失敗)
[remote "origin"]
url = https://github.com/kekec/Test.git
fetch = +refs/heads/*:refs/remotes/origin/*複製程式碼
git remote set-url origin https://github.com/kekec/Test.git // 修改.git/config檔案中新增remote origin指向的遠端倉庫URL
git remote prune origin // 對於遠端倉庫不存在的分支,清除對應的遠端分支cache
遠端操作
git clone https://github.com/kekec/Test.git // 將https://github.com/kekec/Test.git上的當前分支克隆到本地(會建立一個名為Test目錄,遠端倉庫名稱使用預設名origin)
git clone https://github.com/kekec/Test.git MyProject // 將https://github.com/kekec/Test.git上的當前分支克隆到本地(會建立一個名為MyProject目錄,遠端倉庫名稱使用預設名origin)
git clone -b v1.0 https://github.com/kekec/Test.git // 將https://github.com/kekec/Test.git上的v1.0分支克隆到本地(會建立一個名為Test目錄,遠端倉庫名稱使用預設名origin)
git clone -b v1.0 https://github.com/kekec/Test.git d:\MyGame // 將https://github.com/kekec/Test.git上的v1.0分支克隆到d:\MyGame目錄(會在d:\MyGame中建立一個名為Test目錄,遠端倉庫名稱使用預設名origin)
git clone -o TestPrj https://github.com/kekec/Test.git // 將https://github.com/kekec/Test.git上的當前分支克隆到本地(會建立一個名為Test目錄,並將遠端倉庫名稱設定為TestPrj)
git fetch origin master // 從遠端倉庫拉取master分支狀態的變化資訊(工作區檔案不會更新)
git fetch // 從遠端倉庫拉取所有分支和tag狀態的變化資訊(工作區檔案不會更新)
git fetch -p // 從遠端倉庫拉取所有分支和tag狀態的變化資訊,並清除已被刪除的遠端分支和tag在本地的快取(工作區檔案不會更新)
git fetch origin --tags // 從遠端倉庫拉取所有tag到本地(工作區檔案不會更新)
git pull <遠端倉庫名> <遠端分支名>:<本地分支名>
git pull origin master // 先執行fetch,然後將遠端origin/master分支merge合併到當前分支(最後會更新origin/master, origin/HEAD指標到最新提交)
git pull https://github.com/kekec/Test.git master // 先執行fetch,將遠端origin/master分支merge合併到當前分支(最後不會更新origin/master, origin/HEAD指標到最新提交)
git pull origin v1.0:master // 先執行fetch,然後將遠端origin/v1.0分支merge合併到本地master分支
git pull origin // 先執行fetch,然後將對應的遠端分支merge合併到當前分支(當前分支需要預存遠端分支的追蹤關係)
git pull // 先執行fetch,然後將對應的遠端分支merge合併到當前分支(當前分支需要預存遠端分支的追蹤關係,而且當前分支只有一個遠端倉庫)
git pull -p // 先執行fetch,然後將對應的遠端分支merge合併到當前分支,並清除已被刪除的遠端分支和tag在本地的快取
git pull -r origin master // 先執行fetch,然後將遠端origin/master分支rebase合併到master分支
git push <遠端倉庫名> <本地分支名>:<遠端分支名>
git push -u origin master // 將本地倉庫的修改push到origin所指向的遠端倉庫URL的master分支上,並在.git/config檔案中記錄當前分支與遠端分支master的對應關係
git push origin // 將當前分支更新推送給對應的遠端分支
git push // 將當前分支更新推送給對應的遠端分支(當前分支只有一個遠端倉庫,可以省略倉庫名origin)
git push origin -f // 使用當前分支更新強行覆蓋對應的遠端分支(合入遠端分支有衝突時,也使用當前分支更新)
git push origin v1.0 // 將本地分支v1.0更新推送給對應的遠端分支remotes/origin/v1.0
git push origin --all // 將本地所有分支更新推送給各自對應的遠端分支
git push origin tag1.0 // 將本地標籤tag1.0更新到遠端標籤tag1.0
git push origin --tags // 將本地所有標籤更新到對應的遠端標籤
git push origin :v1.0 // 刪除遠端分支v1.0
git push origin :refs/tags/tag1.0 // 刪除遠端標籤tag1.0
git push origin -d v1.0 // 刪除遠端分支v1.0 功能同上
儲藏區
git stash // 將工作區中所有檔案的修改備份壓棧到儲藏區,然後丟棄工作區與暫存區的所有檔案的修改
git stash pop // 使用儲藏區的棧頂處備份(stash@{0})來恢復當前分支的工作區,並將棧頂備份移除
git stash apply stash@{1} // 使用儲藏區的棧頂下面一個備份(stash@{1})來恢復當前分支的工作區,但不移除儲藏區中任何備份
git stash list // 檢視儲藏區棧列表
git stash show -p stash@{0} // 檢視儲藏區的棧頂處備份中各個檔案的內容
git stash drop // 直接移除儲藏區的棧頂處備份(不用於恢復當前分支的工作區)
git stash clear // 清除儲藏區棧列表
工作區
git clean -nd // 探測工作區中哪些檔案和目錄(未追蹤狀態)會被刪除
git clean -fd // 刪除工作區中未追蹤狀態的檔案和目錄
暫存區
git ls-files // 查詢暫存區中的檔案列表(遞迴子目錄)
git ls-files -s // 檢視暫存區中所有檔案的blob資料塊資訊
git ls-files -s -- README.md // 檢視暫存區中的README.md檔案的blob資料塊資訊
其他命令
git fsck --full // 列出所有未引用的blob、tree、commit物件
git archive --format zip --output d:/file.zip master // 將當前master分支所有檔案使用zip壓縮方式打包到d:/file.zip
Git瘦身
git count-objects -v // 檢視git物件的統計資訊
find .git/objects -type f -print0 | xargs -0 du -hk | sort -nr | head -5 // 查詢git庫中最大的5個檔案(du -hk中的k代表單位為KB)
find .git/objects -type f -size +1M -print0 | xargs -0 du -hm | sort -nr | head -5 // 查詢git庫中size超過1M的最大的5個檔案(du -hm中的k代表單位為MB)
git verify-pack -v .git/objects/pack/pack-b340eea7566df839294b71ec91a327ca2ece0b94.idx | sort -k 3 -nr | head -5 // 對壓縮儲存的git庫查詢最大的5個檔案
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch FramePro.cpp' --prune-empty --tag-name-filter cat -- --all // 從git庫的歷史記錄中徹底清理FramePro.cpp
git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin // 清理所有廢棄的ref引用
git gc --prune=now // ①將所有的物件壓縮儲存到pack二進位制檔案中,以節省空間和提高效率 ②移除與任何提交都不相關的陳舊物件
git reflog expire --expire=now --all // 清除所有操作記錄日誌
除了使用git原生命令外,可以使用專門的工具BFG(java實現)來對Git庫瘦身
經典Gitflow
(1) master分支儲存了正式釋出的歷史(master分支上的所有提交都會分配一個版本號)
(2) develop分支作為功能的整合分支
(3) 每個新功能位於一個自己的Feature分支,該分支使用develop分支作為父分支。當新功能完成時,合併回develop分支。新功能提交應該從不直接與master分支互動
(4) 一旦develop分支上有了做一次釋出(或者說快到了既定的釋出日)的足夠功能,就從develop分支上fork一個release分支。
新建的分支用於開始釋出迴圈,所以從這個時間點開始之後新的功能不能再加到這個分支上。 這個分支只應該做Bug修復、文件生成和其它面向釋出任務。
對外發布的工作完成後,釋出分支會合併到master分支並分配一個版本號打好Tag。另外,這些從新建釋出分支以來的做的修改要合併回develop分支。
(5) hotfix分支用於生成快速給產品釋出版本(production releases)打補丁,修復完成,修改應該馬上合併回master分支(打好Tag)和develop分支(當前的釋出分支)。
參考
《IVWEB 技術週刊》 震撼上線了,關注公眾號:IVWEB社群,每週定時推送優質文章。
週刊文章集合: weekly
團隊開源專案: Feflow