【问题标题】:Interactively merge files tracked with git and untracked local files交互式合并使用 git 跟踪的文件和未跟踪的本地文件
【发布时间】:2015-08-05 10:51:26
【问题描述】:

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

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

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

如果我能在git add/commit --interactive 中获得类似交互式补丁模式的东西,那就太棒了。

【问题讨论】:

    标签: git command-line merge diff interactive


    【解决方案1】:

    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 的两个不同的大块选择。

    【讨论】:

      【解决方案2】:

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

      解决方案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\}\) .\+%\1 config.local%g' > /tmp/mypatch
      $ patch < /tmp/mypatch
      

      【讨论】:

      • 我喜欢这个 3-way-merge 的答案,但遗憾的是它根本不像 git add -p 那样具有交互性,这意味着我无法真正查看新的配置选项。尽管如此,它还是有我的支持,因为这可能是其他人的方式。
      【解决方案3】:

      如果你想要完整的 git 合并,你可以让 git 通过像 git read-tree 那样设置一个索引条目,然后只在那个条目上调用 git 的正常合并驱动程序来使用任意内容。 Git 将不同的内容版本称为“阶段”;它们是 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(或保留)索引条目(如果需要)。

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

      我认为这很可能会满足您的需求。如果您确实想从上游更改中进行选择,您可以将自己的内容放在 config.example 并执行 git checkout -p upstream -- config.example,这与 git add -p 相反,然后将内容恢复原状。

      【讨论】:

      • 我最喜欢这个答案的最后一段(其余的对我来说有点像黑魔法)。有了上一段的想法,我可以只做cp config config.example ; git checkout -p upstream -- config.example,然后以交互方式决定如何合并更改,最后运行mv config.example config &amp;&amp; git reset config.example &amp;&amp; git checkout -- config.example 来设置。如果您将最后一段拆分为单独的答案,我会接受它的清晰性和简单性。
      【解决方案4】:

      以下应该有效:

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

      【讨论】:

        【解决方案5】:
        • 您可以将vim 用作vimdiff 的合并工具。
        • Emacs 也可以通过 ediff-mode 执行此操作。

        【讨论】:

          【解决方案6】:

          a tool called sdiff 可能会做你想做的事。

          sdiff -o config config.example config调用它(在你的情况下)

          【讨论】:

            【解决方案7】:

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

            git diff upstream/HEAD config.example
            

            不幸的是,我不知道如何让 git 直接将更改应用到 git 不跟踪的文件。

            【讨论】:

            • 是的,我知道如何获得差异,但现在以交互方式将其应用于 config 是我的实际问题:-/
            • 您是否尝试过将更新的配置文件复制到 config.example 上,签入,使用标准工具从源合并,然后将其移回配置并重置到您将更改提交到配置之前。例子?如果文件不是太不同步,您至少应该获得一些帮助来合并更改的内容。
            猜你喜欢
            • 1970-01-01
            • 2022-01-01
            • 2017-03-15
            • 2017-12-30
            • 2015-01-11
            • 2012-03-28
            • 1970-01-01
            • 2018-07-21
            • 1970-01-01
            相关资源
            最近更新 更多