【问题标题】:File permissions are being shared across git branches文件权限在 git 分支之间共享
【发布时间】:2026-02-25 00:50:01
【问题描述】:

不确定为什么会发生这种情况,但问题是我在将该分支推送到远程后更改了该分支的文件权限。然后我从我们的集成分支中签出一个新分支,它具有“死分支”的权限,它是这样的:

# on feature branch
git checkout --no-track -b foo
git reset --soft "remotes/origin/dev"
git add .
git add -A
git commit --allow-empty -am "bar"
git push -u origin foo
chmod -R -w .  # remove all write permissions in current dir

# later on
git branch --no-track z "remotes/origin/dev"
git checkout z
### ughh this new branch z files are not writable, but whyyyy?

基本上我们将文件更改为不可写,并且该分支永远不会合并到任何分支中 - 我们在修改文件权限之前将其推送到远程。

为什么不可写文件权限会出现在从未与不可写文件分支合并的其他分支中?

【问题讨论】:

    标签: git git-branch git-checkout


    【解决方案1】:

    Git 关心并为每个文件存储的唯一权限是“可执行或不可执行”权限。 chmod 的这种行为的 TL;DR 是“不要那样做”——为此使用单独的克隆或单独的工作树。有关更多详细信息,请继续阅读。

    具体来说,在每个提交快照中,每个文件(或 blob,实际上)都被标记为100644(不可执行)或100755(可执行)模式。您将在 git ls-tree 输出中看到这一点,就像在任何现有提交上运行一样。 所有其他权限,包括读取或写入的能力,由您决定。在 Unix 和类 Unix 系统上,当 Git 创建工作树文件时,它实际上使用模式0777(如果文件是可执行的)或0666(如果不是)。您的 umask 会从这些中删除任何不需要的权限;典型的 umask 值为022(删除组和其他写入权限)或002(仅删除非组/其他写入权限),但安全子系统可能使用077(删除所有组和其他权限),例如.

    请注意,Git 确实具有保持内部存储库数据组可写的能力,但这些不是工作树文件:这些主要影响 Git 存储松散和打包对象的目录,参考值等。这些由core.sharedRepository 设置控制;见the git config documentation。 (请记住,在目录中创建和删除文件的能力取决于当前用户和组 ID 对目录本身的写入权限。嗯,也就是说,除非您涉及 ACL;然后它变得非常复杂。)

    当使用git checkout 从一个提交切换到另一个提交时,Git 只会根据需要删除和替换工作树文件。这种需求在很大程度上取决于 index 内容,索引索引工作树。这解释了为什么最终保留了一些(但不是全部)文件权限。更多关于此的信息,请参阅Checkout another branch when there are uncommitted changes on the current branch

    【讨论】:

    • 嗯,有道理,如何为给定的分支创建一个新的工作树,这样我就可以在不影响原始工作树的情况下更改文件权限?
    • 在 Git 2.5 或更高版本中,您可以使用git worktree addgit worktree 有一些奇怪的问题,其中只有一些是最近才修复的,但如果你将它与特定的分支名称一起使用,你应该没问题。在 2.5 之前,有一个 hacky 脚本来完成这项工作,但你最好只做一个额外的克隆。如果你在同一个挂载的文件系统上创建一个本地克隆,Git 无论如何都会对所有存储库数据使用硬链接,并且几乎不需要额外的磁盘空间。 (这仍然适用于 2.5 及更高版本。)
    • 嗯,硬链接听起来有风险,这不是说删除一个就删除另一个吗?
    • 不,硬链接有引用计数。 符号 链接存在这种风险。使用硬链接,删除您克隆的存储库以创建另一个存储库只会减少链接文件上的链接计数。在链接计数变为零之前,操作系统不会删除底层文件(即使这样,所有打开的实例也必须先关闭:保持文件打开会强制系统保留文件块)。