`git checkout branch -- .`,不覆盖现有文件 上下文问题潜在客户

问题描述

上下文

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

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

简单的解决方案是将配置分支基于 master,但这需要在 master 前进时定期重新设置所有分支。因此,我正在尝试使用两个单独的 git 历史记录的解决方案:

  • master 分支包含应用程序的源代码
  • 所有 config/<archetype> 都源自一个单独的根(通过 git checkout --orphan 创建),并且仅包含构建脚本及其各自的配置。

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

问题

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

潜在客户

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

  • 我在 Windows 上使用 Powershell,它显然提供了一个没有 tar 的原生 --skip-old-files 命令;
  • Git 的 tar 版本接受该标志,但由于我认为 Powershell 管道工作方式的一个怪癖而失败(从 Git Bash 运行的相同命令的工作方式):
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 的解决方案。

解决方法

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

考虑到您在孤立分支上,您不能使用简单的 git merge,而必须指定选项 --allow-unrelated-histories。由于您要编写此脚本,因此不能有任何冲突:这对于正确的合并策略应该很简单,考虑到您在 ours 中,合并策略可能是 config/<archetype>。您还希望您的孤立分支保持不变,这仍然可以使用 --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. 将这些文件保存在某处:复制或移动它们,从主文件中提取所有文件,然后把这些文件放回去

    方法二比较简单。在没有子目录的情况下很容易实现。子目录的存在使这两种方法都有问题:如果工作树当前有一个目录 d 和一个文件 f,但是 master 上的提示提交有一个 file 名为 d 和/或名为 f目录?您希望本案的结果是什么? (tar 方法表示保留现在的内容,因此您最终将保存 d/*f,您可以选择使用方法 2 手动执行这些操作.)

一旦你决定如何处理目录/文件冲突——顺便说一下,这些也是 git checkout 的一个难题——你可以用某种脚本语言编写其余的来实现方法 2。使用 {{ 1}} 或等效物,以便在 mktemp -d 期间隐藏所有现有文件。