如何强制执行 "git pull" 并覆盖本地文件?

9510

如何在执行 git pull 命令时强制覆盖本地文件?我的本地仓库中包含与服务器上相同文件名的文件。

错误:无法完成合并,未跟踪的工作树文件 'example.txt' 将被覆盖。


48
如果您正在阅读此内容并认为自己可能会丢失文件,我曾经遇到这种情况,并发现Sublime Text的缓冲区保存了我的工作 - 如果我在某个项目上工作时,意外删除了所有内容,尝试解决类似于这个问题或使用此问题的答案,并且已经在Sublime中打开这些文件(有很大的可能性),那么文件仍将存在于Sublime中,可能是在其中,也可能在撤销历史记录中。 - Toni Leigh
303
重置当前分支到与远程分支origin/branch_to_overwrite相同的状态,包括代码和提交记录。命令为:git reset --hard origin/branch_to_overwrite - Andrew Atkinson
5
基本上,在初始的checkout -b之后,只需从develop分支进行一次pull操作,然后进行你的工作,完成后再将更改push回去。 - ldgorman
5
简短回答:删除并重新创建分支。
  1. 删除分支: git branch <branch> -D
  2. 重置到冲突之前的提交: git reset <commit> --hard
  3. 重新创建分支: git branch <branch>
  4. 设置跟踪服务器: git --set-upstream-to=origin/<branch> <branch>
  5. 拉取:git pull
- Nino Filiu
6
将所有的CRLF行尾符改为LF(以干净的方式开始),请执行以下操作:git config core.autocrlf false; git ls-files -z | xargs -0 rm; git checkout . - Chloe
显示剩余6条评论
53个回答

69
所有这些解决方案的问题在于它们要么过于复杂,要么更大的问题是它们会从Web服务器中删除所有未跟踪的文件,而我们不希望这样做,因为服务器上始终需要一些配置文件,而这些文件不在Git存储库中。

以下是我们正在使用的最干净的解决方案:

# Fetch the newest code
git fetch

# Delete all files which are being added, so there
# are no conflicts with untracked files
for file in `git diff HEAD..origin/master --name-status | awk '/^A/ {print $2}'`
do
    rm -f -- "$file"
done

# Checkout all files which were locally modified
for file in `git diff --name-status | awk '/^[CDMRTUX]/ {print $2}'`
do
    git checkout -- "$file"
done

# Finally pull all the changes
# (you could merge as well e.g. 'merge origin/master')
git pull
  • 第一条命令获取最新的数据。

  • 第二条命令检查是否有任何文件被添加到仓库,并从本地仓库中删除这些未跟踪的文件,这将引起冲突。

  • 第三条命令检出所有在本地修改过的文件。

  • 最后,我们执行拉取操作以更新到最新版本,但这次没有冲突,因为不再存在在repo中的未跟踪文件,而且所有在本地修改过的文件已经与仓库中的文件相同了。


使用“git merge origin/master”作为最后一行(就像您在注释中所说的那样),而不是“git pull”,这将更快,因为您已经从git存储库中拉取了任何更改。 - Josh
2
当然,git merge origin/master 会更快,而且可能更安全。因为如果在删除此脚本文件期间有人推送了新更改(虽然不太可能,但仍有可能),整个拉取过程可能会失败。我之所以加入 pull 是因为有些人可能不是在主分支上工作,而是在其他分支上,我希望这个脚本是通用的。 - Strahinja Kustudic
如果您有本地创建的文件,例如选项文件,请将它们放在.gitignore中。 - Sebi

66

首先,尝试标准方法:

git reset HEAD --hard # To remove all not committed changes!
git clean -fd         # To remove all untracked (non-git) files and folders!

警告:如果您没有提交数据/文件,则上述命令可能会导致数据/文件丢失!如果您不确定,请首先备份整个存储库文件夹。

然后再次拉取它。

如果上述方法无效,且您不关心未跟踪的文件/目录(为了安全起见,请先备份),请尝试以下简单步骤:

cd your_git_repo  # where 'your_git_repo' is your git repository folder
rm -rfv *         # WARNING: only run inside your git repository!
git pull          # pull the sources again

这将删除所有 git 文件(除了你拥有所有提交的 .git/ 目录)并重新拉取。


为什么 git reset HEAD --hard 在某些情况下会失败?

  1. .gitattributes 文件中自定义规则

    在 .gitattributes 文件中添加 eol=lf 规则可能导致 git 修改一些文件更改,将某些文本文件中的 CRLF 换行符转换为 LF。

    如果是这种情况,您需要通过在 git status 中查看它们来提交这些 CRLF/LF 更改,或尝试使用 git config core.autocrlf false 临时忽略它们。

  2. 文件系统不兼容

    当您使用不支持权限属性的文件系统时。 例如,在 Linux/Mac (ext3/hfs+) 上有一个存储库,在基于 FAT32/NTFS 的文件系统上有另一个存储库。

    正如您所注意到的,有两种不同类型的文件系统,因此不支持 Unix 权限的文件系统基本上无法在不支持该类型权限的系统上重置文件权限,因此无论您如何尝试 --hard,git 都会始终检测到一些“更改”。


63

额外奖励:

在之前的回答中提到了拉取/获取/合并,我想分享一个有趣且高效的技巧:

git pull --rebase

这是我在 Git 生涯中最有用的命令,它节省了很多时间。

在将新提交推送到服务器之前,请尝试此命令,它会自动将最新的服务器更改(使用fetch + merge)同步,并将您的提交置于 Git 日志的顶部。 不需要担心手动拉动/合并。

What does "git pull --rebase" do? 中找到详细信息。


8
简而言之:git pull -r - kenorb
在我的情况下,在执行此操作之前,我必须先进行以下步骤:1)git add -A,2)git commit -m,3)最后是git pull rebase。谢谢。 - Pathros
这会删除我已提交的更改。我做错了什么? - Nikhil S

60

我曾遇到同样的问题,但没人给我这个解决方案,但它对我有效。

我是这样解决的:

  1. 删除所有文件,只留下.git目录。
  2. git reset --hard HEAD
  3. git pull
  4. git push

现在它可以工作了。


2
同感。有时候只有最困难的解决方案才能奏效,经常会发生只有重置和清理不够的情况... - jdehaan

52

如果您不想始终粘贴分支名称或者想在脚本中自动执行此操作,这是一种通用解决方案

git fetch
git reset --keep origin/$(git rev-parse --abbrev-ref HEAD)

如果您也想重置本地更改:

git fetch
git reset --hard origin/$(git rev-parse --abbrev-ref HEAD)

您还可以使用以下命令添加bash别名:

alias gplf='git fetch && echo "HEAD was at $(git rev-parse --short HEAD)" && git reset --hard origin/$(git rev-parse --abbrev-ref HEAD)'

4
如果您发现自己经常使用此命令,请添加一个 Bash 快捷方式alias gplf='git fetch && echo "HEAD was at $(git rev-parse --short HEAD)" && git reset --hard origin/$(git rev-parse --abbrev-ref HEAD)' - Paul Odeon
2
太棒了。谢谢!人们在回答问题时通常不考虑自动化脚本。当你无法传递分支名称时,这非常优雅。 - Zeno Popovici
3
这个中间的方法对我很有效:先运行 git fetch,然后再运行 git reset --hard origin/$(git rev-parse --abbrev-ref HEAD)。我选择它是因为它简单易懂,并且答案最近更新过。 - Nike

40

我遇到了类似的问题。我必须这样做:

git reset --hard HEAD
git clean -f
git pull

9
请谨慎使用 git clean 命令。 - nategood

36

我总结了其他答案。您可以执行git pull而不出现错误:

git fetch --all
git reset --hard origin/master
git reset --hard HEAD
git clean -f -d
git pull

警告:此脚本非常强大,因此您可能会失去所做的更改。


3
这将覆盖已修改的文件(先前签入的文件),并删除未跟踪的文件(从未签入的文件)。正是我在寻找的,谢谢! - styfle
4
我怀疑第三行的git reset --hard HEAD可能是多余的;我的本地手册(2.6.3版)显示,第二行中的 git reset --hard origin/master 在所有形式上都“默认为 HEAD”。 - arichards
3
@arichards 我认为你的怀疑是正确的,但如果第二行由于任何原因无法工作,第三行将很好地进行重置。这个解决方案不需要被优化。我只是总结了其他答案。就这些了。谢谢你的评论。 :) - Robert Moon
谢谢您的总结。这些步骤确实非常有力 :) - Glorian

30
根据我自己的类似经验,Strahinja Kustudic提供的解决方案是目前最好的。正如其他人指出的那样,简单地进行硬重置会删除所有未被跟踪的文件,其中可能包括您不想删除的许多东西,例如配置文件。更安全的做法是仅删除即将添加的文件,而且您可能还想检出任何即将更新的本地修改文件。
考虑到这一点,我更新了Kustudic的脚本来实现这一点。我还修复了一个打字错误(原始版本中缺少一个')。
#/bin/sh

# Fetch the newest code
git fetch

# Delete all files which are being added,
# so there are no conflicts with untracked files
for file in `git diff HEAD..origin/master --name-status | awk '/^A/ {print $2}'`
do
    echo "Deleting untracked file $file..."
    rm -vf "$file"
done

# Checkout all files which have been locally modified
for file in `git diff HEAD..origin/master --name-status | awk '/^M/ {print $2}'`
do
    echo "Checking out modified file $file..."
    git checkout $file
done

# Finally merge all the changes (you could use merge here as well)
git pull

使用“git merge origin/master”作为最后一行(就像您在注释中所说的那样),而不是“git pull”,这将更快,因为您已经从git存储库中拉取了任何更改。 - Josh
需要检出修改过的文件,这样就可以百分之百地工作。我很久以前就用这个脚本更新了,但是忘记在这里也更新了。我使用的方式也有点不同于你。我检出任何类型的修改过的文件,而不仅仅是 M,所以它始终有效。 - Strahinja Kustudic

26

这里似乎大部分答案都集中在master分支,但是有时我会在两个不同的地方工作于同一个特性分支,并且我希望在其中一个分支上进行的变基被反映到另一个分支中,而不需要太多复杂的操作。

结合RNA的回答torek对类似问题的回答,我想到了以下方法,它运行得非常出色:

git fetch
git reset --hard @{u}

如果你从一个分支运行它,它只会将你的本地分支重置为上游版本。

这也可以很好地放入到 git 别名 (git forcepull) 中:

git config alias.forcepull "!git fetch ; git reset --hard @{u}"

或者,在你的.gitconfig文件中:

[alias]
  forcepull = "!git fetch ; git reset --hard @{u}"

享受!


这个答案也很好,因为它可以在你在哪个分支上都能工作! - leafmeal

25

我不确定为什么还没有人谈论FETCH_HEAD

git fetch origin master && git reset --hard FETCH_HEAD
如果您想将其放在别名中,命令将是:
git config --global alias.fpull '!git fetch origin master && git reset --hard FETCH_HEAD'

因为SO不相信有人会进行一个字符的编辑(?!?!):&与&&不同! - Connor Clark
2
@ConnorClark 我用 && 替换了 & - Ahmad Ismail
2
只有这个有效。 - Tai Le

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