ChatGPT解决这个技术问题 Extra ChatGPT

将分支指针移动到不同的提交而不签出

要移动已签出分支的分支指针,可以使用 git reset --hard 命令。但是如何移动未签出分支的分支指针以指向不同的提交(保留所有其他内容,如跟踪的远程分支)?

听起来您想要做的只是从与现在创建的提交不同的提交中创建一个分支。如果我的理解是正确的,那么您为什么不简单地从要使用 git branch <branch-name> <SHA-1-of-the-commit> 创建它的提交创建一个新分支并转储旧分支?
@yasouser - 我不确定倾倒“master”分支是个好主意。

N
Nathan
git branch --force <branch-name> [<new-tip-commit>]

如果省略 new-tip-commit,则默认为当前提交。

new-tip-commit 可以是分支名称(例如,master、origin/master)。


或者对于任意参考,git update-ref -m "reset: Reset <branch> to <new commit>" <branch> <commit>。 (如果您愿意,您可以挑选有关 reflog 消息的细节 - 我相信 branch -freset --hard 不同,这两者都不完全一样。)
Jefromi,请写一个单独的答案,以便您获得选票。 :)
这是一个更好的答案,因为它处理了 99% 的情况并且实际上符合文档。 git help branch 说“-f, --force 重置 <branchname> 到 <startpoint> 如果 <branchname> 已经存在。没有 -f git branch 拒绝更改现有分支。”
我正在执行 git branch -f master <hash>,它告诉我 fatal: Cannot force update the current branch. 嗯,我现在必须做什么,在允许我使用此命令之前检查其他一些随机分支?
如果您尝试移动的分支是您当前的分支(HEAD 指向它),这将不起作用。
1
13 revs, 11 users 21%

您可以为任意参考做到这一点。这是移动分支指针的方法:

git update-ref -m "reset: Reset <branch> to <new commit>" refs/heads/<branch> <commit>

其中 -m 将消息添加到分支的 reflog。

一般形式是

git update-ref -m "reset: Reset <branch> to <new commit>" <ref> <commit>

如果您愿意,您可以挑选有关 reflog 消息的细节 - 我相信 branch -freset --hard 不同,这两者都不完全一样。


消息的好处在哪里?它存储在哪里以及以后如何读取它?
注意:这不适用于裸存储库。在裸存储库上,您必须使用 'git branch -f master ' 来更新分支(请参阅下面的答案)。
如果像我一样,你不小心使用了 而不是 refs/heads/,你最终会在你的 .git 目录中的 .git/ 中得到一个新文件,你会收到类似这样的消息当您尝试使用它时,“refname 'master' 不明确”。您可以从 .git 目录中删除该文件以进行修复。
尚未令人满意地解释为什么这比 git branch -f 更好。具体来说,这种方法似乎是:(A)更难使用(B)更难记住,以及(C)更危险
“任意引用的确切含义” - 分支不是唯一指向提交的引用。有标签,您也可以自己创建既不是分支也不是标签的任意 refs/whatevs/myref 样式的 ref。我相信这也回答了 Steven Lu 关于这可能是“更好”的问题。如果您使用分支,我同意 branch -f 是最简单的。
A
Amiel Martin

您还可以传递 git reset --hard 提交参考。

例如:

git checkout branch-name
git reset --hard new-tip-commit

我发现我经常做这样的事情:

假设这段历史

$ git log --decorate --oneline --graph
* 3daed46 (HEAD, master) New thing I shouldn't have committed to master
* a0d9687 This is the commit that I actually want to be master

# Backup my latest commit to a wip branch
$ git branch wip_doing_stuff

# Ditch that commit on this branch
$ git reset --hard HEAD^

# Now my changes are in a new branch
$ git log --decorate --oneline --graph
* 3daed46 (wip_doing_stuff) New thing I shouldn't have committed to master
* a0d9687 (HEAD, master) This is the commit that I actually want to be master

如果您的工作树是干净的,这很好。如果您有很多分阶段或非分阶段的更改,最好按照上面的讨论执行 git update-ref
您是否注意到您的“答案”没有添加任何不属于问题的内容? – OP说:如果是check out...可以用git reset --hard ...这里不用重复了! :-(
@罗伯特:我不同意。问题没有说明如何使用它,但确实如此。不用去找那个方法真是太好了。
@WilsonF,也许你很高兴在这里找到这个,但它根本没有回答这个问题。也许这是其他问题的答案,但这里是错误的。
我认为这可能应该是对问题的评论,而不是答案。 (或者它可能是在回答问题的早期版本?)
M
Matheus Felipe

只是为了丰富讨论,如果您想将 myBranch 分支移动到您的 current 提交,只需省略 -f 之后的第二个参数

例子:

git branch -f myBranch

我通常在 rebase 处于 Detached HEAD 状态时这样做:)


P
Peter Cordes

gitk --all 中:

右键单击您想要的提交

-> 创建新分支

输入现有分支的名称

在确认替换该名称的旧分支的对话框上按回车键。

请注意,重新创建而不是修改现有分支将丢失跟踪分支信息。 (对于只有一个遥控器并且您的本地分支与遥控器中的相应分支具有相同名称的简单用例,这通常不是问题。有关更多详细信息,请参阅评论,感谢@mbdevpl 指出这个缺点。)

如果 gitk 具有对话框具有 3 个选项的功能,那就太棒了:覆盖、修改现有或取消。

即使您通常像我一样是个命令行迷,git guigitk 也非常适合它们允许的 git 使用子集。我强烈建议将它们用于他们擅长的事情(即有选择地在 git gui 中将大块放入/退出索引,并且也只是提交。(ctrl-s 添加签名:行,ctrl-enter 提交.)

gitk 非常适合在您将更改整理成漂亮的补丁系列以提交上游时跟踪几个分支,或者在您需要跟踪多个分支中间的任何其他内容时跟踪。

我什至没有打开图形文件浏览器,但我喜欢 gitk/git gui。


太简单!我可能刚刚从 gitg 转换为 gitk。
但是,这种方式会丢失跟踪分支信息。
@mbdevpl:我不是真正的 git 专家。我想我明白你的意思,但不明白其中的含义。我经常使用它,并且仍然能够将这些分支推送到遥控器上的同名分支。分支与其远程跟踪分支之间的关联对您有什么作用?
@PeterCordes Ineed,当分支名称不匹配时,这很重要。此外,当有多个遥控器时。此外,当您使用 git prompt 显示分支状态时,它会显示到您的跟踪分支的提交距离(如果已设置)。此外,git status 输出也会受到影响。此外,在某些情况下,如果您不设置跟踪分支,如果不明确指定远程,git fetchgit push 将无法工作。我不了解所有情况,但对我来说,一般的经验法则是,为了方便和工作速度,最好按顺序设置跟踪分支。
C
Community

TortoiseGit 中的 recommended solution git branch -f branch-pointer-to-move new-pointer

“Git 显示日志”

检查“所有分支”

在您希望分支指针移动到的行上(new-pointer):右键单击,“在此版本创建分支”在“分支”旁边,输入要移动的分支的名称(branch-pointer-to-move)下“Base On”,检查新指针是否正确检查“Force”确定

右键单击“在此版本创建分支”

在“分支”旁边,输入要移动的分支的名称(branch-pointer-to-move)

在“Base On”下,检查新指针是否正确

勾选“强制”

好的

https://i.stack.imgur.com/jMZWU.png

https://i.stack.imgur.com/WZhnB.png


A
Adrian

老实说,我很惊讶没有人想到 git push 命令:

git push -f . <destination>:<branch>

点 ( . ) 指的是本地存储库,您可能需要 -f 选项,因为目标可能“在其远程对应物之后”。

尽管此命令用于将更改保存在服务器中,但结果与将远程分支 (<branch>) 移动到与本地分支 (<destination>) 相同的提交完全相同


您也可以在不使用 -f 的情况下执行此操作,以避免破坏本地任何内容;例如,git fetch origin && git push . origin/develop:developgit checkout develop && git pull --ff-only 的不需要结帐的快速失败版本
a
alondono

Git 2.23.0 引入了 git-switch 命令,也可用于执行此操作。

git switch -C <branch-name> [<start-point>]

-C(大写 C)选项表示如果 <branch-name> 已经存在,它将重置为 <start-point>

使用 -c(小写 C)时,它将尝试创建一个新分支,但如果一个已经存在则失败。

<start-point> 可以是哈希、标签或其他分支名称。


我喜欢这个主意,但截至 git version 2.35.1 ... git switch --help | egrep -i EXPERIMENTALTHIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.
git switch 命令自 2.23.0 版中引入以来一直处于试验阶段。请参阅 2019 年 8 月的 Git 博客github.blog/2019-08-16-highlights-from-git-2-23
我想说一个新命令首先作为实验性引入是正常的。它只是意味着某些选项或标志可能会更改,或者可能会添加或改进其他选项。自 2019 年 8 月引入该命令以来,此处讨论的 -c|-C 标志行为没有改变。
M
Mansoor

如果要将未签出的分支移动到另一个提交,最简单的方法是运行带有 -f 选项的 git branch 命令,该选项确定分支 HEAD 应指向的位置:

git branch -f <branch-name> (<sha1-commit-hash> or <branch-name>)

例如,如果您希望本地开发分支跟踪远程(源)开发分支:

git branch -f develop origin/develop

请注意,如果您尝试移动的分支是您当前的分支,这将不起作用。要移动分支指针,请运行以下命令: git update-ref -m "reset: Reset to " refs/heads/

git update-ref 命令安全地更新存储在 ref 中的对象名称。

希望我的回答对您有所帮助。信息来源是this snippet


G
Guillermo Gutiérrez

打开文件 .git/refs/heads/<your_branch_name>,并将其中存储的散列更改为您要移动分支头部的散列。只需使用任何文本编辑器编辑并保存文件。只要确保要修改的分支不是当前活动的分支。

免责声明:可能不是一个可取的方法,但可以完成工作。


不确定这是混乱还是邪恶的做法。 🤔😉
@KeithRussell 可能两者兼有:P
@KeithRussell,@G 只是混乱,除非你在搞乱公共回购! :-) 从某种意义上说,这是一种受支持的方式,因为 git 文档非常详细地描述了它的内部工作原理。能够了解哪些文件是如何更新的,例如 git commit 是使用 git 而不是 mercurial 的最令人信服的理由。
J
Jean Paul

对于签出的分支,如果您要指向的提交在当前分支之前(除非您想撤消当前分支的最后一次提交,否则应该是这种情况),您可以简单地执行以下操作:

git merge --ff-only <commit>

这使得 git reset --hard 的替代方案更加柔和,如果您不是上述情况,则会失败。

要对未签出的分支执行相同的操作,等价于:

git push . <commit>:<branch>

问题询问如果分支未签出该怎么办。
糟糕,我错过了这一点。在这种情况下,您可以按照已经建议的方式执行 git push . <commit>:<branch>
这并不能回答问题,因为合并会创建新的提交(以及新的事态)。它不会简单地更新分支指针,除非您执行 git merge --ff-only,并且只有在没有冲突的情况下。
@jpaugh这就是为什么我说在您要指向的提交在前面的情况下(我也可以精确而不是落后)。是的,如果您想确定,您可以使用 --ff-only 选项,但该选项不会改变合并的完成方式,它只会阻止非快进合并的完成。此外,如果合并不是快进,您可以拒绝提交并使用 git merge --abort 取消合并。
@JeanPaul 您显然是 git 专家,但您的许多读者都不会。新手不仅需要知道要使用哪个命令,还需要知道在什么上下文中使用。您的最后一条评论对此有很大帮助。但是用 git merge 回答 OP 的问题需要更多的命令来保护起始分支和工作树。 git stash; git checkout <branch>; git merge --ff-only <commit>; git checkout <starting-branch>; git pop