Git – “skip-worktree”与 Pull 的问题
使用 Git “skip-worktree” 选项可能会导致 Git Pull 出现问题。
1. 问题
如果您有一个像 web.config 这样的配置文件,它需要被 Git 跟踪,但您希望忽略本地的更改,这种情况无法通过 .gitignore 解决。我们将展示实现此目标的替代方法。
有关在 Git 中忽略文件的更多方法,请参阅 [1]。
skip-worktree 选项是一个不太为人所知的 Git 选项。此方法旨在应用于您希望跟踪、忽略本地更改的文件。这是 .gitignore 方法的推荐替代方案。
但是,问题在于,skip-worktree 选项仍然需要 Git 对情况进行仔细管理。如果在其他地方修改了原始文件,尝试执行 Git Pull 可能会导致错误,并拒绝 Pull。需要仔细的手动管理/解决情况。
2. 从命令行使用 skip-worktree
以下是您需要的命令
忽略已跟踪文件的本地更改
git update-index --skip-worktree  [<file>...]
重新跟踪本地更改
git update-index --no-skip-worktree [<file>...]
列出所有标记为 skip-worktree 的文件
git ls-files -v | grep ^S
3. “skip-worktree” 选项的问题情况
让我们解释一下问题情况是如何生成的。假设我们有两个用户,每个用户都有自己的仓库。让我们关注一个文件 config.txt。最初,所有仓库的文件版本都相同。

然后,User1 在他的系统上决定修改 config.txt,但不想提交更改,因此他使用 skip-worktree 选项对其进行标记。

然后,User2 在他的系统上作为其正常工作的一部分修改了 config.txt。

然后,User2 提交了他的工作并将更改推送到远程仓库。

现在,当 User1 想要从远程仓库 Pull 时,我们就遇到了问题。

问题在于 Pull 被拒绝并出现错误。文件 config.txt 无法修改。更糟糕的是,我们现在有两个版本的 config.txt 文件,版本 A1 和 A2。
Pull 生成的错误将类似于
error:
Your local changes to the following files would be overwritten by merge:
                Folder1/config.txt
Please commit your changes or stash them before you merge.
Aborting
4. 理解问题
问题在于,“本地仓库 1”上的 config.txt 版本为 A1,该文件已被修改并通过 skip-worktree 标志进行保护。Git 无法触及该文件。User1 希望拥有 config.txt 的本地版本 A1,但现在全局版本已更改为 A2。
如果用户仅解除 skip-worktree 标志(git update-index --no-skip-worktree config.txt),文件 config.txt Ver A1 将仅显示为“本地仓库 1”中的 UNSTAGED 和 UNCOMMITTED。
User1 现在肯定需要合并版本 A1 和 A2,问题在于他是否希望将他的版本 A1 的更改提交到“远程仓库”。
5. 问题解决
5.1. 变体 1:提交本地更改
如果 User1 决定他实际上想将他的更改 ver A1 提交到“远程仓库”,他将解除 skip-worktree 标志。由于文件 config.txt ver A1 未提交,Git Pull 仍然无法工作。User1 决定将文件 config.txt 提交到“本地仓库 1”。User1 再次执行 Pull,然后自动或手动将 config.txt 合并到版本 A12。然后,他将文件 A12 提交到“本地仓库 1”。然后 Push 到“远程仓库”。
现在“远程仓库”拥有版本 A12,而 User1 在“本地仓库 1”中也拥有版本 A12。

User1 的步骤是
- 在“本地仓库 1”中解除 config.txt ver A1的skip-worktree标志
 (git update-index --no-skip-worktree config.txt)
- 将文件 config.txt ver A1提交到“本地仓库 1”
- 拉取 (Pull)
- 解决文件合并(得到文件 config.txt ver A12)
- 将文件 config.txt ver A12Push到“远程仓库”
5.2. 变体 2:Stash 本地更改
如果 User1 决定他不想将他的更改提交到“远程仓库”,他将解除 skip-worktree 标志。由于文件 config.txt ver A1 未提交,Git Pull 仍然无法工作。User1 决定 stash 文件 config.txt。User1 再次执行 Pull 并将文件 config.txt ver A2 提取到“本地仓库 1”。然后 User1 将文件从 stash 应用到“本地仓库 1”。Git 将执行自动或手动合并。User1 手动将新文件合并到版本 A12。然后,User1 将 skip-worktree 标志应用于文件 A12。
现在“远程仓库”拥有版本 A2,而 User1 在“本地仓库 1”中拥有版本 A12。

User1 的步骤是
- 在“本地仓库 1”中解除 config.txt ver A1的skip-worktree标志
 (git update-index --no-skip-worktree config.txt)
- Stash 文件 config.txt ver A1
- 拉取 (Pull)
- 将 stash 应用到“本地仓库 1”
- 解决文件合并(得到文件 config.txt ver A12)
- 在“本地仓库 1”中为 config.txt ver A12设置skip-worktree标志
 (git update-index --skip-worktree config.txt)
6. 结论
Git 选项 skip-worktree 是处理诸如 web.config 等配置文件需要本地修改的情况的推荐方法。但是,有时情况会变得相当复杂,就像上面的问题一样,当同一个文件在其他地方被修改,然后 Git Pull 就无法工作时。
Git 没有什么是一帆风顺的。我使用流行的 Git GUI SourceTree,但即便如此,如果没有对您所做的事情有很好的理解,工具本身也无法解决问题。
7. 参考文献
- [1] https://codeproject.org.cn/Articles/5363864/Git-4-Ways-to-Ignore-Files
- [2] https://codeproject.org.cn/Articles/5363987/Git-SourceTree-Custom-Actions-for-skip-worktree-Op
- [3] https://stackoverflow.com/questions/35690736/handling-changes-to-files-with-skip-worktree-from-another-branch
8. 历史
- 2023年7月10日:初始版本


