git 原理

參考文檔 https://maryrosecook.com/blog/post/git-from-the-inside-out

參考 http://blog.csdn.net/bdss58/article/details/45023493

上面的文檔2 已經整理的可以了 我自己整理一下 自己再做一遍

先說明一下我的git 是1.7 版本比較老可能有差異

[root@martincentos alpha]# git --version
git version 1.7.1

1. 創建一個新庫 【我就按照第一個英文文檔新建了】

[root@martincentos gittest]# mkdir alpha 
[root@martincentos gittest]# cd alpha/
[root@martincentos alpha]# git init
Initialized empty Git repository in /root/gittest/alpha/.git/
[root@martincentos alpha]# tree -a
.
└── .git
    ├── branches
    ├── config
    ├── description
    ├── HEAD
    ├── hooks
    │   ├── applypatch-msg.sample
    │   ├── commit-msg.sample
    │   ├── post-commit.sample
    │   ├── post-receive.sample
    │   ├── post-update.sample
    │   ├── pre-applypatch.sample
    │   ├── pre-commit.sample
    │   ├── prepare-commit-msg.sample
    │   ├── pre-rebase.sample
    │   └── update.sample
    ├── info
    │   └── exclude
    ├── objects
    │   ├── info
    │   └── pack
    └── refs
        ├── heads
        └── tags

10 directories, 14 files
[root@martincentos alpha]# 

我創建了一個文件夾 並且初始化了一個倉庫 (repository)
git init 命令就是在目錄中創建了一個 .git 文件夾

注意面 tree 查看到的.git 目錄中的object 文件夾 沒有內容

2. 添加文件到倉庫

現在 我創建了一個文件夾 data/ 創建一個文件 number.txt 和 letter.txt 文件

[root@martincentos alpha]# mkdir data
[root@martincentos alpha]# touch data/number.txt
[root@martincentos alpha]# touch data/letter.txt
[root@martincentos alpha]# git add .
[root@martincentos alpha]# tree -a
.
├── data
│   ├── letter.txt
│   └── number.txt
└── .git
    ├── branches
    ├── config
    ├── description
    ├── HEAD
    ├── hooks
    │....//我這邊去掉了爲了簡寫
    ├── index
    ├── info
    │   └── exclude
    ├── objects
    │   ├── e6
    │   │   └── 9de29bb2d1d6434b8b29ae775ad8c2e48c5391
    │   ├── info
    │   └── pack
    └── refs
        ├── heads
        └── tags

12 directories, 18 files

git add . 幹了什麼事情 ? 【一共經歷了兩個步驟】

  1. ==第一步==在.git/objects 目錄裏面創建了一個文件夾 和文件 且文件夾合起來是e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 這個是文件的哈希值 ,和文件的內容有關 。(前兩個字符作爲文件夾 後面作爲文件名 估計是爲了方便git查找,使用的策略) 爲什麼新建了兩個文件在這個目錄下面只有一個文件夾 一個文件? 就是因爲新建的兩個文件都是空,所以hash值是一樣的。 這個文件的內容包含提交文件內容,當然是經過壓縮了的

    怎麼查看文件的內容?
    使用git cat-file -p hashcode

    [root@martincentos alpha]# git cat-file -p e69d
    沒有內容 # 爲了驗證我們可以往文件裏面加內容
  2. ==第二步== 在.git/index 文件中記錄兩個文件的index
    使用git ls-files -s 命令查看

    [root@martincentos alpha]# git ls-files -s
    100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0   data/letter.txt
    100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0   data/number.txt

如果直接使用 cat 命令查看index

[root@martincentos alpha]# cat ./.git/index 
DIRCZ?_Z??Z?_Z???????⛲??CK?)?wZ???S?data/letter.txtZ?_P??wZ?_P??w?????⛲??CK?)?wZ?[r[[root@martincentos alpha]# 

出現亂碼


現在解釋一下index 文件代表什麼意思

100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0   data/letter.txt
  1. 第一個表示文件權限,
  2. 第二個是文件的hash值 這個hash 值和 .git/objects 文件夾下面的內容是一致的

    hash 值都是代表文件內容。那麼
    爲什麼兩個文件的hash 值是一樣的?因爲文件的內容都是空 所有都是這個值 可以新增加一個空文件 比對比對 就知道了

  3. 第三個是index 的狀態

那麼這個index 表示什麼意思呢? 有什麼用呢? 其實這個index 就是用來指示當前 git 指向的最新的文件的 指針,指向 .git/objects 裏面的具體文件

3.準備提交文件

現在我往data/letter.txt 文件和 data/number.txt 文件添加內容 git add .之後,順便查看一下 .git/index 文件的內容變化

[root@martincentos alpha]# echo 'a' > data/letter.txt 
[root@martincentos alpha]# echo '1' > data/number.txt 

此處的git add. 命令幹了什麼? 和上面說的一樣 在.git/objects 文件夾裏面添加文件 並且修改 .git/index 文件

現在查看.git/index 文件

[root@martincentos alpha]# git add .
[root@martincentos alpha]# tree -a
.
├── data
│   ├── letter.txt
│   └── number.txt
└── .git
    ├── branches
    ├── config
    ├── description
    ├── HEAD
    ├── hooks
    │   ...//省去了
    ├── index
    ├── info
    │   └── exclude
    ├── objects
    │   ├── 78
    │   │   └── 981922613b2afb6025042ff6bd878ac1994e85
    │   ├── d0
    │   │   └── 0491fd7e5bb6fa28c517a0bb32b8b506539d4d
    │   ├── e6
    │   │   └── 9de29bb2d1d6434b8b29ae775ad8c2e48c5391
    │   ├── info
    │   └── pack
    └── refs
        ├── heads
        └── tags

14 directories, 20 files

注意到了上面 .git/objects 文件新增了兩個文件,查看兩個新增的文件

[root@martincentos alpha]# git cat-file -p 7898
a
[root@martincentos alpha]# git cat-file -p d004
1

看到沒? 這兩個文件裏面就是我們存儲在 data/number.txt 和data/letter.txt 文件裏面的內容

那麼怎麼知道哪個hash值對應哪個文件呢?就是 .git/index 文件乾的事情,現在查看 .git/index 文件,就是使用git ls-files -s命令

[root@martincentos alpha]# git ls-files -s
100644 78981922613b2afb6025042ff6bd878ac1994e85 0   data/letter.txt
100644 d00491fd7e5bb6fa28c517a0bb32b8b506539d4d 0   data/number.txt

4. 提交文件

提交之前先設定一下git 提交的參數

#我設置了一下git 顯示彩色一點 如果你不需要 
#也可以不設置 如果你已經設置好了 就不需要設置

[root@martincentos alpha]# git config  --global user.name "martin"
[root@martincentos alpha]# git config --global user.email "[email protected]"
[root@martincentos alpha]# git config --global color.ui true 

[root@martincentos alpha]# git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#   new file:   data/letter.txt
#   new file:   data/number.txt
#

現在 我開始提交

[root@martincentos alpha]# git commit -m "first commit "
[master (root-commit) 08fd64f] first commit
 2 files changed, 2 insertions(+), 0 deletions(-)
 create mode 100644 data/letter.txt
 create mode 100644 data/number.txt

那麼提交都幹了什麼? 做了三件事情
1. 創建一個樹圖(treei graph)包含着要被提交的目錄,文件等
2. 創建一個提交對象
3. 將當前的branch(分支)指向新的提交對象

4.1 創建一個樹圖

git 就是通過一個一個樹圖來記錄每一個文件的路徑和內容,也是用來記錄日誌依據

樹圖包含兩個部分 二進制文件 和 tree 文件
二進制文件(blob)保存在 .git/objects目錄中,

這個二進制文件在 git add命令的時候創建

tree 文件,其實就是一個目錄結構 需要注意的是,每一層都會有一個樹結構 最後到根目錄
查看一下 .git/objects 目錄

[root@martincentos alpha]# tree .git/objects/ -a
.git/objects/
├── 08
│   └── fd64f66828b35515fa2d87b5177e4435d7aaa0
├── 23
│   └── e3db4885d1e39c4c55d7e1b7d27206f5bbf15e
├── 78
│   └── 981922613b2afb6025042ff6bd878ac1994e85
├── 89
│   └── 29f1d99ae7ad510c084efe4babc036c6dbb8cb
├── d0
│   └── 0491fd7e5bb6fa28c517a0bb32b8b506539d4d
├── e6
│   └── 9de29bb2d1d6434b8b29ae775ad8c2e48c5391
├── info
└── pack

8 directories, 6 files

1. 首先在data 目錄上創建一個tree 對象 tree 對象的內容如下

[root@martincentos alpha]# git cat-file -p 23e3
100644 blob 78981922613b2afb6025042ff6bd878ac1994e85    letter.txt
100644 blob d00491fd7e5bb6fa28c517a0bb32b8b506539d4d    number.txt

==第一部分== 是文件的權限 ==第二部分== 是文件的類型 是blob(二進制) 文件 ==第三部分==是對應文件的hash值,==第四部分==是文件的名稱(相對於root目錄)

查看到上面的hash 值是就是index 裏面的值

==值得一提的是所有的提交啊,文件內容啊,tree 對象啊 等等 都是保存在 .git/objects目錄裏面==

2. 其次在root(項目根目錄) 創建一個tree 對象 包含着上面的tree 對象

【如果有多個目錄,就會創建多個tree對象 層層包裹,最後形成一個樹形的結構】

[root@martincentos alpha]# git cat-file -p 8929
040000 tree 23e3db4885d1e39c4c55d7e1b7d27206f5bbf15e    data

==這裏麪包含的內容就是== 文件權限 類型(tree) hash值 文件名稱

現在 樹圖已經創建好了,下面是圖

1

爲了方便理解 我自己畫了一個圖

28

圖中藍色框以上和上面簡圖結構是一樣的

再次說明一下 root 是項目的根目錄 不是linux 的根目錄

這裏有一個疑問

爲什麼你知道在.git/objects 文件中查找對象的哈希值是什麼?比如說上面聊到的,tree的hash值是 23e3 和 8929 開頭的二進制文件?

這個先拋出來,等一會會給你解答

4.2 創建commit 對象

commit 對象其實就是一個.git/objects 目錄下面的一個文件,文件內容如下

[root@martincentos alpha]# git cat-file -p 08fd
tree 8929f1d99ae7ad510c084efe4babc036c6dbb8cb
author martin <1150354658@qq.com> 1521118810 +0800
committer martin <1150354658@qq.com> 1521118810 +0800

first commit

整個提交文件的結構詳細圖就是上面畫的圖
簡圖如下

2

4.3 將當前branch對象所對應的ref 指向當前這次提交

當前branch什麼意思呢?

現在先解釋一下 上面叫當前branch
(分支) 就是HEAD 指向的分支,這裏需要指出的其實ref(或者branch) 都是分支的意思 HEAD 想當一個快捷方式 可以迅速切換到不同的分支上面 分支其實就是對應於.git裏面的一個文件

再查看一下.git 目錄裏面的文件目錄結構

[root@martincentos alpha]# tree -a
.
├── data
│   ├── letter.txt
│   └── number.txt
└── .git
    ├── branches
    ├── COMMIT_EDITMSG
    ├── config
    ├── description
    ├── HEAD
    ├── hooks
    │//省略了
    ├── index
    ├── info
    │   └── exclude
    ├── logs
    │   ├── HEAD
    │   └── refs
    │       └── heads
    │           └── master
    ├── objects
    │..省略了
    │   ├── info
    │   └── pack
    └── refs
        ├── heads
        │   └── master
        └── tags

20 directories, 27 files

當前分支就是.git/HEAD 文件裏面指向的文件

[root@martincentos alpha]# cat ./.git/HEAD 
ref: refs/heads/master

ref 表示指向 後面的表示指向的內容也就是 .git/refs/heads/master 文件
現在查看一下 .git/refs/heads/master 文件

[root@martincentos alpha]# cat ./.git/refs/heads/master 
08fd64f66828b35515fa2d87b5177e4435d7aaa0

看到了沒?

這個文件裏面的內容就是我們提交文件的hash值,【可以參考,我畫的那張圖的commit 中的hash值】
所以現在的git 圖變成了

3

現在整個git 圖就是下面樣子的

4

現在可以解答一下 上面拋出的問題:怎麼知道哪個hash值對應哪個文件

  1. 對於工作區的文件 可以直接在.git/index 文件中查看
[root@martincentos alpha]# git ls-files -s
100644 78981922613b2afb6025042ff6bd878ac1994e85 0   data/letter.txt
100644 d00491fd7e5bb6fa28c517a0bb32b8b506539d4d 0   data/number.txt

具體的查看hash文件對應的內容使用git cat-file -p 哈希值(只要前面幾個字符就可以了)

  1. 查看提交文件及提交文件中的樹文件

    • 先找到那個提交文件,只要到.git/refs/heads/master文件中查看 得到master 所在的分支的最新一次提交的hash值
[root@martincentos alpha]# cat ./.git/refs/heads/master 
hash
  • 當然聰明如你,肯定會想到使用git log命令 查看提交歷史 然後拿到你想要的那次提交的hash值

anyway 不管使用怎麼樣的方法,你都先要拿到提交的hash值 然後通過 git cat-file -p 哈希值 先找到樹圖 然後在樹圖裏面層層往下 就可以知道不同的樹 包含的hash值和內容

使用到的兩個命令是

git cat-file -p 哈希值
git ls-files -s 列出 index文件中的內容

# git中可以使用 git help 命令  這種方式查找幫助

5. 再創建一次新的提交

5.1 修改文件 git add . 到緩衝區

現在我修改 data/number.txt 的值

[root@martincentos alpha]# echo '2' > data/number.txt 

這句話並不會對git 倉庫造成任何影響,只是工作區中 data/number.txt 中的值變成了2

[root@martincentos alpha]# cat ./data/number.txt 
2

現在的git圖 如下

5

現在我們把對data/number.txt 的修改添加到git 倉庫中

[root@martincentos alpha]# git add .

git add . 做了什麼事情?
還是和第一次git add 一樣
==第一步==將文件經過加密壓縮形成二進制文件保存到.git/objects 文件夾下面。
同樣的方法 hash值的前兩個字符作爲文件夾名稱,剩餘的部分作爲文件名保存着文件的內容,記住!!!保存的是文件的全部內容,不是差異 如果沒有變化的文件,不需要再次創建新的文件

==第二步==將文件內容的hash值,(就是.git/objects 目錄下面對應的hash值) 保存到 .git/index 文件中
下面就是index文件在add 之前和add 之後的變化

add 之前

[root@martincentos alpha]# git ls-files -s
100644 78981922613b2afb6025042ff6bd878ac1994e85 0   data/letter.txt
100644 d00491fd7e5bb6fa28c517a0bb32b8b506539d4d 0   data/number.txt

add 之後

[root@martincentos alpha]# git ls-files -s
100644 78981922613b2afb6025042ff6bd878ac1994e85 0   data/letter.txt
100644 0cfbf08886fca9a91cb753ec8734c84fcbe52c9f 0   data/number.txt

比較,發現只有 data/number.txt 這一行hash 值發生了變化

此時git 圖變成下面的圖
6

現在git status 的狀態如下

[root@martincentos alpha]# git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   data/number.txt
#

5.2 開始提交

使用git commit 提交本次修改

[root@martincentos alpha]# git commit -m "second commit"
[master e84ceea] second commit
 1 files changed, 1 insertions(+), 1 deletions(-)

和第一次提交步驟一樣 分三步走
1. 創建tree 圖
2. 創建提交對象
3. 將當前branch(分支)更新到當前的提交上去,沒有什麼特殊的

但是需要注意的最底層的那個tree 文件。
文件中包含的blob 文件只有修改的data/number.txt 那一行數據發生了變化 變成了.git/index 文件中指定的值

5.1.1 現在我們就來一層一層的獲取commit 對象裏面包裹的對象

1 . 先查看一下.git/refs/heads/master 文件
[root@martincentos alpha]# cat ./.git/refs/heads/master 
e84ceea7a3b6983f4fdec9058cf42c0ab02361c5

這個裏面的hash值就是我們之前的提交顯示的簡介hash信息中的全稱

[root@martincentos alpha]# git commit -m "second commit"
[master e84ceea] second commit
 1 files changed, 1 insertions(+), 1 deletions(-)

上面提交中[master e84ceea]只是部分信息

2. 查看提交對象

先查看現在git 圖
7

  1. 查看提交對象內容
[root@martincentos alpha]# git cat-file -p e84ce
tree 2d798b26837fd4b198a3cc0c95084032be8ea42d
parent 08fd64f66828b35515fa2d87b5177e4435d7aaa0
author martin <1150354658@qq.com> 1521128648 +0800
committer martin <1150354658@qq.com> 1521128648 +0800

second commit

和第一提交不同點在於 多了一行parent 用來指向上一次提交的提交對象的hash值
這就是git log 能夠查看到之前的歷史的原因

  1. 再往下查 tree
[root@martincentos alpha]# git cat-file -p 2d798b2
040000 tree a2d2b3fada83eb0f8f02187fd7ee6a111391d700    data

其實這個就是項目根目錄的tree 對象

  1. 再往下查tree
[root@martincentos alpha]# git cat-file -p a2d2b3f
100644 blob 78981922613b2afb6025042ff6bd878ac1994e85    letter.txt
100644 blob 0cfbf08886fca9a91cb753ec8734c84fcbe52c9f    number.txt

這個就是data 目錄對應的tree 對象 裏面包含着二進制文件的hash值。有心的你就會發現 文件的hash值就是.git/index 文件中的hash值

[root@martincentos alpha]# git ls-files -s
100644 78981922613b2afb6025042ff6bd878ac1994e85 0   data/letter.txt
100644 0cfbf08886fca9a91cb753ec8734c84fcbe52c9f 0   data/number.txt

此時git 圖簡化成

8

5.3幾點需要說明

  1. 只有修改的文件纔會新添加到.git/objects 文件中 沒有修改的文件不會發生任何變化
  2. 依靠commit對象 git 可以保存一個項目的所有歷史
  3. refs 其實就是 .git/refs 文件夾下面的文件 就是branch 分支
    下面是我的另外一個項目的分支
dingmac@cos-git$ tree ./.git/refs/
./.git/refs/
├── heads
│   └── master
├── remotes
│   └── origin
│       └── master
└── tags

和本實驗的分支

[root@martincentos alpha]# tree ./.git/refs/
./.git/refs/
├── heads
│   └── master
└── tags

2 directories, 1 file

其實分支就是 ./.git/refs/ 的目錄和文件名

我們可以給這些文件夾起一個有意義的名字 比如 bugfix-180316表示要修復的一些問題
這個之後我們會在創建分支的時候再細聊

  1. .git/objects 文件夾是不可改變的 只要 一旦 git add 文件 之後就會被添加到 .git/objects 文件 就可以說 你的文件已經被git 記住了 可以找回來的,但是也是要當心的,如果沒有被任何ref 保存過 會很容易丟失
  2. refs 文件包含的內容是可以修改的 修改最勤快的就是那個./git/HEAD文件
    除了這個.git/HEAD 文件還有.git/FETCH_HEAD 還有 .git/MERGE_HEAD

6. checkout 一個 commit 檢出一個提交

就是在一次提交上checkout

[root@martincentos alpha]# git checkout e84cee
Note: checking out 'e84cee'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b new_branch_name

HEAD is now at e84ceea... second commit

值得注意的是現在HEAD 文件裏面就是 就是e84cee 對應的hash值全稱

[root@martincentos alpha]# cat ./.git/HEAD 
e84ceea7a3b6983f4fdec9058cf42c0ab02361c5

現在HEAD 處於一個detached (遊離) 的狀態

6.1 那麼checkout 幹了些啥?

這個簡單的命令一共做了4件事情
1. 根據 hash值找到提交對象
2. 拉取到樹圖的內容,根據 樹圖中的blob 哈希值對應的文件 從 ./.git/objects 目錄中寫入到當前的工作目錄 (working directory)中
3. 將index 文件修改成 tree 文件中指定的hash值
4. 最後將HEAD 文件指向這次hash值

在上面的步驟中 2和 3 其實沒有任何改變 因爲該commit 中tree 對象裏面的內容就是 現在index 裏面的內容,所以不需要改變

現在 git 圖如下

9

6.2 在新的check 上面創建新的提交

現在先來修改一下 number.txt 文件

[root@martincentos alpha]# echo "3" > data/number.txt 
[root@martincentos alpha]# git add .
[root@martincentos alpha]# git commit -m "third commit"
[detached HEAD 550f984] third commit
 1 files changed, 1 insertions(+), 1 deletions(-)

此時git圖變成

10

說明一下,現在HEAD 處於detached (遊離)狀態, 也就是說此時沒有 分支跟蹤當前的HEAD 如果現在HEAD 分支切換到master 上面 如果不知道 當前的commit hash值 就不知道怎麼回來了

現在查看一下所有的log

[root@martincentos alpha]# git log  --decorate=full
commit 550f9843419d2616e03587232500ca8264cc9272 (HEAD)
Author: martin <1150354658@qq.com>
Date:   Fri Mar 16 01:23:57 2018 +0800

    third commit

commit e84ceea7a3b6983f4fdec9058cf42c0ab02361c5 (refs/heads/master)
Author: martin <1150354658@qq.com>
Date:   Thu Mar 15 23:44:08 2018 +0800

    second commit

commit 08fd64f66828b35515fa2d87b5177e4435d7aaa0
Author: martin <1150354658@qq.com>
Date:   Thu Mar 15 21:00:10 2018 +0800

    first commit

–decorate=full 表示查看稍微詳細一點的信息

查看一下branch

[root@martincentos alpha]# git branch -vvv
* (no branch) 550f984 third commit
  master      e84ceea second commit

如果不記一下 550f984 這個 就沒有辦法再回來到這個提交

* (no branch) 550f984 third commit

6.3 創建一個branch 分支

[root@martincentos alpha]# git branch deputy

這句話幹了啥?

其實branch 就是對應於 .git/refs 文件夾的一個文件 ,其實創建一個分支就是在該文件夾下面創建一個文件或者文件夾 並把文件的內容指向本次提交

現在的git 圖 如下

11

現在.git/refs 目錄結構如下

[root@martincentos alpha]# tree .git/refs/
.git/refs/
├── heads
│   ├── deputy
│   └── master
└── tags

2 directories, 2 files

deputy 文件的內容就是550f984 開頭的hash值

[root@martincentos alpha]# cat .git/refs/heads/deputy 
550f9843419d2616e03587232500ca8264cc9272

所以 創建一個分支對git來講就是創建一個文件那麼簡單 lightweight(輕量級)

6.4 切換分支

切換分支原理上就是修改 .git/HEAD 文件的內容
現在我們先查看一下HEAD 文件的內容

[root@martincentos alpha]# cat .git/HEAD 
550f9843419d2616e03587232500ca8264cc9272

也可以通過 git branch -vvv 查看分支的狀態

[root@martincentos alpha]# git branch -vvv
* (no branch) 550f984 third commit
  deputy      550f984 third commit
  master      e84ceea second commit

現在我們開始切換分支

[root@martincentos alpha]# git checkout master
Previous HEAD position was 550f984... third commit
Switched to branch 'master'

會說明之前的HEAD 指向的是哪一個commit 的hash值 和提交的comment(備註)。
現在,我們再來看一下 .git/HEAD 文件

[root@martincentos alpha]# cat .git/HEAD 
ref: refs/heads/master

現在 HEAD 指向了refs/heads/master 也就是傳說中的master 分支。

查看一下分支

[root@martincentos alpha]# git branch -vvv
  deputy 550f984 third commit
* master e84ceea second commit

帶*號的表示HEAD所在的分支 值得注意的是那個遊離分支木有了

6.4.1 那麼切換分支 git 幹了啥?

步驟如下:

  1. 根據master 找到commit對象
  2. 從commit對象中獲取樹圖,從樹圖裏面拿到 二進制文件的hash值 根據hash值從 .git/objects 目錄中將文件恢復出來copy 到工作目錄中
  3. 更新 .git/index 文件將裏面對應的hash值更新成上面tree 圖中的文件hash 值
  4. 將HEAD 指針切換成 master分支

和之前checkout 哈希值 步驟相同
只不過這一次工作區域的內容發生了改變 ,index 文件發生了改變而已

現在的git 圖 如下

12

6.4.1 如果工作目錄中也修改了文件,可能和會引起 git checkout停止

什麼意思呢?
打個比方:
1. 假如 master對應的commit 對象中 data/number.txt 裏面的內容是 1
2. 而現在HEAD 還在 上圖a3 對應的提交上,如果此時我在工作路徑上面我修改 data/number.txt 內容是 123
3. 我再切換分支的時候 將會報錯 因爲切換分支 會將commit 對應的樹圖中的 文件 覆蓋寫入到工作目錄和 現在的工作目錄中的文件發生衝突就沒有辦法checkout 了

下面是我的例子:

[root@martincentos alpha]# git branch -vv
* deputy 550f984 third commit
  master e84ceea second commit

現在我在deputy 分支

我修改工作目錄中的data/number.txt

[root@martincentos alpha]# echo "1200" >  data/number.txt 
[root@martincentos alpha]# git status
# On branch deputy
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   data/number.txt
#
no changes added to commit (use "git add" and/or "git commit -a")

現在我要切換分支到master 分支

[root@martincentos alpha]# git checkout master
error: You have local changes to 'data/number.txt'; cannot switch branches.

此時有兩個辦法 git stash 或者直接git commit 之後再切換

比如 我先存儲起來等切換之後再恢復回來

[root@martincentos alpha]# git stash
Saved working directory and index state WIP on deputy: 550f984 third commit
HEAD is now at 550f984 third commit
[root@martincentos alpha]# git checkout master
Switched to branch 'master'

切換之後恢復

[root@martincentos alpha]# git stash pop
Auto-merging data/number.txt
CONFLICT (content): Merge conflict in data/number.txt
[root@martincentos alpha]# cat data/number.txt 
<<<<<<< Updated upstream
2
=======
1200
>>>>>>> Stashed changes

發生衝突
我這裏就直接先解決了 文章後面會講到 怎麼解決衝突,你可以直接跳過

1 . 如果你想保留 1200 就把data/number.txt 內容換成1200 然後進行一次merge 然後提交之後 就會在master的基礎上向後走了一個提交 就和 deputy 分支有了交叉口 而且在之後merge deputy 分支的時候 還需要解決衝突

這個不是一個好的解決方法

當然如果你stash 之後切換到master 分支 工作完之後 再切換回deputy 分支 進行工作 那麼stash 還是很不錯的 方法是很多的可以自己選擇

any way 你可以先不用看這些

我直接使用git reset –merge 恢復到merge之前

  1. 你可以先在deputy分支先提交再切換到master 分支

我感覺靠譜的方法還是先stash 切換到master 工作完成之後再回到deputy 分支 git stash apply 就可以了

7.Merge 分支

7.1 merge 一個祖先branch 不會有任何動作

爲啥?

因爲祖先有的你都有了,所以不會做任何事情

再看一下現在的git 圖

13

master 就是deputy 的祖先節點

[root@martincentos alpha]# git merge master
Already up-to-date.

7.2 merge 一個孩子branch

先切換到master 分支,然後merge deputy

[root@martincentos alpha]# git checkout master
Switched to branch 'master'
[root@martincentos alpha]# git merge deputy
Updating e84ceea..550f984
Fast-forward
 data/number.txt |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

7.2.1 那麼merge 到底幹了些什麼事?

首先master 分支看到 deputy 是master 分支的孩子節點,所有使用快進merge

master 分支叫做receiver(接收者) deputy 分支叫做giver(提供者)

  1. 先獲取deputy中的commit 對象,
  2. 從commit對象中抽取樹圖(樹圖中包含最終的二進制文件hash值) ,根據這個hash值將.git/objects 文件中的內容寫到working directory(工作目錄)
  3. 更新index 文件
  4. 將master 的文件內容更新成deputy 對應的commit的hash值

此時git 圖變成
15

commit 可以理解成一個一個的歷史節點,這種fast-forward 模式的merge 不會有任何新的commit 的產生,變化的只有git 圖

值得一說的是HEAD文件沒有變奧 變得之後master 文件

7.3 MERGE兩個並行分支

我們分別在master 和deputy 分支 分別創建一個提交

[root@martincentos alpha]# echo "3" > data/number.txt 
[root@martincentos alpha]# git commit -am "master分支測試並行合併"
# On branch master
nothing to commit (working directory clean)
[root@martincentos alpha]# git checkout deputy
Switched to branch 'deputy'


#and

[root@martincentos alpha]# echo b > data/letter.txt
[root@martincentos alpha]# git commit -am "deputy分支測試並行合併"
[deputy 4512f26] deputy分支測試並行合併
 1 files changed, 1 insertions(+), 1 deletions(-)

現在 整個git圖看上去是這樣的
17

現在切換到 deputy 分支合併master 【之前有一個誤解 說一定要切換到master 分支然後再merge 其實沒有那麼嚴格 master 分支本質上也是個分支】

###master 分支的情況
[root@martincentos alpha]# git plog
* 269e530 - (HEAD, master) master 分支測試並行合併 (5 minutes ago) <martin>
* 550f984 - third commit (3 hours ago) <martin>
* e84ceea - second commit (5 hours ago) <martin>
* 08fd64f - first commit (7 hours ago) <martin>

### deputy 分支的情況
[root@martincentos alpha]# git checkout deputy
Switched to branch 'deputy'
[root@martincentos alpha]# git plog
* 4512f26 - (HEAD, deputy) deputy分支測試並行合併 (34 minutes ago) <martin>
* 550f984 - third commit (3 hours ago) <martin>
* e84ceea - second commit (5 hours ago) <martin>
* 08fd64f - first commit (7 hours ago) <martin>

上面出現了一個命令叫plog 是我自己alias 的一個命令 主要用來 pretty 展示log的

git config --global alias.plog2 "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative"

上面參數配置來源於 互聯網

順便說一點 可以設置自動填充

wget https://raw.githubusercontent.com/git/git/master/contrib/completion/git-completion.bash
# 下載完成之後 下載到/root/ 文件夾 你可以下載到任何位置
然後執行
. /root/git-completion.bash

現在我的git提交圖是下面這樣的

[root@martincentos alpha]# git merge master
Merge made by recursive.
[root@martincentos alpha]# git plog
*   0e73c8c - (HEAD, deputy) Merge branch 'master' into deputy (16 seconds ago) 
|\  
| * 269e530 - (master) master 分支測試並行合併 (9 minutes ago) <martin>
* | 4512f26 - deputy分支測試並行合併 (36 minutes ago) <martin>
|/  
* 550f984 - third commit (3 hours ago) <martin>
* e84ceea - second commit (5 hours ago) <martin>
* 08fd64f - first commit (7 hours ago) <martin>

18

Merge 一共經歷了8個步驟

  1. 將giver的那次提交的hash 寫入到.git/MERGE_HEAD 文件中 【這個是一箇中間文件 用來查看merge 是不是已經完成了】
  2. 查找到兩個分支的最近的共同祖先 就是上面圖的a3 也就是我的提交的550f984
  3. git 將三方的提交都提取出來(原文中使用的是indices 指數,其實就是將三者的提交提取出來,來一個三方合併)
  4. 建立一個三方差異圖,包含add, remove, modify or conflict 信息,如果是差異 將會出現兩個條目
    對於差異的 條目a 和條目 b 只要兩者之間一個和base 一樣 另外一個不管怎麼樣 都會變成那個和base 不一樣的樣子。條目a = base條目; 條目b !=base 條目;最終就是條目b
  5. 將上麪條目修改的信息 寫入到工作目錄
  6. 將最後兩個文件的hash 值寫入到 .git/index文件中
  7. 要想永久保留,必須形成一次提交,聰明的git 自動幫你創建了一次提交。提交的內容如下
[root@martincentos alpha]# git cat-file -p 0e73c8caa
tree 463bf1e217d85b4dc38b678ccef5007331363154
parent 4512f26fc30b74a2ac81d5fcf9b8bcb12f9dff6c
parent 269e530dfb6f21dcc7bebc80958012f1834c5926
author martin <1150354658@qq.com> 1521145379 +0800
committer martin <1150354658@qq.com> 1521145379 +0800

Merge branch 'master' into deputy

需要注意的是 這一次的merge 有兩個parent

可以使用-m 參數指定 提交的備註 我直接沒有使用

  1. 最後將 .git/refs/heads/deputy 文件的內容修改成本次merge 提交的hash值
[root@martincentos alpha]# cat ./.git/refs/heads/deputy 
0e73c8caa50b77120e3c6dc2e9ba6ec4f832a992

現在master 分支使我們這次提交的祖先分支了 現在可以直接fast-forward 跳轉到deputy 分支上面

[root@martincentos alpha]# git checkout master
Switched to branch 'master'
[root@martincentos alpha]# git merge deputy
Updating 269e530..0e73c8c
Fast-forward

現在master 分支和 deputy 分支都在一個提交上面了
一次Merge結束

7.4 MERGE 兩個並行分支並且都修改了同一個文件的內容

現在我們來創建測試環境
1. master 分支將data/number.txt內容修改成 5
2. deputy 分支 將data/number.txt 內容修改成 6

#master 分支
[root@martincentos alpha]# echo 6 > ./data/number.txt 
[root@martincentos alpha]# git commit -am "merge with conflict"
[master be11ab8] merge with conflict
 1 files changed, 1 insertions(+), 1 deletions(-)

#deputy 分支
[root@martincentos alpha]# git checkout deputy
Switched to branch 'deputy'
[root@martincentos alpha]# cat ./data/number.txt 
3
[root@martincentos alpha]# echo 5 > ./data/number.txt 
[root@martincentos alpha]# git commit -am "merge with conflict deputy 分支"
[deputy 041dab9] merge with conflict deputy 分支
 1 files changed, 1 insertions(+), 1 deletions(-)

現在的git 圖如下

19

現在我們開始merge,merge 之前我們先看一下 兩個分支的提交

#master 分支
[root@martincentos alpha]# git plog
* be11ab8 - (HEAD, master) merge with conflict (35 minutes ago) <martin>
*   0e73c8c - Merge branch 'master' into deputy (84 minutes ago) <martin>
|\  
| * 269e530 - master 分支測試並行合併 (2 hours ago) <martin>
* | 4512f26 - deputy分支測試並行合併 (2 hours ago) <martin>
|/  
* 550f984 - third commit (4 hours ago) <martin>
* e84ceea - second commit (6 hours ago) <martin>
* 08fd64f - first commit (9 hours ago) <martin>

master 分支最新一次的提交時be11ab8

# deputy分支
[root@martincentos alpha]# git plog
* 041dab9 - (HEAD, deputy) merge with conflict deputy 分支 (35 minutes ago) <mar
*   0e73c8c - Merge branch 'master' into deputy (87 minutes ago) <martin>
|\  
| * 269e530 - master 分支測試並行合併 (2 hours ago) <martin>
* | 4512f26 - deputy分支測試並行合併 (2 hours ago) <martin>
|/  
* 550f984 - third commit (4 hours ago) <martin>
* e84ceea - second commit (6 hours ago) <martin>
* 08fd64f - first commit (9 hours ago) <martin>

deputy 最新的一次提交是041dab9 這個和後面提到的.git/MERGE_HEAD 文件的是一樣的

現在merge 出現衝突

[root@martincentos alpha]# git merge deputy
Auto-merging data/number.txt
CONFLICT (content): Merge conflict in data/number.txt
Automatic merge failed; fix conflicts and then commit the result.

出現衝突經歷了什麼?同樣的我們還是同樣的8個步驟

  1. 還是將giver的 commit hash值寫入到.git/MERGE_HEAD 文件中

20

.git/MERGE_HEAD 裏面的內容就是
就是 deputy 分支最後一次提交的hash值

我們查看一下 .git/MERGE_HEAD

[root@martincentos alpha]# cat ./.git/MERGE_HEAD
041dab91a17831be7ef633a6be15b5d2dc47f074
  1. 找到共同祖先也就是b4
  2. 獲取三方的indices(目錄,指數)
  3. git 生成一個三方差異的一個差異,包含add, remove, modify or conflict,這個只有一個文件 data/number.txt 文件,因爲有差異所以被標識成 conflict
  4. 差異被應用到工作目錄副本
[root@martincentos alpha]# cat ./data/number.txt 
<<<<<<< HEAD
6
=======
5
>>>>>>> deputy
  1. 改變之後的index被寫入到.git/index 文件中
[root@martincentos alpha]# git ls-files -s
100644 61780798228d17af2d34fce4cfbdf35556832472 0   data/letter.txt
100644 00750edc07d6415dcc07ae0351e9397b0222b7ba 1   data/number.txt
100644 1e8b314962144c26d5e0e50fd29d2ca327864913 2   data/number.txt
100644 7ed6ff82de6bcc2a78243fc9c54d3ef5ac14da69 3   data/number.txt

可能git 版本問題,我的index文件中標識在第三欄,如果不是0 標識有衝突了

1標識base 的hash值,2標識 receiver的hash值,3 標識giver 的hash值

我們開始解決問題,修改工作區的文件

修改/data/number.txt的文件將裏面的內容修改掉, 重新git add . ,那麼.git/index 文件 就會重新更改,在沒有git add
之前,.git/objects 沒有任何修改 ,add 之後 index文件就清淨了

[root@martincentos alpha]# echo 11 >./data/number.txt 
[root@martincentos alpha]# git add .
[root@martincentos alpha]# git ls-files -s
100644 61780798228d17af2d34fce4cfbdf35556832472 0   data/letter.txt
100644 b4de3947675361a7770d29b8982c407b0ec6b2a0 0   data/number.txt
7. 現在我們提交我們的修改,提交的時候,git查看到.git/MERGE_HEAD 文件內容還在,git 檢查index 文件發現沒有衝突了,【可以理解成這個conflict檢查 像鉤子一樣掛載commit 上面】,這個時候就將.git/MERGE_HEAD 文件刪掉,MERGE完成!
  1. 將當前分支,指向當前的提交

現在整個的git 圖就是下面樣子

21

22

8. 刪除一個文件

現在整個git 的圖簡化成下面的樣子

23

現在操作刪除 git rm /data/letter.txt

[root@martincentos alpha]# git rm --cached data/number.txt 
rm 'data/number.txt'

我用的是git rm --cached 我想保留工作目錄的文件
我本來要刪除data/letter.txt

文件的刪除錯了怎麼辦,還沒有提交 使用git reset 命令回去

如果使用的是git rm --cached 刪除 直接git reset 就回去了,如果使用的是git rm刪除 ,可以使用git reflog 查找到本次提交的hash 值,然後 git reset --hard 哈希值

不過 git reset --hard 是一個危險的命令

言歸正傳 git rm 做了些什麼事?

24

直接看圖即可:index 刪掉了【記住一件事情 .git/objects 文件的內容沒有刪掉,只要有一次提交能夠被到達 就能恢復我們刪掉的文件】,默認情況下,工作目錄的文件也被刪掉了。

刪除之後 創建一個新的提交【得出一個結論 index 一旦變化就需要創建一個新的提交】

[root@martincentos alpha]# git rm data/letter.txt 
rm 'data/letter.txt'
[root@martincentos alpha]# git commit -m "rm file"
[master 01ac460] rm file
 1 files changed, 0 insertions(+), 1 deletions(-)
 delete mode 100644 data/letter.txt

25

9.複製一個倉庫

直接將 alpha 複製出來

[root@martincentos alpha]# cd ..
[root@martincentos gittest]# cp -R alpha/ bravo
#grep -C 2 表示 前後兩行都打印出來 -e 表示查詢的內容一旦查詢的內容出現“-” 就需要這個參數指定
[root@martincentos gittest]# man cp | grep  -C 2 -e "-R"
              use full source file name under DIRECTORY

       -R, -r, --recursive
              copy directories recursively

[root@martincentos gittest]# tree 
.
├── alpha
│   └── data
│       └── number.txt
└── bravo
    └── data
        └── number.txt

4 directories, 2 files

現在兩個倉庫

26

兩個倉庫完全獨立

將 bravo 作爲本庫的遠程倉庫

[root@martincentos alpha]# git remote add bravo /root/gittest/bravo/
[root@martincentos alpha]# git remote -vvv
bravo   /root/gittest/bravo/ (fetch)
bravo   /root/gittest/bravo/ (push)
[root@martincentos alpha]# git pull bravo master
From /root/gittest/bravo
 * branch            master     -> FETCH_HEAD
Already up-to-date.
[root@martincentos alpha]# 

9.1 現在我們修改文件並且添加到遠程分支

沒有辦法提交

#添加新的文件
[root@martincentos alpha]# echo "new" >data/new.txt
#添加新的提交
[root@martincentos alpha]# git add .
[root@martincentos alpha]# git commit -m "new content"
[master d725b1a] new content
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 data/new.txt

####重點來了

[root@martincentos alpha]# git push bravo master
Counting objects: 6, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (4/4), 308 bytes, done.
Total 4 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (4/4), done.
remote: error: refusing to update checked out branch: refs/heads/master
remote: error: By default, updating the current branch in a non-bare repository
remote: error: is denied, because it will make the index and work tree inconsistent
remote: error: with what you pushed, and will require 'git reset --hard' to match
remote: error: the work tree to HEAD.
remote: error: 
remote: error: You can set 'receive.denyCurrentBranch' configuration variable to
remote: error: 'ignore' or 'warn' in the remote repository to allow pushing into
remote: error: its current branch; however, this is not recommended unless you
remote: error: arranged to update its work tree to match what you pushed in some
remote: error: other way.
remote: error: 
remote: error: To squelch this message and still keep the default behaviour, set
remote: error: 'receive.denyCurrentBranch' configuration variable to 'refuse'.
To /root/gittest/bravo/
 ! [remote rejected] master -> master (branch is currently checked out)
error: failed to push some refs to '/root/gittest/bravo/'

爲什麼無法提交

  1. 其實上面的錯誤提示裏面有提示了

refusing to update checked out branch: refs/heads/master,By default, updating the current branch in a non-bare repository(非裸倉庫) is denied, because it will make the index and work tree inconsistent(不一致)

更新到一個非裸倉庫默認情況下不被允許,因爲會導致index 和工作目錄樹的不一致

with what you pushed, and will require ‘git reset –hard’ to match the work tree to HEAD.

在遠程倉庫中使用git reset --hard hash值 的方式 切換HEAD的位置,下面是操作

#bravo倉庫的提交 【遠程的】
[root@martincentos bravo]# git plog
* 01ac460 - (HEAD, master) rm file (44 minutes ago) <martin>
*   821474c - conflict merge (77 minutes ago) <martin>
|\  
| * 041dab9 - (deputy) merge with conflict deputy 分支 (3 hours ago) <martin>
...

# alpha 倉庫的提交歷史 【本地的】
[root@martincentos alpha]# git plog
* d725b1a - (HEAD, master) new content (27 minutes ago) <martin>
* 01ac460 - rm file (43 minutes ago) <martin>
*   821474c - conflict merge (76 minutes ago) <martin>
|\  
| * 041dab9 - (deputy) merge with conflict deputy 分支 (3 hours ago) <martin>

到bravo倉庫 執行git reset --hard d725b1a 現在遠程倉庫 就是最新的

[root@martincentos bravo]# git reset --hard d725b1a
HEAD is now at d725b1a new content
[root@martincentos bravo]# git plog
* d725b1a - (HEAD, master) new content (31 minutes ago) <martin>
* 01ac460 - rm file (47 minutes ago) <martin>
*   821474c - conflict merge (79 minutes ago) <martin>

2 . 繼續解釋

You can set ‘receive.denyCurrentBranch’ configuration variable to
‘ignore’ or ‘warn’ in the remote repository to allow pushing into
its current branch; however, this is not recommended unless you
arranged to update its work tree to match what you pushed in some
other way.

你可以設置’receive.denyCurrentBranch’ 爲’ignore’ |’warn’ 這個不是建議的,所以最好還是不要這麼做

具體做法如下:

  1. 修改遠程倉庫的 receive.denycurrentbranch 配置
[root@martincentos gittest]# cd bravo/
[root@martincentos bravo]# ls
data
[root@martincentos bravo]# git config receive.denyCurrentBranch ignore
[root@martincentos bravo]# git config -l
    user.name=martin
    ....
    receive.denycurrentbranch=ignore
[root@martincentos bravo]# 
  1. 此時切換回alpha 倉庫提交【我新建了一個提交】
[root@martincentos alpha]# git push bravo master
Counting objects: 5, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (1/1), done.
Writing objects: 100% (3/3), 274 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
To /root/gittest/bravo/
   d725b1a..e2b7bec  master -> master
  1. 還沒有結束 ,再切換回遠程分支bravo
[root@martincentos bravo]# git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   data/new.txt
#

其實就是需要將HEAD 進行切換一下,我們使用hook進行自動完成reset --hard 的功能
  1. 拷貝一份.git/hooks/post-update.simple 命名爲 .git/hooks/post-update 修改這個文件爲
...
#上面不用修改
#exec git update-server-info
# 添加以下三行即可
unset GIT_DIR
cd ..
git checkout -f

5.重新測試

[root@martincentos alpha]# echo "b" > data/letter.txt
[root@martincentos alpha]# git add .
[root@martincentos alpha]# git commit -m "new commit"
[master a549e79] new commit
...

[root@martincentos alpha]# git push bravo master
Counting objects: 6, done.
...
[root@martincentos alpha]# cd -
/root/gittest/bravo
[root@martincentos bravo]# git plog
* a549e79 - (HEAD, master) new commit (24 seconds ago) <martin>
* e2b7bec - delelet data/new.txt (13 minutes ago) <martin>
* d725b1a - new content (75 minutes ago) <martin>
* 01ac460 - rm file (2 hours ago) <martin>

主要看上面提交的a549e79這個編號,兩個倉庫都有a549e79這個提交了

正確的建遠程倉是如下

clone 一個 bare 倉庫 ,裸倉庫和正常我們的倉庫不一樣的是,裸倉庫將本來在.git 目錄下面的文件 都移動到了項目根目錄

1.git clone xxx yyy --bare 
2.git remote add yyy yyyurl(path),添加爲遠程分支 
3.git push yyy master 就可以了

提交到遠程分支一共經歷了三步

  1. 現將遠程分支的最新的提交和現在推送過來最新的提交 做對比(git push 遠程 branch,就是你提交的倉庫和遠程同樣的倉庫進行對比),將現在工作目錄中的.git/objects 文件夾中提交的內容 更新到遠程倉庫 【alpha/.git/objects/ 到 bravo/objects/】

非裸倉庫還要經歷的步驟 更新index 文件 更新工作區的文件

  1. 遠程倉庫中的 .git/refs/heads/master 文件修改成 最新的commit
  2. 本地倉庫的.git/refs/remotes/bravo/master 變成了最新的一次提交(表示我現在和遠程是一樣的)

10.從遠程分支中抓取

現在我們bravo 倉庫和alpha 倉庫都是正常的倉庫,並且兩個倉庫是一樣的
我們在 bravo [遠程倉庫] 創建一個提交

[root@martincentos bravo]# vi data/number.txt 
[root@martincentos bravo]# git commit -am "new number"
[master 1347af6] new number
 1 files changed, 1 insertions(+), 1 deletions(-)
[root@martincentos bravo]# cd -
/root/gittest/alpha
[root@martincentos alpha]# git pull bravo master
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 4 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (4/4), done.
From /root/gittest/bravo
 * branch            master     -> FETCH_HEAD
Updating a549e79..1347af6
Fast-forward
 data/number.txt |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

現在在alpha 分支,抓取到bravo分支的內容【一共經歷了四個步驟】

我本地倉庫 修改了很多 假設現在我自己的倉庫入下

27

當然我本地的倉庫,在b11之後還有很多此的提交

  1. 第一步 抓取到bravo 倉庫你要抓取的分支的最新的commit

  2. 第二步 在提交的12 往前拉一直拉到 alpha 分支沒有的commit,將這些提交修改的 .git/objects 文件 copy 到alpha 分支中的.git/objects 文件倉庫中

  3. 第三步 將12 的這次提交的hash值 放到alpha 倉庫 .git/refs/remotes/bravo/master
  4. 修改 alpha/.git/FETCH_HEAD
[root@martincentos alpha]# cat .git/FETCH_HEAD 
1347af6fd0bdeab9a4915e8d3a14276d36bd102c        branch 'master' of /root/gittest/bravo

alpha/.git/FETCH_HEAD 這個記錄了最新的一次FETCH 命令,用來將本地的分支 更新成最新的commit ,所以這裏面的內容永遠慢於 最新的commit

[root@martincentos alpha]# cat .git/refs/remotes/bravo/master 
a549e79060ae10244bff173c1738c76aa184a255

記錄了抓取到的遠程分支的hash值

我使用的是git pull 所以還做了一步將master 分支更新到了最新的一次commit 即git merge FETCH_HEAD

小結

提交到遠程倉庫

本地倉庫修改的地方

  1. .git/refs/remotes/bravo/master

遠程倉庫修改的點

  1. .git/objects
  2. .git/refs/heads/master

如果遠程倉庫不是裸倉庫 還要修改index 和工作目錄

從遠程倉庫分支 抓取分支

本地倉庫修改的點
1. .git/objects
2. .git/refs/remotes/bravo/master
3. .git/FETCH_HEAD
4. .git/refs/heads/master【這個需要使用git merge FETCH_HEAD】
5. git merge FETCH_HEAD 修改working 工作區和index

遠程倉庫修改的點

沒有任何修改

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章