`git 结帐分支 -- .`,不覆盖现有文件

`git checkout branch -- .`, without overwriting existing files

上下文

我有一个包含应用程序开发历史的存储库。该应用程序是从其源代码即时编译的,可以在存储库被克隆后立即使用。

该应用程序使用了几个配置文件,这些文件在几个配置原型中被拒绝。为了便于部署,我想将每个原型存储在一个单独的分支上,并在克隆后从master获取应用程序并从所选分支获取配置。

简单的解决方案是将配置分支建立在 master 的基础上,但这需要在 master 前进时定期将它们全部变基。因此,我正在尝试使用两个单独的 git 历史的解决方案:

构建脚本现在必须在构建之前从 master 检索所有源代码,注意不要用来自 master 的默认配置文件覆盖原型配置文件。因此...

问题

我需要 git checkout <branch> -- . 的变体 触及现有文件,无论它们处于什么状态(已更改、未更改、已忽略、未跟踪...)。

潜在客户

我找到了 this 个确实有答案的相同问题 (git archive mybranch | tar x --skip-old-files),但是我遇到了一些问题:

PS> &"C:\Program Files\Git\bin\git.exe" archive master | &"C:\Program Files\Git\usr\bin\tar.exe" x --skip-old-files
/usr/bin/tar: Malformed extended header: missing newline
/usr/bin/tar: Substituting `.' for empty member name
/usr/bin/tar: Substituting `.' for empty member name
/usr/bin/tar: Skipping to next header
/usr/bin/tar: Exiting with failure status due to previous errors

在这种环境下,我更喜欢只使用 Git.

的解决方案

我不确定这是否正是您所需要的,但您可能想要使用 merge 而不是部分 checkout,这是解决冲突提交的标准方法。

考虑到你在一个孤立的分支上,你不能使用简单的 git merge 但你必须指定选项 --allow-unrelated-histories。既然你想编写脚本,就不会有任何冲突:这应该是直接使用正确的合并策略,假设你在 config/<archetype> 中,这可能是 ours。您还希望您的孤立分支保持不变,仍然可以使用 --no-commit 和后续的 git reset --hard HEAD。最后,来自 config/<archetype> 的合并命令可能是这样的(未经测试):

git merge --allow-unrelated-histories -s recursive -Xours --no-commit master

就我个人而言,我更喜欢这里的 git archive ... | tar -x --skip-old-files 解决方案,但请考虑以下几点:

  • 你现在在某个孤儿分支 B 上。您有一些文件占用了您的工作树。在提取出现在 master.

    的提示提交中的所有文件后,您希望这些相同的文件出现在您的工作树中
  • 因此,只有两种方法可以实现:

    1. master 中提取文件,但不要触及任何现有文件:这是可以使用管道完成的解决方案,它不适用于 PowerShell。您还可以构建一个相当奇特的“列出提交顶层的所有文件和目录;对于每个这样的名称,测试它是否存在于此处;如果不存在;从提交中提取该名称;完成”循环。
    2. 将这些文件保存某处:复制或移动它们,从 master 中提取所有文件,然后将这些文件放回原处

    方法二比较简单。在没有子目录的情况下很容易实现。子目录的存在使这两种方法都受到质疑:如果工作树当前有一个目录 d 和一个文件 f,但是 master 上的提示提交有一个 文件怎么办 名为 d and/or 一个名为 f 目录?对于这种情况,您希望得到什么结果? (tar 方法说 保留现在这里的任何内容 所以你最终会保存 d/*f,你可以选择手动执行使用方法 2.)

一旦决定如何处理 directory/file 冲突——顺便说一下,这些对于 git checkout 来说也是一个难题——你可以用某种脚本语言编写其余部分以实现方法 2 . 在 git checkout.

期间使用 mktemp -d 或等价物来隐藏所有现有文件