版本控制
版本控制就是記錄專案檔案的歷史變化。它為我們查閱日誌,回退,協作等方面提供了有力的幫助。
版本控制一般分為集中化版本控制和分散式版本控制。
集中化主要的版本資料都儲存服務端。
分散式版本資料分散在多端。
Git
Git屬於分散式版本控制,也是現在比較流行的一種版本管理工具。
Git專案有三個區塊:工作區 / 暫存區 / 版本庫
- 工作區存放從版本庫提取出來的檔案,供我們編輯修改;
- 暫存區儲存了下一次要提交的目錄資訊;
- 版本庫儲存專案版本後設資料和Objects資料,後文會詳解。
Git工作流程
# 下載
<<==== clone
# 上傳
====>> add ====>> commit ====>> push
# 更新
<<==== merge|rebase <<===== fetch
區分 Pull vs Fetch
我們將一個更新操作拆分為資料更新+合併處理兩部分,這樣來看 fetch 只是進行資料更新。而pull 其實是 ( fetch + (merge|rebase) )組合操作,它執行資料更新同時執行合併處理。pull 預設是fetch+merge 組合 ,也可以通過引數 --rebase 指定為 fetch + rebase。
區分Merge vs Rebase
合併處理是Git很重要的一塊知識。兩個命令在工作中也經常使用,區分它們對我們很有用。
場景如下
專案有一個mywork分支。C2時間點我和小明各自下載專案進行功能開發,小明效率比較高,先推送了C3 C4 到遠端倉庫。我本地倉庫現在有C5 C6兩個提交,要推送到遠端倉庫,需先同步遠端倉庫版本。
如果通過 fetch + merge 方式,Git會將遠端最新(C4)和本地最新(C6)進行合併併產生一個新的(C7)。
衝突處理步驟
git merge # 發生衝突會出現衝突標記
“<<<<<<< HEAD
40
=======
41
>>>>>>> 41”
# 手動處理衝突
git add .
git commit -m 'fix conflict'
git push origin HEAD
如果通過 fetch + rebase 方式,git會先將C5 C6儲存到.git/rebase零時目錄,合併成功後刪除。
衝突處理步驟
git rebase # 發生衝突會出現衝突標記
“<<<<<<< HEAD
40
=======
41
>>>>>>> 41”
# 手動處理衝突
git add .
git rebase --continue
git push origin HEAD
小結
git merge 會產生大量Merge日誌,可能會對檢視帶來不便。不過大家還是根據實際情況進行選取。
關於撤銷回退幾種場景
提交後發現有檔案漏了,又不想提交兩次。此時通過 “git commit --amend” 可以合併為一個提交。
git commit -m 'initial commit'
git add .gitignore
git commit --amend
如果檔案想撤回且尚未提交,執行下面命令撤出暫存空間(index)
git reset HEAD <file>...
關於 reset 其它用法
# 重置到指定版本,之前提交內容將丟失
git reset --hard HEAD
# 重置到指定版本,保留更改的檔案但未標記為提交
git reset --mixed HEAD
# 重置到指定版本,保留所有改動檔案
git reset –soft HEAD
特別注意 當你使用 “git reset --hard HEAD” 重置到某一版本,發現搞錯了想回退。這時你可能會執行“git log”,但是發現已經沒有以前的版本記錄,怎麼辦?送你一瓶後悔藥如下
# reflog 是Git操作的全日誌記錄
git reflog
6241462 (HEAD -> master) HEAD@{0}: reset: moving to 6241462
ea9b5ab HEAD@{1}: reset: moving to ea9b5ab
6241462 (HEAD -> master) HEAD@{2}: commit: Hello
34cd1e3 HEAD@{3}: commit: 3
ea9b5ab HEAD@{4}: commit: 2
729a8b1 (origin/master) HEAD@{5}: commit (initial): 1
# 找到最左邊對應hash值就可以回退到任意位置
git reset --hard {index}
如果想撤回檔案修改內容且檔案尚未提交,執行下面命令
git checkout -- <file>
如果建立的分支名稱需要更改
git branch -m old new
# 如果分支已經推送到遠端,先刪除再推送新分支
git push origin --delete old
git push origin new
如果需要撤回的提交已經推送到了遠端倉庫,那麼補救的方式只有建立新的提交。
可以利用revert快速撤回到需要回退的版本。
# 還原最近一個提交
git revert HEAD
# 還原倒數第二個
git revert HEAD^
# 還原倒數第第四個
git revert HEAD~3
版本庫 Objects
這一節介紹一下Git版本庫的儲存模型。
專案歷史變動資訊都記錄在object檔案。檔名稱是通過雜湊演算法 ( 這裡是SHA1(物件內容) ) 產生的40位字元。
這種做法的一個優點就是“在對比兩物件是否相同時,只需要比較檔名稱就能迅速得出結果”
雜湊演算法:簡單來說就是向函式輸入一些內容,輸出長度固定的字串。這裡SHA1函式固定輸出40長度字元。
object檔案分 blob tree commit tag 四種型別
-
blob 儲存檔案資料,一般是一個檔案;
-
tree 儲存目錄和樹的引用(子檔案目錄);
-
commit 儲存單一樹引用,時間點,提交作者,上一次提交指標;
-
tag 標記特定的commit 比如說發版。
特別注意:Subversion,CVS,Perforce,Mercurial等是儲存前後兩次提交的差異資料。Gi-每次提交時,它都會以樹狀結構儲存專案中所有檔案的外觀快照。
Blob
Blob 是二進位制資料塊,不會引用其它東西。如果目錄樹(或儲存庫中多個不同版本)中的兩個檔案具有內容相同,它們將共享相同的Blob物件。
Tree
Tree 儲存blob和tree的引用。
# 我查詢 add1a1306e20...
git ls-tree add1a1306e20...
100644 blob 4661b39c3460a5c1f9e9309e6341962e0499b037 README.md
040000 tree ad46b24a4b0648ede3ca090dde32c89b89f7f2c1 src
...
Commit
Commit 包含下面幾個資訊
- tree 提交時間點的目錄;
- parent 上一個提交;
- author 提交人;
git show -s --pretty=raw add1a1306e....
commit add1a1306e....
tree 81d4e4271a56575da7f992dc0dfc72ff7ddff94c
parent cd397e4c373013b19825b857b43ad8f677607f5d
author lixingping <lixingping233@gmail.com> 1589783810 +0800
committer lixingping <lixingping233@gmail.com> 1589783810 +0800
Tag
git cat-file tag v_1.0
object 24d16acd6aa08f74556c7ce551fa571b4bfe4079
type commit
tag v_1.0
tagger lixingping <lixingping233@gmail.com> 1588591122 +0800
例子
假設專案目錄結構如下,我們進行一個初始提交。幾種檔案關係如下圖
|-- read.txt
--| lib
--| hello.java
附上一些常用命令
生成SSH key
ssh-keygen -t rsa -b 4096 -C "email@example.com"
# 指定生成的檔案
ssh-keygen -t rsa -b 4096 -C "email@example.com" -f ~/.ssh/id_rsa_example
# id_rsa_example.pub 貼上遠端倉庫
# 配置多個遠端倉庫
touch ~/.ssh/config
#新增一下內容
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_rsa_github
Host example.com
HostName example.com
User git
IdentityFile ~/.ssh/id_rsa_example
配置
git config –global user.name “xxx”
git config –global user.email “xxx@email.com“
git config --global core.autocrlf true # 建議配置 windows mac換行符不統一問題
git config --global core.editor vim # 配置預設編輯器
git config --global core.excludesfile ~/.gitignore_global # 配置全域性忽略檔案
git config –list # 檢視配置資訊
分支管理
git branch --list # 羅列本地所有分支
git branch --all # 羅列本地和遠端所有分支
git branch -r # 羅列遠端所有分支
git branch -v # 顯示各分支最後提交資訊
git checkout <branch name> # 切換分支
git checkout -b <new branch name> # 建立新分支
git push origin <new branch name> # 推送新分支到遠端
git checkout -m <old branch> <new branch> # 重新命名分支名稱
git branch -d <[list]branch name> # 刪除本地分支
git push origin --delete <branch name> # 刪除遠端分支
標籤管理
git tag -l # 羅列本地所有標籤
git show <tag name> # 顯示指定標籤
git tag -a v_1.0.0 -m "備註" # 建立標籤
git push origin <tag name> # 推送標籤到遠端
git tag -d <tag name> # 刪除本地標籤
git push --delete origin <tag name> # 刪除遠端標籤
總結
工作多年以來一直在使用Git,但是對Git沒有一個系統瞭解,所以寫這篇文章歸整一下。
歡迎大家留言交流,一起學習分享!!!