前東家使用 Perforce 做版本控制,現(xiàn)東家使用 Git戳杀。
Git 工作原理
Git 和其他版本控制系統(tǒng)的主要差別在于该面,Git 只關心文件數(shù)據(jù)的整體是否發(fā)生變化夭苗,而大多數(shù)其他系統(tǒng)則只關心文件內容的具體差異。
這類系統(tǒng)(CVS隔缀,Subversion题造,Perforce,Bazaar 等等)每次記錄有哪些文件作了更新猾瘸,以及都更新了哪些行的什么內容:
Git 并不保存這些前后變化的差異數(shù)據(jù)界赔。實際上,Git 更像是把變化的文件作快照后牵触,記錄在一個微型的文件系統(tǒng)中淮悼。每次提交更新時,它會縱覽一遍所有文件的指紋信息并對文件作一快照揽思,然后保存一個指向這次快照的索引袜腥。為提高性能,若文件沒有變化钉汗,Git 不會再次保存羹令,而只對上次保存的快照作一鏈接。Git 的工作方式就如下圖所示:
工作區(qū) Working Directory VS 暫存區(qū) Staging Directory
本地倉庫由 Git 維護的三棵“樹“組成:
-
工作目錄 Working Directory损痰,它持有實際文件
例如福侈,我們添加了一個文件NewFile.txt
,那它就是在工作目錄 Working Directory 中:
-
暫存區(qū) Staging Directory 或者叫 Index Directory卢未,緩存區(qū)域癌刽,臨時保存文件的改動
我們通過git add
命令添加到暫存區(qū) Staging Directory 或者叫 Index Directory:
-
倉庫 Head 區(qū),指向最后一次提交的結果
我們通過git commit
命令實際提交改動到 Head 區(qū):
通過下面這個圖可以清晰的表示不同區(qū)的切換:
引用自:http://marklodato.github.io/visual-git-guide/index-zh-cn.html
文件的三種狀態(tài)
- 已修改 (modified):表示修改了某個文件尝丐,但還沒有提交保存
- 已暫存 (staged):表示把已修改的文件放在下次提交時要保存的清單中
- 已提交 (committed):表示該文件已經(jīng)被安全的保存在本地數(shù)據(jù)庫中了
版本庫
工作區(qū)中的隱藏目錄 .git
,就是 Git 的版本庫衡奥。
.git
目錄結構如下:
其中:
-
HEAD
文件:指示目前被檢出的分支
index
文件:保存暫存區(qū)信息-
objects
目錄:存儲所有數(shù)據(jù)內容
-
refs
目錄:存儲指向數(shù)據(jù)(分支)的提交對象的指針
什么是 HEAD
HEAD 默認指向指向當前分支的最新提交爹袁。
也可以通過 git show HEAD
命令看到當前 HEAD 的位置:
- HEAD 可以指向某個分支,如果指向某個分支矮固,則永遠指向分支上的最新提交
- 當然 HEAD 也可以指向某個提交
什么是 origin 和 master
通過 Git 來修改代碼失息,主要包括如下三步:
-
從 Git 取數(shù)據(jù)
git clone
- 涉及到 遠程服務器下的某個倉庫下的某個分支
remote server/remote repository/remote branch
- 涉及到 遠程服務器下的某個倉庫下的某個分支
-
改動代碼
- 涉及到 本地某個倉庫下的某個分支
local repository/local branch
- 涉及到 本地某個倉庫下的某個分支
-
將改動傳回 Git
git push
- 涉及到 遠程服務器下的某個倉庫下的某個分支
remote server/remote repository/remote branch
- 涉及到 遠程服務器下的某個倉庫下的某個分支
涉及到兩個 repository:
- 遠程倉庫 Remote Repository,在遠程服務器上
- 本地倉庫 Local Repository档址,在本地工作區(qū)上
找到最近創(chuàng)建的一個 Github 項目:https://github.com/chenxiangcyr/spring-cloud-config-repo-demo
現(xiàn)在我們通過如下命令 Clone 到本地:
git clone git@github.com:chenxiangcyr/spring-cloud-config-repo-demo.git
在 Clone 完成之后盹兢,Git 會自動為你將此遠程倉庫命名為 origin
,并下載其中所有的數(shù)據(jù)守伸,建立一個指向它的 master
分支的指針绎秒。
因此 origin
是遠程倉庫的名字,master
是分支的名字尼摹。
我們用 (遠程倉庫名)/(分支名)
這樣的形式表示遠程分支见芹,所以 origin/master
指向的是一個遠程分支 remote branch剂娄。我們實際上是從這個遠程分支 remote branch 中 Clone 數(shù)據(jù)到本地,但你無法在本地更改遠程分支 remote branch 中的數(shù)據(jù)玄呛。
通過 git branch -r
可以查看遠程分支:
我們也可以通過 cat .git/config
看到 origin
的含義:
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[remote "origin"]
url = git@github.com:chenxiangcyr/spring-cloud-config-repo-demo.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
同時阅懦,Git 會建立一個屬于你自己的本地 master
分支,它指向的是你剛剛從遠程分支 remote branch 傳到你本地的副本徘铝。
通過 git branch
可以查看本地分支:
總結:
-
master
就是本地分支 local branch -
origin/master
是遠程分支 remote branch -
remotes/origin/master
和origin/master
的指向是相同的 - 我們可以通過
git diff origin/master master
來顯示 本地分支 與 遠程分支 的區(qū)別
隨著你不斷的改動文件耳胎,git add
, git commit
,master
的指向會自動移動惕它。最終你還要通過 git push
命令來將修改推送到遠程分支怕午。
git push origin master:master
- 我們看到有兩個
master
,其中前面一個為 source怠缸,后面一個為 destination - 該命令可以理解為诗轻,在本地倉庫中找到名字為
master
的分支,使用它去更新遠程倉庫下名字為master
的分支揭北,如果遠程倉庫下不存在名字是master
的分支扳炬,那么新建一個。 -
git push origin master
(省略了<destination>
搔体,等價于git push origin master:master
)
Git 基本操作
引用自:http://marklodato.github.io/visual-git-guide/index-zh-cn.html
上面的四條命令在工作目錄恨樟、暫存目錄(也叫做索引)和倉庫之間復制文件。
git add files
把當前文件放入暫存區(qū)域疚俱。git commit
給暫存區(qū)域生成快照并提交劝术。-
git reset files
用來撤銷最后一次git add files
,你也可以用git reset
撤銷所有暫存區(qū)域文件呆奕。
例如我們修改了文件NewFile.txt
(添加了內容123
)养晋,通過git add NewFile.txt
放入暫存區(qū)域,隨后通過git reset NewFile.txt
來撤銷梁钾,但是修改的內容沒有被撤銷(依然是123
):
-
git checkout files
把文件從暫存區(qū)域復制到工作目錄绳泉,用來丟棄本地修改。
例如在上面的基礎上執(zhí)行git checkout NewFile.txt
姆泻,這時候NewFile.txt
所修改的內容被撤銷了零酪。
也可以跳過暫存區(qū)域直接從倉庫取出文件或者直接提交代碼:
-
git commit -a
相當于運行git add
把所有當前目錄下的文件加入暫存區(qū)域再運行git commit
拇勃。 -
git commit files
進行一次包含最后一次提交加上工作目錄中文件快照的提交四苇,并且文件被添加到暫存區(qū)域。 -
git checkout HEAD files
從倉庫歷史中回滾到復制最后一次提交到工作區(qū)方咆。
diff 查看兩次提交之間的變動
-
git diff NexFile.txt
查看工作目錄與暫存區(qū)的變動 -
git diff HEAD NexFile.txt
查看工作目錄與倉庫中 HEAD 的變動 -
git diff --cached NexFile.txt
查看暫存區(qū)與倉庫中 HEAD 的變動 -
git diff <branch> NexFile.txt
查看工作目錄與倉庫中某個分支的變動 -
git diff <commit> <commit>
查看兩次 commit 之間的變動
checkout
checkout
命令用于從歷史提交(或者暫存區(qū)域)中拷貝文件到工作目錄月腋,也可用于切換分支。
- 當給定某個文件名(或者打開
-p
選項,或者文件名和-p
選項同時打開)時罗售,Git 會從指定的提交中拷貝文件到暫存區(qū)域和工作目錄辜窑。比如,git checkout HEAD NewFile.txt
會將提交節(jié)點HEAD~
(即當前提交節(jié)點的父節(jié)點)中的NewFile.txt
復制到工作目錄并且加到暫存區(qū)域中寨躁。 - 當不指定文件名穆碎,而是給出一個(本地)分支時,那么 HEAD 標識會移動到那個分支(也就是說职恳,我們“切換”到那個分支了)所禀,然后暫存區(qū)域和工作目錄中的內容會和 HEAD 對應的提交節(jié)點一致。
- 如果既沒有指定文件名放钦,也沒有指定分支名色徘,而是一個標簽、遠程分支操禀、SHA-1值或者是像 master~3 類似的東西褂策,就得到一個匿名分支,稱作 detached HEAD(被分離的HEAD標識)颓屑。
reset
reset
命令把當前分支指向另一個位置斤寂,并且有選擇的變動工作目錄和索引。也用來在從歷史倉庫中復制文件到索引揪惦,而不動工作目錄遍搞。
如果不給選項,那么當前分支指向到那個提交器腋。如果用 --hard
選項溪猿,那么工作目錄也更新,如果用 --soft
選項纫塌,那么都不變诊县。
如果沒有給出提交點的版本號,那么默認用 HEAD
措左。這樣翎冲,分支指向不變,但是索引會回滾到最后一次提交媳荒,如果用 --hard
選項,工作目錄也同樣驹饺。
cherry pick
cherry-pick
命令"復制"一個提交節(jié)點并在當前分支做一次完全一樣的新提交钳枕。
rebase
交互式變基 (rebase)。它可以用來編輯提交信息赏壹,或者將多個提交壓縮成一個提交鱼炒。
git rebase -i HEAD~n
Git 創(chuàng)建與合并分支
Git 關于分支的操作主要包括:
-
查看分支:
git branch
-
創(chuàng)建分支:
git branch <name>
-
切換分支:
git checkout <name>
-
創(chuàng)建 + 切換分支:
git checkout -b <name>
-
合并某分支到當前分支:
git merge <name>
-
刪除分支:
git branch -d <name>
每次提交,Git 都把它們串成一條時間線蝌借,這條時間線就是一個分支昔瞧。在 Git 里指蚁,這個分支叫主分支,即 master
分支自晰。HEAD
嚴格來說不是指向提交凝化,而是指向 master
,master
才是指向提交的酬荞,所以搓劫, HEAD
指向的就是當前分支。每次提交混巧,master
分支都會向前移動一步枪向,這樣,隨著你不斷提交咧党,master
分支的線也越來越長:
一個具體的 Case
我們目前在 master
分支上秘蛔,下一階段要開發(fā)一個新需求付款,因此需要在一個新的分支 payment
上開發(fā)傍衡,測試深员,最后合并到 master
分支上。
創(chuàng)建新分支:
git checkout -b payment
以上操作相當于:
git branch payment
git checkout payment
隨后我們在 payment
分支在不斷的向前推進:
這時候聪舒,突然來了一個線上的 Issue辨液,需求緊急修復,因此我們停下手動上的工作箱残,需要在一個新的分支 bugfix
上開發(fā)滔迈,測試,最后合并到 master
分支上被辑。
創(chuàng)建新分支:
git checkout -b bugfix
隨后我們在 bugfix
分支上進行修改:
測試沒問題后燎悍,合并到 master
分支上:
git checkout master
git merge bugfix
在合并的時候,由于當前 master
分支所指向的提交是 bugfix
的直接上游盼理,所以 Git 只是簡單的將指針向前移動谈山。 換句話說,當你試圖合并兩個分支時宏怔,如果順著一個分支走下去能夠到達另一個分支奏路,那么 Git 在合并兩者的時候,只會簡單的將指針向前推進(指針右移)臊诊,因為這種情況下的合并操作沒有需要解決的分歧鸽粉,這就叫做 “快進(fast-forward)”。
隨后我們上線該緊急修復抓艳,然后可以使用命令來刪除 bugfix
分支:
git branch -d bugfix
接下來触机,我們切換到之前的 payment
分支上繼續(xù)工作:
測試沒問題后,合并到 master
分支上:
git checkout master
git merge payment
這和你之前合并 bugfix
分支的時候看起來有一點不一樣。在這種情況下儡首,你的開發(fā)歷史從一個更早的地方開始分叉開來片任。 因為 master
分支所在提交并不是 payment
分支所在提交的直接祖先,Git 不得不做一些額外的工作蔬胯。
出現(xiàn)這種情況的時候对供,Git 會使用兩個分支的末端所指的快照(藍色部分)以及這兩個分支的工作祖先(黃色部分),做一個簡單的三方合并笔宿。
引用:
Git 教程 創(chuàng)建與合并分支
3.2 Git 分支 - 分支的新建與合并
淺析 Git 思想和工作原理
圖解Git
Git 的origin和master分析(轉)