简体   繁体   English

`git checkout branch —.`,不覆盖现有文件

[英]`git checkout branch — .`, without overwriting existing files

Context语境

I have a repository containing the development history for an application.我有一个包含应用程序开发历史的存储库。 The application is compiled on the fly from its source code to be used right after the repository has been cloned.该应用程序是从其源代码动态编译的,以便在克隆存储库后立即使用。

The application uses a couple of configuration files, which are declined in several configuration archetypes.该应用程序使用几个配置文件,这些文件在几个配置原型中被拒绝。 For ease of deployment, I would like to store each archetype on a separate branch, and obtain both the application from master and the configuration from the selected branch after cloning.为了便于部署,我想将每个原型存储在一个单独的分支上,并在克隆后从master获取应用程序和从所选分支获取配置。

The plain solution would be to base the configuration branches on master , but this requires regularly rebasing them all when master advances.简单的解决方案是将配置分支基于master ,但这需要在master前进时定期重新设置它们。 I'm therefore trying out a solution with two separate git histories:因此,我正在尝试使用两个单独的 git 历史记录的解决方案:

  • The master branch contains the application's source code; master分支包含应用程序的源代码;
  • All config/<archetype> stem from a separate root (created through git checkout --orphan ), and only contain the build script and their respective configurations.所有config/<archetype>都来自一个单独的根(通过git checkout --orphan创建),并且只包含构建脚本及其各自的配置。

The build script must now retrieve all source code from master before building it, taking care not to overwrite the archetype configuration files with the default ones from master .构建脚本现在必须在构建之前从master检索所有源代码,注意不要用来自master的默认配置文件覆盖原型配置文件。 Therefore...所以...

Question问题

I need a variant of git checkout <branch> --.我需要一个git checkout <branch> --. that does not touch existing files, whatever their state (changed, unchanged, ignored, untracked...).不会触及现有文件,无论它们的 state(已更改、未更改、已忽略、未跟踪......)。

Leads潜在客户

I have found this identical question which does have an answer ( git archive mybranch | tar x --skip-old-files ), however I'm encountering some issues:我发现这个相同的问题确实有答案( git archive mybranch | tar x --skip-old-files ),但是我遇到了一些问题:

  • I'm using Powershell on Windows, which apparently provide a native tar command that does not have --skip-old-files ;我在 Windows 上使用 Powershell,它显然提供了一个没有--skip-old-files的本机tar命令;
  • Git's version of tar accepts the flag, but fails due to what I assume is a quirk in how Powershell's pipeline works (the same command run from Git Bash works): 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

In this environment, I would much prefer a solution which only uses Git.在这种环境下,我更喜欢只使用 Git 的解决方案。

I am not sure this is exactly what you need but instead of a partial checkout you may want to use a merge , which is the standard way to resolve conflicting commits.我不确定这是否正是您所需要的,但您可能想要使用merge而不是部分checkout出,这是解决冲突提交的标准方法。

Considering that you are on an orphaned branch, you cannot use a simple git merge but you have to specify the option --allow-unrelated-histories .考虑到您在孤立的分支上,您不能使用简单的git merge ,但您必须指定选项--allow-unrelated-histories Since you want to script this, there cannot be any conflict: this should be straightforward with the right merging strategy, which could be ours given that you are in config/<archetype> .由于您要对此编写脚本,因此不会有任何冲突:这应该使用正确的合并策略很简单,因为您在config/<archetype>中,这可能是ours的。 You also want your orphaned branch to remain untouched, still possible with --no-commit and a following git reset --hard HEAD .您还希望您的孤立分支保持不变,仍然可以使用--no-commit和以下git reset --hard HEAD In the end, the merge command from config/<archetype> could be something like this (untested):最后,来自config/<archetype>的合并命令可能是这样的(未经测试):

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

Personally, I'd favor the git archive... | tar -x --skip-old-files就个人而言,我更喜欢git archive... | tar -x --skip-old-files git archive... | tar -x --skip-old-files solution here, but consider the following: git archive... | tar -x --skip-old-files解决方案在这里,但请考虑以下几点:

  • You're on some orphan branch B right now.你现在在某个孤儿分支B上。 You have some set of files occupying your working tree.您有一些文件占用了您的工作树。 You want these same files to appear in your working tree after extracting all the files that appear in the tip commit of master .在提取出现在master的提示提交中的所有文件后,您希望这些相同的文件出现在您的工作树中。

  • Therefore, there are just two ways to achieve this:因此,只有两种方法可以实现这一点:

    1. Extract files from master but don't touch any existing files: that's the solution that can be done with the pipe, which isn't working from PowerShell.master文件中提取文件但不要触及任何现有文件:这是可以使用 pipe 完成的解决方案,而 PowerShell 无法使用该解决方案。 You could also construct a rather fancy "list all files and directories in the top level of the commit; for each such name, test whether it exists here; if not; extract that name from the commit; done" loop.您还可以构建一个相当花哨的“列出提交顶层中的所有文件和目录;对于每个这样的名称,测试它是否在此处存在;如果不存在;从提交中提取该名称;完成”循环。 Or或者
    2. Save these files somewhere: copy or move them away, extract all files from master, then put these files back .将这些文件保存在某处:复制或移动它们,从 master 中提取所有文件,然后将这些文件放回去

    Method 2 is relatively simple.方法二比较简单。 It's easy to achieve in the absence of subdirectories.在没有子目录的情况下很容易实现。 The presence of subdirectories makes both methods questionable: what if the working tree currently has a directory d and a file f , but the tip commit on master has a file named d and/or a directory named f ?子目录的存在使得这两种方法都有问题:如果工作树当前有一个目录d和一个文件f ,但是master上的提示提交有一个名为d文件和/或一个名为f目录怎么办? What result would you like for this case?对于这个案例,您希望得到什么结果? (The tar method says preserve whatever is here now so you'll end up saving d/* and f , which you can choose to do manually with method 2.) tar方法说保留现在这里的任何内容,因此您最终将保存d/*f ,您可以选择使用方法 2 手动执行。)

Once you decide on what to do about directory/file conflicts—these are a hard case for git checkout too, by the way—you can write the rest in some scripting language to implement method 2. Use mktemp -d or equivalent to make a place to hide all the existing files for the duration of the git checkout .一旦你决定如何处理目录/文件冲突——顺便说一句,这些也是git checkout的困难案例——你可以用某种脚本语言编写 rest 来实现方法 2。使用mktemp -d或等效项来制作在git checkout期间隐藏所有现有文件的地方。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM