在Git中删除分支时会发生什么

时间:2016-01-19 16:03:03

标签: git branch git-branch

我一直在寻找这个问题的答案,但我仍然不完全确定答案。 我发现的关于删除分支的大多数信息只不过是手册所说的副本(例如here on SO)。 我认为一个核心问题是我不确切知道Git中的一个分支是什么(即使有许多文章声称要解释这一点)。 我找到的唯一有用的信息是this SO answer和使用git安装的文档。

我的问题:

当我运行git branch -d BRANCH_NAME时,

  1. 引擎盖下发生了什么?
  2. 外部有什么变化,即我与存储库的交互如何变化?
  3. 这些变化中的任何一个都可以被视为历史上的变化吗?
  4. 以及git branch -D BRANCH_NAME的相同子请求。

    我目前的理解:

    首先我对分支的看法: 根据上下文,术语分支指的是指向某个提交的指针(严格称为分支头),或者指向导致该提交的提交列表

    对于git branch -d BRANCH_NAME

    ,我认为发生了什么(但我不太确定)
    1. 删除了指向分支头的指针,不再有
    2. 我再也看不到任何列表中的分支了,我不能再切换到它或从中分支了(虽然我想我可以在该提交中创建一个新的分支,以有效地实现这些目的)
    3. 可能不是:提交仍然存在,它们只是没有标记分支的名称了?
    4. 对于git branch -D BRANCH_NAME

      ,我认为会发生什么
      1. 删除了指向分支头的指针,并删除了不在其他分支上的任何提交
      2. 我再也看不到任何列表中的分支了,我无法再切换到它或从中分支,或以任何方式检索代码
      3. 是:分支中的提交丢失

2 个答案:

答案 0 :(得分:5)

无论是使用git branch -d还是git branch -D删除,git都不会删除提交,只会删除分支ref。请继续阅读,看看这意味着什么。

首先,我们将设置一个简单的演示历史记录。

$ touch initial ; git add initial ; git commit -m 'Initial commit'
[master (root-commit) 2182bb2] Initial commit
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 initial

$ git checkout -b mybranch
Switched to a new branch 'mybranch'

此时,mastermybranch都指向同一个 提交,我们至少可以通过两种方式进行验证。

$ git lola
* 2182bb2 (HEAD -> mybranch, master) Initial commit

请注意,git lola是一个非标准但非常有用的别名, 相当于

$ git log --graph --decorate --pretty=oneline --abbrev-commit --all
* 2182bb2 (HEAD -> mybranch, master) Initial commit

我们将在mybranch上创建新提交后查看另一种方式。

$ touch mybranch ; git add mybranch ; git commit -m 'My branch'
[mybranch 7143aa4] My branch
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 mybranch

执行此操作后,我们确实在多个分支上进行了多次提交。

$ git lola
* 7143aa4 (HEAD -> mybranch) My branch
* 2182bb2 (master) Initial commit

我们现在可以看看git如何在幕后实现这一点。

$ ls -R .git/refs
.git/refs:
heads  tags

.git/refs/heads:
master  mybranch

.git/refs/tags:

好奇有些文件与我们的分支同名。 在里面看,我们看到了

$ cat .git/refs/heads/master .git/refs/heads/mybranch
2182bb2d5a0a7f57d0b74e95d37e208dac41f95b
2182bb2d5a0a7f57d0b74e95d37e208dac41f95b

因此git将refs实现为名称匹配的特定位置的文件 分支名称和包含SHA1 某些提交的哈希值。注意git lola2182bb2)输出中的缩写哈希是上面cat输出的前导。

将git refs想象成简单的指针,为存储库历史记录中的特定提交提供人类可读的名称。

现在,如果我们切换回master并点击mybranch

$ git checkout master ; git branch -D mybranch
Switched to branch 'master'
Deleted branch mybranch (was 7143aa4).

我们看到裁判消失了

$ ls -R .git/refs
.git/refs:
heads  tags

.git/refs/heads:
master

.git/refs/tags:

但提交仍在那里。

$ git show --pretty=oneline 7143aa4
7143aa477735382e7a0ed11c9e4b66c1f27583df My branch
diff --git a/mybranch b/mybranch
new file mode 100644
index 0000000..e69de29

如果您想要mybranch,则只需运行

$ git checkout -b mybranch 7143aa4
Switched to a new branch 'mybranch'

$ git branch mybranch 7143aa4

取决于,各自的输出差异表明, 是否要切换到分支。在后一种情况下, 你留在当前分支的地方,git lola看起来像

$ git lola
* 7143aa4 (mybranch) My branch
* 2182bb2 (HEAD -> master) Initial commit

是的,即使您删除了使它们保持活动状态的指针,您的提交也会在中短暂停留。这在意外删除的情况下非常有用。另请参阅git refloggit gc

请注意,存储库中的SHA1哈希值会有所不同 因为你的姓名和电子邮件地址至少会有所不同 从我使用的。

为了完整起见,-d-D之间的区别是小写版本稍微安全一些。

  

-d
  --delete

     

删除分支。分支必须在其上游分支中完全合并,或者如果没有使用HEAD--track设置上游,则必须在--set-upstream中合并。

     

-D

     

--delete --force的快捷方式。

     

-f
  --force

     

如果 branchname 已存在,请将 branchname 重置为 startpoint 。没有-f git branch拒绝更改现有分支。结合-d(或--delete),允许删除分支,无论其合并状态如何......

答案 1 :(得分:1)

删除分支后,提交仍然存在,直到下一次垃圾回收。 git branch -d(和-D)打印缩写的提交哈希,您可以将其用作git loggit checkoutgit branch的参数,这样就可以恢复已删除的分支。

-d-D之间的唯一区别是git branch -d不会让您删除未合并的分支(即可能会丢失提交)。