gpt4 book ai didi

git - 如果git rebase被rebase分支删除,为什么git rebase删除最近提交的文件?

转载 作者:太空狗 更新时间:2023-10-29 13:49:28 27 4
gpt4 key购买 nike

我正试图弄清楚为什么git rebase会导致一个新创建的文件被删除,如果我正在删除它的分支。例如:

A1 - A2 - A3
\
B1

A2 = add a new file test.txt
A3 = delete test.txt
B1 = add the exact same file as A2

如果b1被签出并且我执行 git rebase A3,test.txt仍然被删除。我希望结果是:
A1 - A2 - A3 - B1

这就意味着仍然存在。为什么在rebase之后删除test.txt?

最佳答案

哇,这是一个艰难的一次!:-)
使用你的脚本,我重现了这个问题。不过,这一切有点奇怪,所以首先,我删掉了rebase步骤,留下了这个(稍微修改过的)脚本:

#!/bin/sh
set -e
if [ -d testing_git ]; then
echo test dir testing_git already exists - halting
exit 1
fi

mkdir testing_git
cd testing_git

git init
touch main.txt
git add .
git commit -m "initial commit"

# setup B branch
git checkout -b B
echo hello > test.txt
git add .
git commit -m "added test.txt"

# setup master
git checkout master
echo hello > test.txt
git add .
git commit -m "added test.txt"
rm test.txt
git add .
git commit -m "remove test.txt"

一旦运行,检查提交,我就会得到:
$ git log --graph --decorate | sed 's/@/ /'
* commit 249e4893ea7458f45fe5cdc496ddc0292a3f03ef (HEAD -> master)
| Author: Chris Torek <chris.torek gmail.com>
| Date: Thu May 5 20:28:02 2016 -0700
|
| remove test.txt
|
* commit a132dc9e3939b5338f7c784c58da9c83f4902c8d (B)
| Author: Chris Torek <chris.torek gmail.com>
| Date: Thu May 5 20:28:02 2016 -0700
|
| added test.txt
|
* commit 81c4d9be82094fdb4c88ed0a53bdbd5c3dfd7a5a
Author: Chris Torek <chris.torek gmail.com>
Date: Thu May 5 20:28:02 2016 -0700

initial commit

注意, master的父提交是branch B的提交,只有三个提交,而不是四个。当脚本运行四个 git commit命令时,这怎么可能?
现在让我们将 sleep 2添加到脚本中,在 git checkout master之后,重新运行它,看看会发生什么…
[edit]
$ sh testrebase.sh
[snip output]
$ cd testing_git && git log --oneline --decorate --graph --all
* cddbff1 (HEAD -> master) remove test.txt
* c4ac1b2 added test.txt
| * fefc150 (B) added test.txt
|/
* 8c07bb6 initial commit

哇,现在我们有四个承诺,一个适当的分支!
为什么第一个脚本要进行三次提交,而添加 sleep 2会将其更改为进行四次提交?
答案在于犯罪的身份。每个提交都有一个(应该是!)唯一ID,它是提交内容的校验和。以下是首次在 B-branch commit中出现的内容:
$ git cat-file -p B | sed 's/@/ /'
tree c3cd0188a6a1490204e25547986e49b0b445dec8
parent 81c4d9be82094fdb4c88ed0a53bdbd5c3dfd7a5a
author Chris Torek <chris.torek gmail.com> 1462505282 -0700
committer Chris Torek <chris.torek gmail.com> 1462505282 -0700

added test.txt

我们有 treeparent、两个(姓名、电子邮件、时间戳)三元组(作者和提交者)、一个空行和日志消息。父级是主分支上的第一个提交,树是我们添加 test.txt时生成的树(及其内容)。
然后,当我们对 master分支进行第二次提交时,git从新文件创建了一个新树。这棵树与我们刚才在branch B上提交的树一模一样,所以它得到了相同的惟一id(记住,repo中只有一个树的副本,所以这是正确的行为)。然后,它创建了一个新的commit对象,其中包含我的姓名、电子邮件和时间戳,以及日志消息。但是这个提交与我们刚才在branch B上所做的提交一模一样,所以我们得到了与之前相同的id,并使branch master指向那个提交。
换句话说,我们使用了承诺。我们只是在不同的分支上完成了它(因此 master指向与 B相同的提交)。
添加 sleep 2更改了新提交的时间戳。现在这两个提交(在 Bmaster中)不再是逐位相同的:
$ git cat-file -p B | sed 's/@/ /' > bx
$ git cat-file -p master^ | sed 's/@/ /' > mx
$ diff bx mx
3,4c3,4
< author Chris Torek <chris.torek gmail.com> 1462505765 -0700
< committer Chris Torek <chris.torek gmail.com> 1462505765 -0700
---
> author Chris Torek <chris.torek gmail.com> 1462505767 -0700
> committer Chris Torek <chris.torek gmail.com> 1462505767 -0700

不同的时间戳=不同的提交=更合理的设置。
不过,实际上执行了rebase之后,还是删除了文件!
原来这是故意的。当您运行 git rebase时,设置代码并不是简单地列出每个提交以供挑选,而是使用 git rev-list --right-only来查找它应该删除的提交。1
由于添加 test.txt的提交在上游,git将其完全删除:这里的假设是您将它发送到上游的某个人,他们已经接受了它,不需要再接受它。
让我们再次修改复制器脚本,这次我们将能够取出 sleep 2,加快速度,以便对 master的更改是不同的,并且不会通过 --cherry-pick --right-only从列表中删除。我们仍将使用同一行添加 test.txt,但我们也将在该提交中修改 main.txt
# setup master
git checkout master
echo hello > test.txt
echo and also slight difference >> main.txt
git add .
git commit -m "added test.txt"

我们可以继续并打开最后的 git checkout Bgit rebase master行,这一次,重新调整工作正如我们最初预期的那样:
$ git log --oneline --decorate --graph --all
* c31b13a (HEAD -> B) added test.txt
* da2ca52 (master) remove test.txt
* 6972019 added test.txt
* 0f0d2e8 initial commit
$ ls
main.txt test.txt

我没有意识到rebase做到了这一点;这不是我所期望的(尽管正如另一个答案指出的那样,它已经被记录在案),这意味着说“rebase只是重复的cherry pick”是不太正确的:它是重复的cherry pick,有一些特殊情况是丢弃提交。
1实际上,对于非交互式rebase,它使用了这一点:
git format-patch -k --stdout --full-index --cherry-pick --right-only \
--src-prefix=a/ --dst-prefix=b/ --no-renames --no-cover-letter \
"$revisions" ${restrict_revision+^$restrict_revision} \
>"$GIT_DIR/rebased-patches"

在这种情况下, $revisions扩展到 master...B
--cherry-pick --right-onlygit format-patch选项没有文档记录;您必须知道要在 git rev-list文档中查找它们。
InteractiveRebase使用了不同的技术,但仍然会选择已经在上游的任何提交。如果您将 rebase更改为 rebase -i,则会出现这种情况,因为rebase指令由一行 noop组成,而不是预期的单个 pick行。

关于git - 如果git rebase被rebase分支删除,为什么git rebase删除最近提交的文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37063238/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com