git - 在 Git 中删除分支时究竟发生了什么

标签 git branch git-branch

我一直在寻找这个问题的答案,但我仍然不完全确定它的答案。 我发现的大多数关于删除分支的信息只不过是手册内容的副本(例如 here on SO )。 我认为一个核心问题是我不知道 Git 中的分支到底是什么(尽管有很多文章声称可以对此进行解释)。 我发现的唯一有用的信息是 this SO answer以及使用 git 安装的文档。

我的问题:

当我运行 git branch -d BRANCH_NAME 时,

  1. 幕后发生了什么?
  2. 外部发生了什么变化,即我与存储库的交互如何变化?
  3. 这些变化中的任何一个都可以被视为历史上的变化吗?

另外还有 git branch -D BRANCH_NAME 的相同子问题。

我目前的理解:

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

对于 git branch -d BRANCH_NAME,我认为发生了什么(但我很不确定):

  1. 指向分支头的指针被移除,不再有
  2. 我再也看不到任何列表中的分支,也不能再切换到它或从它分支(尽管我想我可以在该提交时创建一个新分支,以有效地实现这些目标)
  3. 可能不是:提交仍然存在,只是不再标记分支名称?

对于 git branch -D BRANCH_NAME,我认为会发生什么:

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

最佳答案

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

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

$ 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
7143aa477735382e7a0ed11c9e4b66c1f27583df

所以 git 将 refs 实现为某个位置的名称匹配的文件 包含 SHA1 的分支名称 某些提交的哈希值。观察 git lola (2182bb2) 输出中的缩写哈希是上面 cat 输出的前导前缀。

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

现在,如果我们切换回 master 和 zap mybranch

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

我们看到ref不见了

$ 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

Delete a branch. The branch must be fully merged in its upstream branch, or in HEAD if no upstream was set with --track or --set-upstream.

-D

Shortcut for --delete --force.

-f
--force

Reset branchname to startpoint if branchname exists already. Without -f git branch refuses to change an existing branch. In combination with -d (or --delete), allow deleting the branch irrespective of its merged status …

关于git - 在 Git 中删除分支时究竟发生了什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34881412/

相关文章:

git - 如何将 git 分支变成 fork?

git - 如何在不使用 git-stash 的情况下保存正在进行的工作?

git - 当我将我的项目发布到 GitHub 时,我应该如何处理 Grunt 的 node_modules 目录?

php - Forge + Git + Laravel 问题

mercurial - 如何比较 2 个 Mercurial 分支之间的变更集集?

git - 如何将不同的分支推送到不同的 heroku 应用程序?

ios - 在 GIT 上提交 Crashlytics 框架

git - 如何使用 powershell 和批处理文件从实时网站自动每晚推送到 GitHub 分支

svn - 颠覆 : deleting old feature branches vs. 保留它们

eclipse - svn:将更改从分支合并到工作副本而不更新主干