--skip-worktree后无法切换分支

50

我想要做什么

我有一个包含敏感数据的文件,所以我不想将该文件的内容推送到远程服务器。

我是怎么做的?

为了实现这一点,当文件为空时,我进行了一次提交,并将此空文件推送到服务器(GitHub)。然后填写了敏感数据并应用了git update-index --skip-worktree path/to/file。但我没有进行任何提交。

现在,我正在尝试切换分支,但出现了以下错误:

    error: Your local changes to the following files would be overwritten by checkout:
    path/to/file
    Please, commit your changes or stash them before you can switch branches.
    Aborting

为何我使用skip-worktree而不是assume-unchanged

我阅读了一些有关此主题的stackoverflow的问题,并找到了Borealid的回答

--assume-unchanged 假定开发人员不应更改文件。此标志旨在提高不需要更改的sdk等文件夹的性能。

--skip-worktree 对于你指示git永远不处理特定文件很有用,因为开发人员应该更改它。例如,如果主存储库上游托管某些生产就绪的配置文件,而您不想意外提交对这些文件的更改,则正是--skip-worktree所需要的。

在这之后,我发现Jeff的问题VonC的答案。 Jeff的问题几乎与我的问题相同,我按照VonC的解决方案尝试解决了问题。 但对我来说并没有起作用。 可能是因为git版本差异造成的,因为那个问题是来自2012年的。 我们与VonC交谈,他说要提出一个新问题,因为他无法记住答案。

我尝试使用--assume-unchanged--skip-worktree一起,并对工作树进行软重置。 但没有任何变化。

有什么建议吗?

你能帮我解决我的问题吗?

谢谢。


2
我尝试复现这个问题,但是它在我的电脑上可以正常工作。我唯一能够复现它的方式是如果我在不同的分支中有不同的私有文件。你有仔细检查过 path/to/file 在你的分支之间是否完全相同吗?请执行 git diff branch1..branch2 -- path/to/file - Justin Howard
5
@Eray Justin 是正确的。在使用 --skip-worktree 命令之前,你必须确保相关文件在所有分支中都是相同的。 - Kise
我要补充一点,如果你尝试检出的分支中根本不存在 path/to/file,也会发生这种情况。因为当你尝试检出时,Git 会尝试删除在检出的分支中不存在的文件。虽然这个语句“你必须确保所涉及的文件在你的所有分支中都是相同的”已经暗示了这一点,但我认为最好还是明确说明一下。 - yfede
8个回答

8

我没有找到一个简洁的解决方案,所以我使用一个 .bat 文件在受影响的文件上运行 --no-skip-worktree。然后我进行 stash,切换分支,stash apply,然后使用另一个 .bat 文件在相同的文件上运行 --skip-worktree

这不是很好,但这是我目前找到的最简单和最快的方法。

更新: 我再次遇到了这个问题,因此我创建了一个 PowerShell 脚本来代替上面提到的 .bat 文件。它将提示用户是否跳过或不跳过这些文件。更新这两个参数,你就可以开始了:

[string]$repoPath = "C:\Fully\Qualified\Path"
[string[]]$filesToSkip = @(
    "Local/Path/To/File.txt",
    "Local/Path/To/OtherFile.txt"
)

$skipText = Read-Host -Prompt 'Skip files? (y/n)'
$skip = $skipText -like "y"
cd $repoPath
foreach ($file in $filesToSkip)
{
    if($skip)
    {
        Write-Host "Skipping" $file
        git update-index --skip-worktree $file
    }
    else
    {
        Write-Host "No-skipping" $file
        git update-index --no-skip-worktree $file
    }
}

请问您能分享一下您的.bat文件吗? - Woland
@Woland,恐怕我已经没有这个副本了,但如果我没记错的话,有一个只包含每个文件的--no-skip-worktree,另一个则只包含相同文件的--skip-worktree - stevospinks
1
@Woland 我又遇到了这个问题,所以我更新了我的答案并添加了一个你可以使用的 PowerShell 脚本。 - stevospinks

5

这并不是一个很好的解决办法,但似乎工作得还算可以(虽然只进行了轻微测试):

在你的PATH中任意位置创建一个名为git-checkoutsw的文件,并将以下内容复制进去:

#!/bin/bash
ignored=( $(git ls-files -v | grep "^S " | cut -c3-) )
for x in "${ignored[@]}"; do
  git update-index --no-skip-worktree -- "$x"
done
git checkout -m $@
for x in "${ignored[@]}"; do
  git update-index --skip-worktree -- "$x"
done

该脚本会捕获被忽略的文件列表,取消忽略它们,使用-m(代表合并)和其他指定的参数检出新分支,然后再次忽略它们。
现在可以使用git checkoutsw来代替git checkout

2
你可以启用稀疏检出并将文件添加到其中,同时添加skip-worktree标记(如果仅添加稀疏检出,则该文件可能会被删除)。
要排除文件,您应将其放入sparse-checkout文件中(请参见git-read-tree手册)。
/*
!unwanted

然后在更新时它不会被更改(所以您不会得到另一个分支的内容,而是保留您的编辑)。这可能不是您想要的。

2

我遇到了相同的问题,最终发现是因为我想要假设未更改的文件有新的更改。

因此,如果我只是对该文件执行no-assume-unchanged操作,将其存储,检出最新版本,弹出我的存储,并再次假设未更改,则现在我可以再次切换分支。


2
这个回答被踩了,但实际上它解释了潜在的问题。上面的解决方案解决了问题,但没有明确说明哪里出了问题——对我很有帮助。 - nitsujri

1

有没有理由在版本控制中检入一个空的文件变体?如果没有

git rm --cached path/to/file
echo 'path/to/file' >> .gitignore
git commit -m'stop tracking path/to/file'

这可能解决您的问题。(必须针对每个具有该文件的分支重复执行。或者,如果您不介意重写历史记录,请使用 git filter-branch 在以前的提交中也将其删除。)


我推送了一个空的文件变量,否则包括sensitivefile.php在内的其他文件以及这些文件将会产生文件未找到错误。因此必须有一个sensitivedata.php文件,无论是否为空。 - Eray
你是否考虑过在构建过程中创建文件?一种常见的模式是拥有一个./configure脚本,该脚本预计在使用某个IDE之前和进行完整构建之前运行。只要它像仅仅touch一个文件那样无害且(计算上)便宜,那么从post-checkout钩子调用该脚本可能是值得的。 - das-g

1

只需找到一行命令即可在所有文件上执行--no-skip-worktree操作。

git ls-files -v | grep "^S" | cut -c 3- | xargs -L1 git update-index --no-skip-worktree

你可以使用该命令轻松创建一个名为alias.unhideall的别名。 :)

0

到目前为止,我发现--assume-unchanged和--skip-worktree并不起作用。我的git版本是:git version 2.8.4(Apple Git-73)。目前唯一有效的方法是使用Stash。


1
根据我的经验,你不能将被忽略的文件存储。 - Stealth Rabbi

0
如一条评论所提到的(我只是在这里写作为答案,希望那些不读评论或可能会错过它们的人看到这个),git update-index --skip-worktree path_to_file可以工作,但前提是文件在所有本地分支中都相同(我假设是本地的)。
在我的情况下有效的方法是:
  1. git update-index --no-skip-worktree path_to_file首先撤销跳过。

  2. 删除所有本地分支(您可以使用:git branch | grep -v "master" | xargs git branch -D),进入主分支并修改包含敏感信息的文件。

  3. 再次使用git update-index --skip-worktree path_to_file

  4. git checkout any__new_local_branch现在完全正常。

感谢原问题中评论区的@kise。

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