天天看點

git基本概念與原理

                                                      git基本概念與原理

-----------------------------------------------------------------------------------------------------------------------------------------------

一、git的安裝與簡單配置

1、安裝

apt install -y git-core

2、配置git名稱與郵箱

git config --global user.name "chenux"

git config --global user.email "[email protected]"

git config --global --list   

mkdir 目錄 && cd 目錄

git init git_repo   #git_repo就是倉庫,其目錄下有個.git

git基本概念與原理

3、git設定優先級

git config --system:使對應配置針對系統内所有的使用者有效

git config --global:使對應配置針對目前系統使用者的所有倉庫生效

git config --local:使對應配置隻針對目前倉庫有效

local選項設定的優先級最高。

4、倉庫中有檔案後,儲存庫狀态

在倉庫中執行指令git add . && git commit -m "status01"

給倉庫裡加了些檔案

git基本概念與原理

5、gitk,圖形化git管理工具,apt install -y gitk

git基本概念與原理

6、此圖狀态為在4中已經執行過的狀态,此時輸入指令更改檔案内容

echo “aa11a1” >1.txt \

echo “b2b2b2” >> 2.txt \

touch project/pro-2.txt

執行後儲存狀态git add. && git commit -m “status02”,此時再啟動gitk

git基本概念與原理

再次稍作修改後儲存一次狀态

git基本概念與原理

二、git對象

1、三種不同類型的git對象:

(1)blob:一個blob就是由一個檔案轉換而來,blob中隻會存儲檔案的資料,而不會存儲檔案的中繼資料

(2)tree:一個tree就是有一個目錄轉換而來,tree隻會存儲一層目錄資訊,它隻存儲它的直接檔案和直接子目錄的資訊,但子目錄中的内容它不會儲存

(3)commit:一個commit就是我們的git commit送出,它指向了一個tree,這個tree儲存了某一時刻項目根目錄中的直接檔案和直接目錄資訊

總而言之,commit指向了tree,tree又指向了自己根下直接檔案的blob或者子目錄的tree,子目錄的tree對象指向了子目錄的檔案blob和子子目錄的tree,以此類推

2、每個git對象都有一個唯一的哈希碼(sha1算法),它是一個40位的16進制數

3、在初始送出後再進行二次送出,若存在

檔案f1沒有修改,在此過程後,它的blob哈希沒有改變;

檔案f2修改内容,在此過程後檔案為f22,它的blob哈希發生改變;

存放f1和f2的目錄alpha,在此過程中它的tree哈希發生改變,但指向檔案f1的blob仍為a以前的blob

git基本概念與原理

三、git過程

1、目錄結構

git基本概念與原理

可以看到,倉庫内部除了自己建的檔案,還有一個.git目錄,該隐藏目錄在git init時候就已經生成了。

.git目錄:可以稱之為版本庫

2、在之前送出的指令中,輸入的是

(1)git add -a:選擇将哪些變化的内容加入到下一次送出中,這些變化内容會被索引或者暫存起來,此時生成檔案的blob

(2)git commit -m “statusname”:建立commit送出對象,執行指令後git會根據索引/暫存中的結構,建立出對應的tree對象,之後git會建立一個commit對象,将新建立的commit對象指向新建立的tree

(3)這個新的送出産生後,記錄了一個狀态,該送出也會指向前一次的送出,每個送出都會指向自己的父送出

git基本概念與原理

(4)圖檔所示,當修改3.txt後,由于它是位于上一次狀态中,修改它後會變紅,等git add 3.txt後它的狀态變為綠色,表明已加入暫存區,做好随時被送出的準備。4.txt由于沒有送出,一直是紅色,表明還在工作區

git基本概念與原理

四、git使用

1、git log

顯示所有的git送出,git log --oneline,git log的簡單模式

git基本概念與原理

git cat-file -t 哈希值 檢視對象的類型

git cat-file -p 哈希值 檢視對象的内容

git rev-parse 13614 通過簡短的哈希值擷取到整個哈希值

2、有如下操作:

git init git_test

cd git_test

echo f1>f1                                                                                         echo f2>f2

git add .

git commit -m "test01"

echo -e "haha \nheihei" >> f1

git commit -m "test02"

我們可以得知f1産生了變化,如果我們此時後悔了對f1的操作,執行指令

git log --oneline,先找出對應送出的哈希值,之後git reset --hard 82a23e7

git基本概念與原理

不難發現,雖說檔案恢複到test01的狀态了,但是剛才test02沒了,現在如果想再次回到test02,該如何操作?

(1)git reflog,輸入後可以檢視之前的test02哈希碼,查詢到後 git reset --hard 哈希碼便可回複

git基本概念與原理

(2)驗證下,恢複成功

git基本概念與原理

五、git分支

1、在實際環境中,檔案的修改是縱容交錯的,是以存在一個問題:檔案回退時,是否會影響其它檔案?這裡就會引入一個概念:分支

git status,顯示位于主分支master

git基本概念與原理

建立新分支時,預設以目前所在分支為基礎建立,建立後分支的最新送出與master一樣

(1)建立分支,使用指令git branch分支名

git基本概念與原理

建立後工作區仍處在master分支上,未切換到建立的slave分支。分支的建立,并不是說明slave複制了master的分支,而是git隻是建立了slave分支指針,指向了master分支對應的最新送出。

邏輯圖如下:

git基本概念與原理

(2)檢視分支情況

git branch、git branch -v、git branch -vv

git基本概念與原理

(3)分支切換

git checkout 分支名

切換後工作區也随之切換,之後的git add與git commit指令影響切換後的分支

git基本概念與原理

現有操作

git基本概念與原理

切換到我的分支slave後,輸入指令後再送出,此時邏輯圖就變為了

git基本概念與原理

master上有5個送出,而slave上有6個送出。這時在切回master分支,看下f2檔案,内容并沒有改變。

git基本概念與原理

 同理,若此時在master分支修改任何檔案,切換到slave分支是看不到master修改的檔案,是以在項目時可以利用分支,負責某個項目a子產品開發的修改檔案在分支1,負責b子產品開發的修改檔案在分支2,後期項目合并時分支合并即可。具體的合并分支在後面。

六、head

(1)head指向分支

我們從之前圖中可以看到,通常情況下,當處于哪個分支,head就指向了哪個分支,是以git是通過head找到自己所處的工作區,在git倉庫下的.git裡,可以檢視head的檔案内容,其内部顯示就是目前指向的分支。

git基本概念與原理

(2)head指向送出

特殊情況下,head可以指向某個送出,這種情況稱作頭分離,detached head。

git基本概念與原理

如上圖所示,head沒有指向任何一個分支,而是指向了commit2這個送出。怎樣才能出現這種效果呢?git checkout commit_hash,如圖示

git基本概念與原理

此時輸入指令,git log --oneline --all --graph

git基本概念與原理

再看下此時的git status

git基本概念與原理

分離頭使用場景:

對該送出點的檔案可以随便看看,或者做一些實驗性的修改,并且可以将這些修改做成 送出,送出後會組成匿名分支,如果最後實驗結果不滿意可以丢棄之前實驗行的送出, 如果實驗結果滿意,就可以建立一個新的分支,來永久儲存這些送出。

示例:首先分離頭已經指向了51fe144,此時輸入了以下指令:

sed -i "s/\:/\-/g" 1.txt

git add 1.txt  && git commit -m "head_modify_1.txt"

echo headtest2 > 1.txt

git add 1.txt  && git commit -m "head_modify_1.txt-02"

修改了兩次,送出了兩次,我們此時通過圖形工具看一下

gitk --all

git基本概念與原理

注:這裡分離頭選的送出剛好選到master分支的最新送出了,這不能說明分離頭是從master分支分出來的

修改完成後,随便切換一個分支,會出現提示,比如我在git checkout test後,有圖

git基本概念與原理

如果修改項目後實驗結果滿意,就可以使用提示的指令

git branch 分支名 哈希值

輸入git branch headfile 65a96e4後

git基本概念與原理

如果不想儲存實驗性修改,切換分支後不用去管提示即可

七、差異比較

1、為比較差異,我們此處建立一個新的用來比對倉庫

#git init git_test && cd git_test

#cat > test1 << eof

> 1

> 2

> 3

> eof

#cat > test2 << eof

> a

> b

> c

#git add . && git commit -m “new test1&2”

之後做出修改:

#sed -i “s/2//” test1 && echo haha >> test1

#sed -i "s/c/cdef/" test2

做出修改後,我們并沒有git add将變化添加到暫存區,是以目前工作區和暫存區的内容是不同的,檢視不同,使用指令git diff

git基本概念與原理

檢視單個檔案變化的指令:git diff -- 檔案名,該指令可以跟多個檔案,git diff -- test1 test2 ...

git基本概念與原理

2、檢視工作區與目前分支的差別,使用指令git diff head,檢視工作區與某個分支的差別,如果head未指向需要檢視的那個分支,使用指令git diff 分支hash,比如說我目前在test分支,我查找test分支與之前status02分支的差別,找到status02的哈希碼,之後便可以使用指令檢視差別了。具體如下圖:(使用另一個送出比較多的git倉庫作示例)

git基本概念與原理

結論:git diff比較的是工作區與暫存區的差異,git head比較的是工作區與送出的差別

3、再次回到建立的git_diff庫,我們繼續做出兩次修改

#echo test1 >>test1

#echo test2 >test2

#git add -a

#git commit -m "first modify"

#echo test1-2 >> test1

#echo test2-2 >> test2

#git commit -m “second modify”

檢視git log --oneline後獲得兩次送出的哈希碼,之後可以通過指令

git diff 送出hash1  送出hash2,此指令檢視兩次送出的差異

如下圖所示:

git基本概念與原理

當然,圖中7ac86fc也是我們head的指向處,此處使用指令git diff cd692e5 head也可以

如果比對這次送出與前一次的送出,使用指令git diff head head~,這是一種簡易寫法,其它簡易寫法如下:

head:等于head~0,表示最新的一次送出

head~:等于head~1,表示最新送出的前一次送出

head~~:等于head~2,表示最新送出的前前一次送出

head~~~:等于head~3,表示最新送出的前前前送出

另:根據送出名擷取對應的哈希碼,使用指令舉例:

git rev-parse head~

git rev-parse master

4、如果是分支之間的比對,比如說git diff master slave,此時對比的是兩個分支各自最新送出的比對。

如圖所示

git基本概念與原理

總結:

#git diff==>比較工作區和暫存區

#git diff head==>比較工作區和目前分支最新的送出,head可以換成别的分支的名字,比如test分支,"#git diff test"表示比較目前工作區和test分支最新的送出之間的差異,也可以把head替換成任何一個commit的id,表示比較目前工作區

和對應送出之間的差異。

#git diff --cached==>比較暫存區和目前分支最新的送出

#git diff -- file1==>比較工作區和暫存中file1檔案的差異

#git diff -- ./file1==>隻比較工作區和暫存區中file1檔案的差異

#git diff -- file1 file2==>比較暫存區file1和file2檔案差異

#git diff -- dir1/ ==>隻比較工作區和暫存區中dir1目錄中所有檔案的差異

#git diff head -- ./file1 ==>隻比較工作區和目前分支最新的送出中file1檔案的差異,head可以替換成分支名或者commitid

#git diff branch01 -- ./file1==>隻比較工作區和branch01分支最新的送出中file1檔案的差異

#git diff --cached branch01==>比較暫存區和branch01分支最新的送出

#git diff --cached branch01 --./file1==>隻比較暫存區和branch01分支最新的送出中file1檔案的差異

#git diff head~ head==>比較目前分支中最新的兩個送出之間的差異

#git diff head~ head -- file1==>比較目前分支中最新的兩個送出中的file1檔案的差異

#git diff commit01 commit02==>比較兩個commit之間的差異

#git diff commit01..commit02==>同比較兩個commit之間的差異,與上個指令等效

#git diff branch01 branch02==>比較兩個分支上最新送出之間的差異

#git diff branch01..branch02==>比較兩個分支上最新送出之間的差異,與上個指令等效

三、撤銷暫存中的變更

1、平時進行版本管理中,修改某處代碼送出了,之後後悔了怎麼辦?

使用指令,git reset head,此操作為“檔案修改已記錄到暫存中,但後悔這麼做,恢複到git add之前”,也就是把暫存區恢複到與最新的commit狀态保持一緻。如圖所示

git基本概念與原理

從圖中我們看出,git reset head撤銷了之前的git add操作,f1和f2的修改又回到了工作區。

我們這裡是撤銷了所有檔案的更改,如果隻想撤銷一個檔案的更改,使用指令

git reset head -- f1

git基本概念與原理

2、git reset、git reset --hard和git reset --soft

(1)git reset = git reset --mixed

git reset head,将所有已經暫存的内容從暫存區撤銷了(即暫存區與最近送出中的狀态一緻)

git reset commit_hash,将head指針指向該次送出,并且将送出中的内容同步到暫存區,但工作區中内容不受影響。産生的結果是工作區記錄的是reset前的最新送出與reset後的送出的變化内容,在git add和git commit之後,檔案依然是reset前的檔案,但head指向了reset後的送出。此處示例git reset commit_hash:

首先我們建立了一個新git庫,并做了7次送出,之後git reset到第3次送出

git基本概念與原理

此時我們git diff,可以看下工作區與第3次送出的差異

git基本概念與原理

這也是第3次送出與我們git reset前的最新送出相比産生的差異,哪怕是git add之後繼續git commit,結果也是git reset前的結果,但是head此時在第3次的送出上。

git基本概念與原理

git reset --hard head,将所有區域恢複成了最近的送出中的狀态(即工作區、暫存區都與最近送出中的狀态一緻)

(2)git reset --hard commit_hash,将head指向該次送出,并且将所有區域内容進行同步,也就是恢複到該次送出狀态。此處示例git reset --hard commit_hash:

#echo "1 2 3" > f1

#echo "a b c" > f2

#git add .

#git commit -m "first add f1&f2"

#echo "456" >>f1

#git add f1

#git commit -m "second modify f1"

#echo "def" > f2

#git add f2

#git commit -m "third update f2"

#echo "linux" >> f1

#echo "chen" >>f2

#git commit -m "4th update f1&f2"

git基本概念與原理

git reset --hard commit_hash,回到first f1&f2這個送出點

git基本概念與原理

(2)git reset --soft

此指令隻将head指針指向commitid對應的送出,但是不會操作暫存區和工作區,也就是說,目前分支中的最新送出會變成commitid對應的送出,工作區和暫存區中的内容或者變更不會受到任何影響。此處示例:git reset --soft

如圖中所示,我們将送出指向了最初建立的commit,檔案結果沒有改變

git基本概念與原理

3、git checkout撤銷檔案

撤銷工作區已修改但未git add進入暫存區的檔案,使其回到上一次暫存後的狀态,如果不存在上一次的暫存,則回到最新的送出狀态。該指令的使用,對象檔案必須之前被git add追蹤過,若是建立的檔案沒有被追蹤過,此撤銷方法對其無效。

git checkout -- file,若是該目錄全部撤銷更改,git checkout -- .

git基本概念與原理

四、git合并分支

1、合并方向

git基本概念與原理

如圖示,有兩種情況如左右兩圖,分支b是基于分支a産生的分支,紅色部分是分支a的送出,橙色部分是分支b的送出,左圖為合并分支後合并分支的head在a上,分支b的head仍在原處;右圖同理亦然。

2、fast-foward

git基本概念與原理

上圖所示即為分支a與分支b的合并,不過我們也可以使用一種快捷的合并方式:fast-forward,也譯為“快進”或者“快速重定向”,這種情況下的快速合并,最終結果會變為如圖:

git基本概念與原理

此種情況為:基于分支a建立的分支b,分支a沒有任何新送出産生,如果此時将分支a合并到分支b,隻需要将分支A的指針指向到B分支的最新送出即可。這就是fast-forward。

git基本概念與原理

如圖所示,右圖分支a産生分支B後又産生了新的送出,是以不能用fast-forward的合并方式

示例:首先輸入以下指令,slv1從master分出去之後,master再無新的送出,slv1産生了新的送出

#touch f1

#git add f1  

#git commit -m "add f1"

#echo f1> f1 && git add . && git commit -m "insert f1 in f1"

#git branch slv1

#git status

#git checkout slv1

#touch f2 && git add f2 && git commit -m "touch f2 at slv1"

#git log --oneline

#echo "f2 in slv1" > f2 && git add f2 && git commit -m "insert f2 in f2 at slv1"

輸入指令之後,我們的git符合了可用fast-forward的模型

git基本概念與原理

此時我們進行分支合并

git merge 分支名

先git cheout 分支a ,再git merge 分支b:将分支b合并到分支A,

git基本概念與原理

圖中所示,這種方式即為fast-forward,git log --oneline可用看到head同時指向了兩個分支

git基本概念與原理

3、正常分支合并

建立個如下圖的模型

git基本概念與原理

首先有如下操作:

#echo 1 > 1.txt && git add . && git commit -m "touch 1"

#echo 2 >> 1.txt && git add . && git commit -m "insert 2 to 1"                                                                             

#git branch slv01

#echo 3 >> 1.txt  && git add 1.txt && git commit -m "insert 3 to 1"

#git checkout slv01

#echo a > a.txt && git add a.txt && git commit -m "touch a"

#echo ab >> a.txt && git add a.txt && git commit -m "insert ab to a"

gitk --all看到模型

git基本概念與原理

此時再合并分支:git checkout master && git merge slv01

輸入後會彈出nano編輯器,編輯器是需要為合并後的新送出而準備。

git基本概念與原理

不添加就用它的預設注釋即可。

gitk後看到結構已變為:

git基本概念與原理

4、常用合并指令

#git merge 分支a:表示将分支a合并到目前分支。

#git merge --no-ff 分支a:表示将分支a合并到目前分支,但是明确指定不使用"fast-forward"的模式進行合并

#git merge --ff-only 分支a:表示将a分支合并到目前分支,但是隻有在符合"fast-forward"模式的前提下才能合并成功,在不符合"fast-forward"模式的前提下,合并操作會自動終止,換句話說就是,當能使用"fast-forward"模式合并時,合并正常執行,當不能使用"fast-forward"模式合并時,則不進行合并。

#git merge --no-edit 分支a:表示将a分支合并到目前分支,但是沒有編輯預設注釋的機會,也就是說,在建立合并送出之前不會調用編輯器(上文的示例中會預設調用vim編輯器,以便使用者能夠有機會編輯預設的注釋資訊),換句話說就是,讓合并送出直接使用預設生成的注釋,預設注釋為" merge branch 'branchname' "

#git merge 分支a --no-ff -m "merge a into master,test merge message":表示将a分支合并到目前分支,并且使用-m參數指定合并送出對應的注釋資訊。

注意,為了保險起見,需要同時使用"--no-ff"參數,否則在滿足"fast-forward"模式的情況下,會直接使用"fast-forward"模式進行合并,進而忽略了-m選項對應的注釋資訊(因為使用"fast-forward"模式合并後不會産生新送出,是以為送出準備的注釋資訊會被忽略)

5、合并沖突解決

如果兩個分支中同一個檔案中的同一行内容不一樣,合并分支時就會出現沖突,此時需要我們認為介入确認,這就是解決沖突的過程。

示例:首先輸入指令造成兩條分支同一檔案同一行内容不同

#git init git_test

#echo 1 > f1.txt && git add . && git commit -m "add 1 in f1"

#echo 2 >> f1.txt && git add f1.txt && git commit -m "add 2 in f1"

#sed -i "s/2/2master/" f1.txt

#git add f1.txt && git commit -m "modify 2 at master"

#git checkout -b slv01        --------------->建立并切換新分支                                                                                    

#sed -i "s/2/2slv01/" f1.txt

#git add f1.txt && git commit -m “modify 2 at slv01”

得到結果:

git基本概念與原理

切到master分支,進行合并,有結果

git基本概念與原理

(1)對于一樣的内容已經合并,對于沖突内容給出提示,修複成一樣的内容或者git merge --abort放棄合并。

此時再看f1.txt,會标出沖突結果:

目前分支中的沖突内容: "<<<<<<< head"與"=======" 之間。

另一條合并分支中的沖突内容: "======="與"<<<<<<< new" 之間。

git基本概念與原理

我們取消沖突,git merge --abort後檢視git status,已經提示幹淨的工作區了

(2)更改沖突檔案使其不沖突

将兩個分支中的f1都修改後送出,變為2master 2slv01,之後git merge後就進入了nano編輯器編輯自己想要的注釋了。

(3)合并後對于之前的slv01分支,就可以不要了,處于非slv01分支下删除slv01分支,git branch -d slv01。

當存在某個分支沒有合并過并且需要删除時,git會提示,如果不予理會這種提示,可以使用指令:git branch -d 分支名

五、github

github:git的遠端倉庫

1、使用前準備

(1)https://github.com新增賬號

注冊好後登入賬号,右上方選new repository

git基本概念與原理

繼續進行建立遠端倉庫

git基本概念與原理

建立好以後螢幕左側會有自己的遠端倉庫名,點選進去可以看到連接配接自己遠端倉庫的兩種方式,https或者ssh

git基本概念與原理

(2)ssh方式添加公鑰

右上角姓名處下箭頭點選settings,之後選擇ssh and gpg keys

git基本概念與原理

建立公鑰私鑰:

ssh-keygen -t rsa,建立密鑰對

git基本概念與原理

去檢視公鑰檔案,将檔案内容寫入github的ssh下

git基本概念與原理

點選add ssh key

至此,github上的遠端倉庫可以使用ssh方式連接配接了。

2、git clone

git clone [email protected]:json-chenux/test.git

注意:如果這裡提示ssh拒絕連接配接的權限問題,需要将之前本機做過ssh免密登入時生成的id_rsa.pub檔案中公鑰内容粘貼至清單

git remote -v,可以看到本地倉庫與遠端倉庫的對應關系

git基本概念與原理

圖中orgin為遠端倉庫名稱,可以更改

3、推送至遠端倉庫

git push,推送本地倉庫至遠端倉庫

剛在github上的倉庫建立好後,進入該倉庫目錄,輸入一些指令:

#touch t1

#git add . && git commit -m "build t1"

#echo 123 > t1 && git add . && git commit -m "add 123 in t1"

#git checkout -b slv

#echo abc > t2 && git add t2 && git commit -m "built add t2"

之後測試遠端連接配接的倉庫,使用指令:

git push origin master:master,将本地master分支推送到遠端master分支,

"master:master"中冒号左邊的master代表你要推送的本地master分支,冒号右邊的master代表推送到遠端倉庫中的master分支

指令結果:

git基本概念與原理

如果本地倉庫master 分支推送至遠端倉庫并不存在的分支,即使用指令:

git push origin master:m1,遠端倉庫會自動建立新的分支m1。

#git push:如果git中隻有一條分支,推送時可以使用指令git push,當遠端倉庫沒有該分支,會自動建立這個分支。

當我們把建立的分支推向遠端倉庫時,需要輸入指令

#git push --set-upstream origin slv:slv   ----------->git 1.x版本

#git push -u orgin slv:slv  ------------->git 2.x版本

,将此分支slv推到遠端倉庫後,等下次在slv分支檔案做出改變時,位于slv分支時輸入git push,便可以自動推送slv分支到遠端slv分支了。

圖示:

#git branch -vv:顯示本地分支資訊

#git branch -vva:顯示本地與遠端分支資訊

圖中origin隻有master有,slv沒有,說明slv沒有推送至遠端倉庫

git基本概念與原理

将slv分支推入遠端倉庫,之後再git branch -vva,發現本地slv分支已經至遠端倉庫的myremote-slv分支。

git基本概念與原理

4、他人加入該遠端倉庫

使用指令:git remote add origin [email protected]:json-chenux/test.git

5、git pull

#git push --all:此指令表示當本地各分支與遠端倉庫各分支同名時,push所有分支的更新到對應的遠端分支。

#git fetch:此指令表示擷取遠端倉庫的更新到本地,但是不會更新本地分支中的代碼。

#git pull:此指令表示當本地分支與上遊分支同名時,對目前分支執行pull操作,對其他分支執行fetch操作,具體的差異主要取決于對應的遠端分支有沒有更新。

 #git pull remote brancha:此指令表示将remote倉庫的a分支pull到本地目前所在分支,如果你想要pull到本地的a分支,需要先checkout到本地a分支中。

 #git pull remote brancha:branchb:此指令表示将遠端倉庫的a分支pull到本地的b分支,成功之後(如果操作失敗,則後面的操作不會執行)再将遠端a分支pull到本地的目前所在的分支。                                                                                                                     

繼續閱讀