自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Git歷險(xiǎn)記(5):Git里的分支與合并

系統(tǒng) Linux
本文是《Git歷險(xiǎn)記》系列的第五篇,譯者劉輝通過翻譯《Git Community Book》為我們具體講解Git里的分支與合并以及如何處理沖突的方法。如果你對Git還不了解,可以參看《Git歷險(xiǎn)記(1):初識版本控制系統(tǒng)Git》。以下是正文。

 51CTO編者按:本文是《Git歷險(xiǎn)記》系列的第五篇,譯者劉輝通過翻譯《Git Community Book》為我們具體講解Git里的分支與合并以及如何處理沖突的方法。如果你對Git還不了解,可以參看《Git歷險(xiǎn)記(1):初識版本控制系統(tǒng)Git》。以下是正文。

分支與合并

在Git里面我們可以創(chuàng)建不同的分支,來進(jìn)行調(diào)試、發(fā)布、維護(hù)等不同工作,而互不干擾。下面我們還是來創(chuàng)建一個試驗(yàn)倉庫,看一下Git分支運(yùn)作的臺前幕后:

$rm -rf test_branch_proj
$mkdir test_branch_proj
$cd test_branch_proj
$git init
Initialized empty Git repository in /home/test/test_branch_proj/.git/

我們?nèi)缫酝粯?,?chuàng)建一個“readme.txt”文件并把它提交到倉庫中:

$echo "hello, world" > readme.txt
$git add readme.txt
$git commit -m "project init"
[master (root-commit) 0797f4f] project init 1 files changed, 1 insertions(+), 0 deletions(-) create mode 100644 readme.txt

我們來看一下工作目錄(working tree)的當(dāng)前狀態(tài):

$git status
# On branch master
nothing to commit (working directory clean)

大家如果注意的話,可以看到“# On branch master”這么一行,這表示我們現(xiàn)在正在主分支(master)上工作。當(dāng)我們新建了一個本地倉庫,一般就是默認(rèn)處在主分支(master)上。下面我們一起看一下Git是如何存儲一個分支的:

$cd .git
$cat HEAD
ref: refs/heads/master

“.git/HEAD”這個文件里保存的是我們當(dāng)前在哪個分支上工作的信息。

在Git中,分支的命名信息保存在“.git/refs/heads”目錄下:

$ls refs/heads
master

我們可以看到目錄里面有一個名叫“master”文件,我們來看一下里面的內(nèi)容:

$cat refs/heads/master
12c875f17c2ed8c37d31b40fb328138a9027f337

大家可以看到這是一個“SHA1哈希串值”,也就是一個對象名,我們再看看這是一個什么類型的對象:

$cat refs/heads/master | xargs git cat-file -t
commit

是的,這是一個提交(commit),“master”文件里面存有主分支(master)最新提交的“對象名”;我們根據(jù)這個“對象名”就可以可找到對應(yīng)的樹對象(tree)和二進(jìn)制對象(blob),簡而言之就是我能夠按“名”索引找到這個分支里所有的對象。

讀者朋友把我們文章里的示例在自己的機(jī)器上執(zhí)行時會發(fā)現(xiàn),“cat refs/heads/master”命令的執(zhí)行結(jié)果和和文章中的不同。在本文里這個提交(commit)的名字是: “12c875f17c2ed8c37d31b40fb328138a9027f337”,前面我講Git是根據(jù)對象的內(nèi)容生成“SHA1哈希串值”作為 名字,只要內(nèi)容一樣,那么的對應(yīng)的名字肯定是一樣的,為什么這里面會不一樣呢? Git確實(shí)根據(jù)內(nèi)容來生成名字的,而且同名(SHA1哈希串值)肯定會有 相同內(nèi)容,但是提交對象(commit)和其它對象有點(diǎn)不一樣,它里面會多一個時間戳(timestamp),所以在不同的時間生成的提交對象,即使內(nèi)容 完全一樣其名字也不會相同。

下面命令主是查看主分支最新提交的內(nèi)容:

$cat refs/heads/master | xargs git cat-file -p
tree 0bd1dc15d804534cf25c5cb53260fd03c84fd4b9
author liuhui998 1300697913 +0800
committer liuhui998 1300697913 +0800 project init

“1300697913 +0800”這就是時間戳(timestamp)。

現(xiàn)在查看此分支里面所包含的數(shù)據(jù)(blob)

$cat refs/heads/master | xargs git cat-file -p | head -n 1 | cut -b6-15 | xargs git cat-file -p
100644 blob 4b5fa63702dd96796042e92787f464e28f09f17d readme.txt

查看當(dāng)前的readme.txt

$git cat-file -p 4b5fa63
hello, world
$cd ..

好的,前面是在主分支(master)里面玩,下面我們想要創(chuàng)建一個自己的測試分支來玩一下。git branch命令可以創(chuàng)建一個新的分支,也可以查看當(dāng)前倉庫里有的分支。下面先創(chuàng)建一個叫“test”的分支: $git branch test

再來看一下當(dāng)前項(xiàng)目倉庫中有幾個分支:

$git branch
* master test

我們現(xiàn)在簽出“test”分支到工作目錄里:

$git checkout test

現(xiàn)在再來看一下我們處在哪個分支上:

$git branch master
* test

好的,我們現(xiàn)在在“test”分支里面了,那么我們就修改一下“readme.txt”這個文件,再把它提交到本地的倉庫里面支:

$echo "In test branch" >> readme.txt
$git add readme.txt
$git commit -m "test branch modified"
[test 7f3c997] test branch modified 1 files changed, 1 insertions(+), 0 deletions(-)

當(dāng)看當(dāng)前版本所包含的blob:

$git cat-file -p HEAD | head -n 1 | cut -b6-15 | xargs git cat-file -p

我們現(xiàn)在再像前面一樣的看看Git如何存儲“test”這個分支的,先來看看“.git/HEAD”這個文件是否指向了新的分支:

$cd .git
$cat HEAD
ref: refs/heads/test

沒錯,“.git/HEAD”確實(shí)指向的“test”分支。再來看看“.git/refs/heads”目錄里的內(nèi)容:

$ls refs/heads
master
test

我們可以看到目錄里面多了一個名叫“test”文件,我們來看一下里面的內(nèi)容:

$cat refs/heads/test
7f3c9972577a221b0a30b58981a554aafe10a104

查看測試分支(test)最新提交的內(nèi)容:

$cat refs/heads/test | xargs git cat-file -p
tree 7fa3bfbeae072063c32621ff08d51f512a3bac53
parent b765df9edd4db791530f14c2e107aa40907fed1b
author liuhui998 1300698655 +0800
committer liuhui998 1300698655 +0800 test branch modified

再來查看此分支里面所包含的數(shù)據(jù)(blob):

$cat refs/heads/test | xargs git cat-file -p | head -n 1 | cut -b6-15 | xargs git cat-file -p
100644 blob ebe01d6c3c2bbb74e043715310098d8da2baa4bf readme.txt

查看當(dāng)前”readme.txt”文件里的內(nèi)容:

$git cat-file -p ebe01d6
hello, world
In test branch
cd ..

我們再回到主分支里面:

$git checkout master
Switched to branch 'master'
$git checkout master
$cat readme.txt
hello, world

如我們想看看主分支(master)和測試分支(test)之間的差異,可以使用git diff命令來查看它們之間的diff:

$git diff test
diff --git a/readme.txt b/readme.txt
index ebe01d6..4b5fa63 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,2 +1 @@ hello, world
-In test branch

大家可以以到當(dāng)前分支與測試分支(test)相比,少了一行內(nèi)容:“-In test branch”。

如果執(zhí)行完git diff命令后認(rèn)為測試分支(test)的修改無誤,能合并時,可以用git merge命令把它合并到主分支(master)中:

$git merge test
Updating b765df9..7f3c997
Fast-forward readme.txt | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)

“Updating b765df9..7f3c997”表示現(xiàn)在正在更新合并“b765df9”和“7f3c997”兩個提交(commit)之間的內(nèi)容;“b765df9”代表著主分支(master),“7f3c997”代表測試分支(test)。

“Fast-forward”在這里可以理解為順利合并,沒有沖突。“readme.txt | 1 +”表示這個文件有一行被修改,“1 files changed, 1 insertions(+), 0 deletions(-)”,表示這一次合并只有一個文件被修改,一行新數(shù)據(jù)插入,0 行被刪除。

我們現(xiàn)在看一下合并后的“readme.txt”的內(nèi)容:

$cat readme.txt
hello, world
In test branch

內(nèi)容沒有錯,是“master”分支和“test”分支合并后的結(jié)果,再用“git status”看一下,當(dāng)前工作目錄的狀態(tài)也是干凈的(clean)。

$git status
# On branch master
nothing to commit (working directory clean)

好的,現(xiàn)在測試分支(test)結(jié)束了它的使命,沒有存在的價(jià)值的,可以用“git branch -d”命令把這個分支刪掉:

$git branch -d test
Deleted branch test (was 61ce004).

如果你想要刪除的分支還沒有被合并到其它分支中去,那么就不能用“git branch -d”來刪除它,需要改用“git branch -D”來強(qiáng)制刪除。

#p#

如何處理沖突(conflict)

前面說了分支的一些事情,還簡單地合并了一個分支。但是平時多人協(xié)作的工作過程中,幾乎沒有不碰到?jīng)_突(conflict)的情況,下面的示例就是剖析一下沖突成因及背后的故事:

還是老規(guī)矩,新建一個空的Git倉庫作試驗(yàn):

$rm -rf test_merge_proj
$mkdir test_merge_proj
$cd test_merge_proj
$git init
Initialized empty Git repository in /home/test/test_merge_proj/.git/

在主分支里建一個“readme.txt”的文件,并且提交本地倉庫的主分支里(master):

$echo "hello, world" > readme.txt
$git add readme.txt
$git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached ..." to unstage)
#
# new file: readme.txt
#
git commit -m "project init"
[master (root-commit) d58353e] project init 1 files changed, 1 insertions(+), 0 deletions(-) create mode 100644 readme.txt

當(dāng)看當(dāng)前版本所包含的blob:

$git cat-file -p HEAD | head -n 1 | cut -b6-15 | xargs git cat-file -p
100644 blob 4b5fa63702dd96796042e92787f464e28f09f17d readme.txt

雖然前面把“readme.txt”這個文件提交了,但是暫存區(qū)里還是會暫存一下,直到下次“git add”時把它沖掉:

$git ls-files --stage
100644 4b5fa63702dd96796042e92787f464e28f09f17d 0 readme.txt

然后再創(chuàng)建測試分支(test branch),并且切換到測試分支下工作:

$git branch test
$git checkout test
Switched to branch 'test'

再在測試分支里改寫“readme.txt”的內(nèi)容,并且提交到本地倉庫中:

$echo "hello, mundo" > readme.txt
$git add readme.txt
$git commit -m "test branch modified"
[test 7459649] test branch modified 1 files changed, 1 insertions(+), 1 deletions(-)

現(xiàn)在看一下當(dāng)前分支里的“readme.txt”的“SHA1哈希串值”確實(shí)不同了:

$git cat-file -p HEAD | head -n 1 | cut -b6-15 | xargs git cat-file -p
100644 blob 034a81de5dfb592a22039db1a9f3f50f66f474dd readme.txt

暫存區(qū)里的東東也不一樣了:

$git ls-files --stage
100644 034a81de5dfb592a22039db1a9f3f50f66f474dd 0 readme.txt

現(xiàn)在我們切換到主分支(master)下工作,再在“readme.txt”上作一些修改,并把它提交到本地的倉庫里面:

$git checkout master
Switched to branch 'master'
$git add readme.txt
echo "hola,world" > readme.txt
$git add readme.txt
$git commit -m "master branch modified"
[master 269ef45] master branch modified 1 files changed, 1 insertions(+), 1 deletions(-)

現(xiàn)在再來看一下當(dāng)前分支里的“readme.txt”的“SHA1哈希串值”:

$git cat-file -p HEAD | head -n 1 | cut -b6-15 | xargs git cat-file -p
100644 blob aac629fb789684a5d9c662e6548fdc595608c002 readme.txt

暫存區(qū)里的內(nèi)容也改變了:

$git ls-files --stage
100644 aac629fb789684a5d9c662e6548fdc595608c002 0 readme.txt

主分支(master) 和測試分支(test)里的內(nèi)容已經(jīng)各自改變了(diverged),我們現(xiàn)在用“git merge”命令來把兩個分支合一下看看:

$git merge test
Auto-merging readme.txt
CONFLICT (content): Merge conflict in readme.txt
Automatic merge failed; fix conflicts and then commit the result.

合并命令的執(zhí)行結(jié)果不是“Fast-foward”,而是“CONFLICT”。是的,兩個分支的內(nèi)容有差異,致使它們不能自動合并(Auto-merging)。

還是先看一下工作目錄的狀態(tài):

$git status
# On branch master
# Unmerged paths:
# (use "git add/rm ..." as appropriate to mark resolution)
#
# both modified: readme.txt
#
no changes added to commit (use "git add" and/or "git commit -a")

現(xiàn)在Git提示當(dāng)前有一個文件“readme.txt”沒有被合并,原因是“both modified”。

再看一下暫存區(qū)里的內(nèi)容:

$git ls-files --stage
100644 4b5fa63702dd96796042e92787f464e28f09f17d 1 readme.txt
100644 aac629fb789684a5d9c662e6548fdc595608c002 2 readme.txt
100644 034a81de5dfb592a22039db1a9f3f50f66f474dd 3 readme.txt

看一下里面的每個blob對象的內(nèi)容:

$git cat-file -p 4b5fa6
hello, world
$git cat-file -p aac629
hola,world
$git cat-file -p 034a81
hello, mundo

我們不難發(fā)現(xiàn),“aac629”是當(dāng)前主分支的內(nèi)容,“034a81”是測試分支里的內(nèi)容,而“4b5fa6”是它們共同父對象(Parent)里的內(nèi)容。因?yàn)樵诤喜⑦^程中出現(xiàn)了錯誤,所以Git把它們?nèi)齻€放到了暫存區(qū)了。

現(xiàn)在我們再來看一下工作目錄里的“readme.txt”文件的內(nèi)容:

$cat readme.txt
<<<<<<< HEAD
hola,world
=======
hello, mundo
>>>>>>> test

“<<<<<<< HEAD“下面就是當(dāng)前版本里的內(nèi)容;而“=======”之下,“>>>>>>> test”之上則表示測試分支里與之對應(yīng)的有沖突的容。修復(fù)沖突時我們要做的,一般就是把“ <<<<<<< HEAD”,“=======”和“ >>>>>>> test”這些東東先去掉,然后把代碼改成我們想要的內(nèi)容。

假設(shè)我們用編輯器把“readme.txt“改成了下面的內(nèi)容:

$cat readme.txt
hola, mundo

然再把改好的“readme.txt”用“git add”添加到暫存區(qū)中,最后再用“git commit”提交到本地倉庫中,這個沖突(conflict)就算解決了:

$git add readme.txt
$git commit -m "fix conflict"
[master ebe2f18] fix conflict

這里看起來比較怪異的地方是Git解決了沖突的辦法:怎么用“git add”添加到暫存區(qū)去,“git add”不是用來未暫存文件的吧,怎么又來解決沖突了。不過我想如果你仔細(xì)讀過上一篇文章的話就不難理解,因?yàn)镚it是一個“snapshot”存儲系統(tǒng),所有新增加的內(nèi)容都是直接存儲的,而不是和老版本作一個比較后存儲新舊版本間的差異。

Git里面合并兩個版本之間的同一文件,如果兩者間內(nèi)容相同則不作處理,兩者間內(nèi)容不同但是可以合并則產(chǎn)生一個新的blob對象,兩者間內(nèi)容不同但是合并時產(chǎn)生了沖突,那么我們解決了沖突后要把文件“git add”到暫存區(qū)中再“git commit”提交到本地倉庫即可,這就和前面一樣產(chǎn)生一個新的blob對象。

假設(shè)我們對合并的結(jié)果不滿意,可以用下面的命令來撤消前面的合并:

$git reset --hard HEAD^
HEAD is now at 050d890 master branch modified

從git reset(2)命令的輸出結(jié)果可以看到,主分支已經(jīng)回到了合并前的狀態(tài)了。

我們再用下面的命令看一下“readme.txt”文件,確認(rèn)一下文件改回來沒有:

$cat readme.txt
hola,world

小結(jié)

由于Git采用了“SHA1哈希串值內(nèi)容尋值”、“快照存儲(snapshot)”等方法, Git中創(chuàng)建分支代價(jià)是很小的速度很快;也這是因?yàn)槿绱?,它處理合并沖突的方法與眾不同。

在這里我想起了“C語言就是匯編(計(jì)算機(jī)硬件)的一個馬甲”這句話,其實(shí)Git也就是底層文件系統(tǒng)的一個馬甲,只不過它帶了版本控制功能,而且更加高效。Git里有些命令可能不是很好理解(如解決合并沖突用git add),但是對于系統(tǒng)層而言,它是最高效的,就像是C語言的數(shù)組下標(biāo)從0開始一樣。

原文連接:http://www.infoq.com/cn/news/2011/03/git-adventures-branch-merge

【編輯推薦】

  1. Git歷險(xiǎn)記(4):索引與提交的那些事
  2. Git歷險(xiǎn)記(3):創(chuàng)建一個自己的本地倉庫
  3. Git歷險(xiǎn)記(2):Git的安裝和配置


 

責(zé)任編輯:黃丹 來源: InfoQ
相關(guān)推薦

2011-01-26 10:05:36

Git安裝配置

2011-03-18 09:35:39

GitLinux版本控制

2011-01-26 09:09:06

版本控制系統(tǒng)GitLinux

2011-02-28 14:37:43

GitLinux版本控制

2009-10-15 09:21:00

CCNA考試歷險(xiǎn)記CCNA

2022-05-26 21:38:02

開源分布式Hadoop

2022-05-05 19:26:17

Druid分布式存儲

2021-02-22 08:20:32

Activity動畫界面

2020-05-28 10:45:31

Git分支合并

2011-09-16 16:05:10

MySQL

2023-12-01 11:05:29

Git 分支

2024-10-14 08:35:29

2014-04-01 09:13:23

程序員招聘

2014-08-08 10:20:23

Git版本管理系統(tǒng)

2023-10-09 08:39:33

Git Flow分支管理模型

2017-06-30 17:54:04

2022-10-26 09:22:19

git命令Linux

2022-11-07 08:01:18

Git分支管理

2020-07-09 08:00:25

Git分支模式

2021-03-28 17:21:15

Git分支策略
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號