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值 文件名称

现在 树图已经创建好了,下面是图

为了方便理解 我自己画了一个图

image

图中蓝色框以上和上面简图结构是一样的

再次说明一下 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

整个提交文件的结构详细图就是上面画的图
简图如下

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 图变成了

现在整个git 图就是下面样子的

image

现在可以解答一下 上面抛出的问题:怎么知道哪个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图 如下

image

现在我们把对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 图变成下面的图
image

现在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 图
image

  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 图简化成

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 图如下

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图变成

说明一下,现在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 图 如下

现在.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 图 如下

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 图

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 图变成

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图看上去是这样的

现在切换到 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>

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 图如下

现在我们开始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 文件中

.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 图就是下面样子

8. 删除一个文件

现在整个git 的图简化成下面的样子

现在操作删除 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 做了些什么事?

直接看图即可:index 删掉了【记住一件事情 .git/objects 文件的内容没有删掉,只要有一次提交能够被到达 就能恢复我们删掉的文件】,默认情况下,工作目录的文件也被删掉了。

删除之后 创建一个新的提交

[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

9.复制一个仓库

直接将 alpha 复制出来

[root@martincentos alpha]# cd ..
[root@martincentos gittest]# cp -R alpha/ bravo
[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

现在两个仓库

两个仓库完全独立

将 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分支的内容【一共经历了四个步骤】

我本地仓库 修改了很多 假设现在我自己的仓库入下

当然我本地的仓库,在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 主要用来

现在整个 git 看上去是这样的

[root@martincentos alpha]# cat .git/FETCH_HEAD 
1347af6fd0bdeab9a4915e8d3a14276d36bd102c        branch 'master' of /root/gittest/bravo

这个记录了最新的一次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

远程仓库修改的点

没有任何修改

TMD 图片没了 讲究查看先

1
2

3

4

5

6

7

8

9

10

11

12

13

14

15

17

18

19

20

21

22

23

24

25

26

27

28

猜你喜欢

转载自blog.csdn.net/successdd/article/details/79626601