使用 TortoiseGit 更新项目时,有时远端信息已经获取成功,文件却没有更新,最后还提示退出码 1。一个常见原因是:本地文件发生了变化,Git 为了避免覆盖这些内容,停止了合并。

本文记录一次实际处理过程,并整理出适合日常使用的 TortoiseGit 操作步骤。示例中的路径和项目名称均已泛化。

一、现象:远端显示 up to date,Pull 仍然失败

下面是脱敏后的典型报错:

git.exe pull --progress -v --no-rebase -- "origin"
From https://example.com/team/project
 = [up to date] development -> origin/development

error: Your local changes to the following files would be overwritten by merge:
    .gitignore
    firmware/sdkconfig
    src/main.c
    tools/publish/device.bin
Please commit your changes or stash them before you merge.

error: The following untracked working tree files would be removed by merge:
    tools/modules/device_info/__init__.py
    tools/modules/device_info/client.py
Please move or remove them before you merge.
Aborting

关键在两处:

提示 含义
local changes ... would be overwritten by merge 本地已有修改,更新可能覆盖它们
untracked working tree files ... would be removed by merge 存在尚未被 Git 跟踪的新文件,更新可能移除它们

这里的 [up to date] 表示本次获取没有改变相应的远端跟踪引用,不代表本地工作目录已经更新。Pull 包含获取和合并两个阶段;前一个阶段成功,后一个阶段仍可能失败。

因此,这类报错通常不需要反复登录或重新 Clone,首先应检查本地修改。

二、为什么我没有主动改代码,也会出现本地修改?

打开项目、编译固件或运行工具,也可能改变仓库里的文件。例如:

  • IDE 保存了本机工具路径、串口号等设置。
  • 构建过程更新了 sdkconfig、依赖锁文件或受管理组件。
  • 编译结果覆盖了仓库中已经跟踪的 .bin 文件。
  • Python 程序产生了 __pycache__ 和 .pyc 文件。
  • 手动解压、复制文件,把不同版本的内容放进了同一个工作目录。

但不能据此认定所有修改都是可以丢弃的。源码、协议文件和文档的变化也可能是有价值的工作,应先查看差异或备份。

三、推荐处理方式:先储藏,再拉取

目标是:保留本地修改,让工作目录更新为远端最新版,同时不向远端提交任何本地文件。

第 1 步:停止正在修改项目文件的程序

先结束正在进行的编译,并关闭会生成或写入文件的项目工具。这样可以避免储藏或拉取期间,工作目录又产生新变化。

第 2 步:检查本地修改

在项目根目录空白处右键,选择:

TortoiseGit → Check for modifications(检查修改)

查看修改、删除和未版本控制的文件;双击已修改文件可以查看差异。Windows 11 如果没有显示菜单,先点击“显示更多选项”。

第 3 步:储藏本地修改,并包含未跟踪文件

右键选择:

TortoiseGit → Stash changes(储藏更改)

填写容易识别的备注,例如:

更新前备份-20261022

勾选 Include untracked(包含未跟踪文件),然后点击确定。

这一步很关键:默认储藏主要处理已经跟踪的文件。对于报错中的新文件,需要启用 Include untracked 才能一起保存。

普通情况下不需要勾选 --all。它还会包含被忽略的文件,可能把大量构建目录和依赖也纳入储藏。

储藏是本机 Git 仓库中的备份操作,不需要 Commit 或 Push 到远端。完成后,通过 Stash List(储藏列表) 确认备份存在。

第 4 步:确认工作目录可以更新

再次打开“检查修改”,确认阻止更新的修改和未跟踪文件已经处理完毕。

如果储藏报错,或工作目录仍有需要保留的改动,先处理这些问题,再继续拉取。不要把“尝试过储藏”当作“备份已经成功”。

第 5 步:执行 Pull

右键选择:

TortoiseGit → Pull(拉取)

确认远端是 origin,远程分支是自己需要更新的分支,例如 development。如果只是跟随远端更新,且没有独立的本地提交,可以勾选 Fast Forward Only(仅快进)。

点击确定,等待成功。如果仅快进被拒绝,说明还存在分支历史方面的问题,需要进一步检查,不应直接改用强制覆盖。

第 6 步:验证更新结果

更新后检查:

  1. Pull 是否明确显示成功。
  2. Show log(显示日志)中的当前提交是否是目标分支的最新提交。
  3. Check for modifications 中是否还有未处理的变化。

文件夹图标可以作为提示,但不要只依赖图标判断是否同步。

四、拉取成功后,需要马上 Stash Pop 吗?

取决于目的。

目的 建议
只想使用远端最新版 保留储藏,暂时不恢复
需要继续之前的本地开发 先检查储藏内容,再恢复需要的修改

如果立刻 Stash Pop,之前的改动就会重新进入工作目录。本地文件可能再次与远端不同,下次 Pull 时仍可能遇到相同问题。

需要恢复时,可以从 Stash List 选择相应记录并使用 Apply(应用)。Apply 会保留储藏记录;Pop(弹出) 则在应用成功后移除对应记录。恢复时如果新旧内容不兼容,还需要解决冲突。

储藏保存在当前仓库的 .git 中,并不是独立的异地备份。删除仓库或清空储藏前,应另行保存仍然需要的内容。

五、习惯命令行时,对应的操作是什么?

在项目根目录的终端中执行,分支名请替换为实际分支:

# 查看当前分支和本地变化
git status --short --branch

# 储藏已跟踪文件的修改,并包含未跟踪文件
git stash push --include-untracked -m "更新前备份-20261022"

# 确认备份存在,再次检查工作目录
git stash list
git status --short --branch

# 仅允许快进,更新指定分支
git pull --ff-only origin development

# 获取当前提交,并检查与远端分支是否有差异
git rev-parse HEAD
git rev-list --left-right --count HEAD...origin/development
git diff --exit-code origin/development
git status --short --branch

这些命令需要逐步检查结果;如果储藏失败,应停止后续步骤。rev-list 显示 0 0 表示两个提交之间互不领先、互不落后;git diff 无输出且退出码为 0,表示受跟踪文件与该远端版本没有差异。

以上流程没有 Push。为保持更新后的工作目录与远端一致,也没有自动恢复储藏。

六、这次还遇到了两个额外问题

1. Git 索引文件缺失

实际检查时发现:Git 索引文件缺失,导致 12,127 个原有文件显示为已暂存删除,而磁盘上仍存在这些文件。

这与普通的“本地修改将被覆盖”不是同一个问题。处理时先记录仓库状态,确认索引确实缺失、没有其他 Git 操作正在进行,再根据当前提交重建索引,保留工作目录内容,随后储藏实际修改。

重建后识别出 70 个受跟踪文件的修改或删除,以及 42 个未跟踪文件,均已备份。不能仅凭大量删除提示,就假定源码真的丢失了。

不要把删除 .git/index、清空索引或批量取消版本控制当作日常解决办法。索引异常需要单独诊断,重建还会改变暂存状态。

2. Windows 文件路径过长

储藏时还遇到了:

Filename too long
Cannot save the current worktree state

这个项目的依赖目录嵌套较深,在当前仓库启用 Git 长路径支持后,储藏成功:

git config --local core.longpaths true

这是当前仓库的本地设置,不修改远端。它可以帮助 Git 处理长路径,但并不保证 IDE、压缩软件或其他构建工具都支持同样长度的路径。

新项目也可以选择更短的目录,例如 D:\Git\project,减少路径嵌套。

七、怎样减少再次发生?

1. 开始工作前先更新

尽量采用下面的顺序:

检查修改 → 必要时储藏 → Pull → 编译、测试或修改

这样能减少本地已经发生大量变化后,再合并远端更新的情况。

2. 分开维护“更新用目录”和“开发测试目录”

对于主要负责获取固件、编译和验证的使用者,可以 Clone 两份独立的工作目录:

D:\Git\project-latest    用于获取和查看远端版本
D:\Git\project-test      用于修改、编译和测试

更新用目录尽量保持干净,避免在那里执行会改写配置和发布文件的程序。开发测试目录仍可能产生修改,需要按正常 Git 流程管理。

3. 由维护者统一调整生成文件的管理方式

长期来看,应区分源码、配置模板、本机配置、构建缓存和发布产物。例如:

  • 本机路径、串口号等设置采用本地配置或模板。
  • 缓存和临时构建文件纳入合理的忽略规则。
  • 构建输出在项目支持的情况下放到独立目录。
  • 如果需要跟踪发布用 .bin,考虑避免日常编译直接覆盖它们。

这些是项目层面的改进,需要结合团队发布方式决定,不能简单地把所有配置或固件文件都取消跟踪。

4. 理解 .gitignore 的限制

.gitignore 不会停止跟踪已经被 Git 跟踪的文件。

因此,已经进入版本管理的 sdkconfig、.bin 或 .pyc 文件,即使加入忽略规则,后续变化仍可能显示为本地修改。

是否取消跟踪,需要由维护者评估并提交到仓库。不要为了让状态看起来干净,就批量取消项目文件的跟踪。

八、常见问题速查

问题 处理建议
远端显示 up to date,但 Pull 失败 查看后面的合并报错,不能只看获取阶段
提示本地修改会被覆盖 先检查差异,再储藏需要保留的修改
提示未跟踪文件会被移除或覆盖 储藏时勾选 Include untracked,或另行备份并移出冲突路径
不想向远端提交自己的文件 使用本地 Stash,不执行 Push
更新后只想使用最新版 暂不恢复旧储藏
加入 .gitignore 后仍显示修改 确认文件是否早已被 Git 跟踪
显示成千上万个文件被删除,但文件还在 检查索引状态,先备份并诊断,避免直接强制操作
储藏时报 Filename too long 检查长路径支持和项目所在目录的长度

这次处理的结果是:本地分支成功快进到远端最新提交,工作目录干净,原有修改仍保存在本机储藏中,没有向远端提交或推送本地文件。这里只验证了版本同步,没有重新编译或烧录。

参考资料