使用Git Pull Force覆盖本地文件

5
"Git Pull Force"指的是强制拉取代码,"git reset branch to origin"或者说是将本地分支回退到远程分支,即拉取一个远程分支来覆盖本地分支。尽管在一些地区使用减少,但这似乎是一项备受关注的功能,并且随着团队和开发人员数量的增长而变得越来越重要。这完全是有道理的。

enter image description here

当前,最短的工作解决方案相当冗长,并需要了解分支知识。
git reset --hard origin/<branch_name>

编辑:有一个更方便的变体git reset --hard @{u}
请给予评论提供的信息。更多快捷方式在这里

这很不幸,因为打以下命令会更快:

git pull

这带来了自身的挑战。分歧的历史、合并冲突等等...


我们确实有像这样的简写方式

git push origin HEAD -u --force

该命令将一个本地分支 <branch_name> 推送到远程仓库,并覆盖同名的远程分支,同时将其设置为本地分支的上游分支。

然而,在git pull中没有类似--force/reset的选项可用。


有什么最好的方法可以将此功能添加到git中?


如何强制执行“git pull”以覆盖本地文件? 660万次查看
将本地存储库分支重置为与远程存储库 HEAD 相同的状态 470万次查看
如何强制 git pull 在每次拉取时覆盖所有内容? 37万次查看
从 Git 远程存储库拉取时使用远程更改解决冲突 24万次查看
如何在执行 git pull 时强制更新? 9万次查看
强制 GIT 拉取而不提交
使用 git 强制拉取
git 强制使用隐式 rebase 拉取
清理分叉,并从上游开始重启它
强制 git 在拉取时更新我的本地 repo
将本地存储库的所有分支重置为与远程相同
Github - 放弃所有更改


起个别名怎么样?你可以让 git p 表示你所描述的意思。这相当简短明了。 - matt
2
git reset --hard @{u} 不需要分支名称。 - Ry-
请注意,“pull”的含义是:(1)运行git fetch;(2)运行第二个Git命令来合并工作。没有“强制合并”,但有“强制使用”。但由于“pull”意味着合并,所以在使用“force”时,您必须停止使用pull。请进行单独的fetch,然后强制执行。 - torek
@Ry- TIL 关于 @{u} https://mirrors.edge.kernel.org/pub/software/scm/git/docs/gitrevisions.html - Qwerty
3个回答

6
我想强调的是,虽然您提到了每天都会出现的需求,但是那些在没有警告的情况下强制删除您的工作的git命令也是问题的来源,它们会带来更严重的后果(搜索“I lost my work after git reset -- hard / git checkout . ,我能找回来吗?”的相关问题)。
通过您的pull -f示例,当您与远程交互时,您不知道您将从远程获取什么,这将加剧问题。
基于我的经验,我强烈建议养成不使用git pull而仅使用git fetch的习惯。
然后使用origin/branchname检查差异,并选择是否要重置或重新提交等等。
一次性地说“移动到该提交并丢弃所有更改”将是不错的,我在@VonC's answer的评论中建议过这个。
#!/bin/bash

target=$1
if [ -z "$target" ]; then
    target=HEAD
fi

set -e  # avoid going forward if one command fails ...

git stash
git restore -SW -s "$target" -- .
git reset "$target"

(我没有一个好的别名来代替这个: git goto ?)

它将为git reset提供一个相对安全的替代方案; 主要注意事项是:磁盘上的文件在起始提交中未被跟踪,但在$target中被跟踪的文件将被覆盖而不会保存。

您需要一个更加复杂的git stash变体来保存这些文件。


[更新] 我认为我找到了一个可以保存受影响文件(仅限受影响的文件)的脚本:

#!/bin/bash

target=$1
if [ -z "$target" ]; then
    target=HEAD
fi

set -e  # avoid going forward if one command fails ...

list_impacted_files () {
   local target=$1

   # tracked files in the working tree that have a diff
   git diff --no-renames --name-only HEAD

   # untracked files that will be clobbered when restoring $target :
   #   * files that are present in $target but not in HEAD
   #   * and that currently exist on disk (use 'ls' to keep only those)
   # add '|| true' to ignore error code returned by 'ls' on non existing files
   git diff --no-renames --name-only --diff-filter=A HEAD "$target" |\
     xargs -r ls 2> /dev/null || true 
}

# list files as described above, and feed this list to `git stash -u`
# 'xargs -r <cmd>' avoids running <cmd> at all if stdin is empty
# in our case: don't run 'git stash -u' if no files are to be stashed ...
list_impacted_files "$target" | xargs -r git stash -u --

git restore -SW -s "$target" -- .
git reset "$target"

要将存储库恢复回原状态:

  • 回滚到初始提交(使用 git reset 或上面的脚本...)
  • git stash list 中找到要恢复的 stash@{xx}
  • 运行 git stash apply --index stash@{xx}

1
我通常不会执行这样破坏性的操作,但是我所考虑的使用场景是团队设置中每天都要进行的“实时代码审查”。通常情况下:我提交审查并告知作者。然后作者立即推送一个热修复,甚至修改/变基/合并并要求我检查它,而我可能仍在继续测试旧分支,该分支甚至可能包含小的本地编辑。然后我想尽快丢弃我所做的任何内容,并尽快在本地获取他们的分支。git pull将给我带来冲突和头疼。 - Qwerty
1
@Qwerty:好的,你可以看一下 git worktree add,它允许你在磁盘上拥有一个单独的仓库检出。 - LeGEC

5

你至少需要三条命令:

  • 一个 fetch 命令,用于更新远程跟踪分支
  • 一个 reset 命令,使用 @{u}@{upstream} 后缀 来重置上游分支
  • 一个 clean 命令,确保不留下任何额外的文件

即:

git fetch
git reset --hard @{u}
git clean -nd

git clean中的-nd选项替换为-fd,以实际删除文件:在实际删除任何内容之前,我总是更喜欢预览一下git clean的操作。您需要将它们分组为别名或脚本(git-pullreset),然后可以调用。


LeGEC评论中建议:
git stash
git restore -SW -s @{u} -- .
git reset @{u}

那会:
  • 完全未被追踪的文件(例如:不在HEAD或@{u}中追踪的文件)将保持不变,并且
  • 仍然有一种方式可以回退到“之前的状态”。

我建议使用以下命令:git stash; git restore -SW -s @{u}; git reset @{u},这将完全保留未被跟踪的文件(例如:在 HEAD@{u} 中未被跟踪的文件),并且可以提供一种还原到“之前状态”的方法。 - LeGEC
1
@LeGEC 很好的观点,谢谢你的建议。我已经将你的评论包含在答案中以增加可见性。 - VonC
1
我在我的建议中发现了一个警告:git stash单独不能保存HEAD中未跟踪的文件,而git restore会覆盖它们。作为一项练习,我正在尝试编写命令来查找并保存这些文件,但我发现这非常困难。 - LeGEC
@LeGEC 那么... git stash -u - VonC
会以某种方式工作,问题在于所有未跟踪的文件都将被隐藏起来,而且已知恢复stash -u时出现一些文件重新出现在磁盘上是很麻烦的。我试图列出要隐藏的文件,并运行git stash -u -- $(thelist) - LeGEC

4

如何在没有本地分支副本的情况下工作,只有你自己的分支

我要采取不同的方法,挑战问题的前提。如果你发现自己不得不调整本地分支以匹配远程分支,这表明你本来就不应该有一个本地分支

相反,以这样的方式工作,你所使用的分支就是远程跟踪分支,因此无需进行任何调整。

例如,我一整天都在敏捷地制作一些小特性分支并将它们推送形成拉取请求以合并到main中。但我自己没有main。我需要它做什么?没有必要!

  • 要开始一个功能分支,我会先fetch,然后从origin/main创建该分支。
  • 为了研究远程的main状态,我会先fetch,然后以分离状态检出origin/main
  • 为了审查拉取请求,我会先fetch,然后以分离状态检出origin/yourbranch。如果在审查期间作者向拉取请求分支推送更多提交,我会再次fetch,然后以分离状态检出origin/yourbranch

因此,我拥有的唯一本地分支就是我的特性分支,它们没有上游,不能被拉取(也不需要被拉取,因为它们的进一步提交只会来自于我)。

简而言之,我建议如果人们真的想知道如何“拉取远程分支以覆盖本地分支”作为他们日常工作流程的一部分,这仅仅表明他们一开始就使用Git方式错误。除非:

  • 你要向其中添加提交并推送,或
  • 你要在本地合并,这在今天的拉取请求世界中并不常见,或
  • 它完全是本地和实验性的,也就是说,在开发功能时,你只是尝试一种可能的方法。

在这些情况下,都不会涉及到相应的远程跟踪分支,因此在问题中提出的情况永远不会出现。


该死的,代码审查期间进行分离检出可能正是我正在寻找的!问题在于,当我提交代码审查时,作者通常会立即推送修复程序,甚至修改/重置/合并,并要求我再次检查,而我可能仍然继续测试旧分支,这甚至可能包括小的本地编辑。然后,我想通过放弃我所做的任何事情尽快在本地获取他们的状态。此时使用 git pull 将给我带来冲突和头痛。我可能会设置一个执行获取和分离检出的宏。谢谢! - Qwerty

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接