交互式合并使用 git 跟踪的文件和未跟踪的本地文件

Interactively merge files tracked with git and untracked local files

我使用了几个软件包(例如 gitlab),您可以通过从他们的 git 存储库中克隆来安装这些软件包。它们通常带有一些 config.example(在版本控制下),您可以将其复制到自己的 config 文件(不受版本控制,甚至在 .gitignore 中被忽略)并适应您的需要。

更新上游包时,例如更改配置文件选项时,显然只会反映在 config.example

是否有我遗漏的 git 命令链可以帮助我将 config.example 的更改与 upstream/HEAD 中的新更改进行比较,甚至可能以交互方式合并它们进入我的本地 config 文件?

如果我能在 git add/commit --interactive.

中获得类似交互式补丁模式的东西,那就太棒了

要将本地存储库中的 config.exampleupstream/HEAD 中的相应文件进行比较,您可以 运行:

git diff upstream/HEAD config.example

不幸的是,我不知道有什么方法可以让 git 直接将更改应用到 git 不跟踪的文件。

a tool called sdiff 可能会满足您的要求。

使用 sdiff -o config config.example config

调用它(在您的情况下)
  • 您可以将 vim 用作与 vimdiff 的合并工具。
  • Emacs 也可以用 ediff-mode 来做到这一点。

以下应该有效:

git diff <some-args> | perl -pe 's/path\/to\/changes\/file/path\/other/g' > t
patch -p1 < t 
rm t

如果您想要完整的 git 合并,您可以 git 通过像 git read-tree 那样设置索引条目然后调用 [=32] 来处理任意内容=] 的正常合并驱动程序就在那个条目上。 Git指的是不同的内容版本为"stages";它们是 1:原件,2:你的,3:他们的。合并比较从 1 到 2 和从 1 到 3 的变化并执行它的操作。要设置它,请使用 git update-index:

orig_example=        # fill in the commit with the config.example you based yours on
new_upstream=        # fill in the name of the upstream branch

( while read; do printf "%s %s %s\t%s\n" $REPLY; done \
| git update-index --index-info ) <<EOD
100644 $(git rev-parse $orig_example:config.example)  1 config
100644 $(git hash-object -w config)                   2 config
100644 $(git rev-parse $new_upstream:config.example)  3 config
EOD

并且您已经为该路径安排了自定义内容的合并。现在开始:

git merge-index git-merge-one-file -- config

它会自动合并或留下通常的冲突排泄物,根据需要修复它,如果需要,git rm --cached --ignore-unmatch(或保留)索引条目。

您放入索引的路径名(此处所有三个条目中的 "config"),顺便说一句,不必已经存在或与任何事情有任何关系。您可以将其命名为 "wip" 或 "deleteme" 或任何您想要的名称。合并是索引条目中的内容 id。

我认为这很可能会满足您的要求。如果你真的想从上游变化中挑选,你可以把你自己的内容放在 config.example 并做 git checkout -p upstream -- config.example,这与 git add -p 相反,然后把事情放回去他们是。

您可以通过多种方式解决这个问题。我能想到两个:git-merge-file 和好旧的 patchgit-merge-file 方法提供了一些交互性,而 patch 方法提供了 none.

git-merge-file

的解决方案

假设您有一个原始文件 config.example,用于创建本地未版本化文件 config.local。现在,当上游更新时 config.example,您可以按照类似以下步骤合并任何新更改。

$ git fetch
$ git show master:config.example > config.example.base
$ git show origin/master:config.example > config.example.latest
$ git merge-file config.local config.example.base config.example.latest

这将使用通常的冲突标记更新 config.local,然后您必须使用您最喜欢的合并工具解决这些问题(Emacs 中的 ediff 很好,我相信有类似的模式Vim)。比如下面三个文件,

config.example.base:

Original: 
          some config

config.example.latest:

Original: 
          some config

Upstream:
          new upstream config

config.local:

Original: 
          some config

My changes:
          some other config

将这样合并:

Original: 
          some config

<<<<<<< config.local
My changes:
          some other config
=======
Upstream:
          new upstream config
>>>>>>> config.example.latest

您或许可以毫不费力地编写此脚本。顺便说一句,git-merge-file 可以对任意 3 个文件进行操作,它们不需要在 git 下进行版本控制。这意味着可以用它来合并任意三个文件!

patch 的解决方案:

假设相同的文件名,config.localconfig.example,以下应该有效。

$ git fetch
$ git diff master..origin/master -- config.example | sed -e 's%\(^[-+]\{3\}\) .\+% config.local%g' > /tmp/mypatch
$ patch < /tmp/mypatch

git checkout --patch 选择 diff hunks,这里最简单的可能是将您的内容放在上游路径,这样做,然后清理:

cp config  config.example
git checkout -p upstream   config.example
mv config.example  config
git checkout @  config.example

这将使您从 git add --patch.

中选择两个不同的大块头