git:如何在分支之间进行合并,同时保留某个分支独有的某些变更集?

发布于 2024-08-02 01:13:33 字数 740 浏览 7 评论 0原文

对于那些将绝对路径和数据库凭证硬编码到 Web 应用程序中多个随机位置的人来说,地狱里有一个特殊的地方。 可悲的是,在他们下地狱之前,他们正在地球上造成严重破坏。 我们必须处理他们的代码。

我必须对此类 Web 应用程序之一执行一些小更改。 我创建一个新分支 features,并执行全局查找和查找。 替换以更新我的本地环境的路径和凭据。 我承诺这一点。 我还将其标记为本地

我高兴地跳入危险的黑客忏悔,在经历了数百个令人困惑的补丁之后,我想将我的 features 更改合并到 master 分支,但我不想要一个 local 提交合并。

今后,我将在 masterfeatures 之间来回合并,并且我希望 local 保留在 features 中,并且永远不会出现在master中。

理想情况下,我希望这一切都能神奇地发生,并且使用尽可能少的有趣参数等。

有没有一种我所缺少的简单明显的方法可以做到这一点?

我可以想到几个,但它们都要求我记住我不想要那个提交。 这绝对不是我的强项。 尤其是对于这种被黑客攻击得很差的程序。

如果做不到这一点,我对更复杂的、手动的方式来处理这种情况感兴趣。

There's a special place in hell for people who hardcode absolute paths and database credentials into multiple random places in web applications. Sadly, before they go to hell they're wreaking havoc on Earth. And we have to deal with their code.

I have to perform a few small changes to one of such web applications. I create a new branch features, and perform a global find & replace to update the paths and credentials to my local environment. I commit that. I also tag this as local.

I merrily leap into perilous hacking penitence, and after a perplexing hundred patches, I want to merge my features changes into the master branch, but I do not want the one local commit to be merged.

Onwards, I'll be merging back and forth between master and features, and I'd like local to stay put in features, and never ever show up in master.

Ideally, I'd like all this to happen magically, with as little funny parameters and whatnot as possible.

Is there a simple obvious way to do it that I'm missing?

I can think of a couple, but they all require me to remember that I don't want that commit. And that's definitely not my forte. Especially with such poorly hacked programs.

Failing that, I'm interested in more convoluted, manual-ish ways to handle the situation.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

太阳公公是暖光 2024-08-09 01:13:33

我对这个问题的解决方案使用 rebase 而不是 merge

从这样的提交树开始:

a-b-c <-- master
 \
  d <-- local
   \
    e-f-g <-- dev

$ git rebase --onto master local dev

       master 
       V 
   a-b-c-e'-f'-g' <-- dev
     \
      d <-- local

$ git checkout master

$ git merge dev

               master 
               V 
   a-b-c-e'-f'-g' <-- dev
     \
      d <-- local

$ git rebase --onto master master local

               master 
               V 
   a-b-c-e'-f'-g' <-- dev
                \
                 d' <-- local

$ gitbranch -f dev当地的

               master 
               V 
   a-b-c-e'-f'-g'
                \
                 d' <-- local
                 ^
                 dev

My solution to this problem uses rebase rather than merge

Starting with a commit tree like this:

a-b-c <-- master
 \
  d <-- local
   \
    e-f-g <-- dev

$ git rebase --onto master local dev

       master 
       V 
   a-b-c-e'-f'-g' <-- dev
     \
      d <-- local

$ git checkout master

$ git merge dev

               master 
               V 
   a-b-c-e'-f'-g' <-- dev
     \
      d <-- local

$ git rebase --onto master master local

               master 
               V 
   a-b-c-e'-f'-g' <-- dev
                \
                 d' <-- local

$ git branch -f dev local

               master 
               V 
   a-b-c-e'-f'-g'
                \
                 d' <-- local
                 ^
                 dev
﹉夏雨初晴づ 2024-08-09 01:13:33

您可以使用 gitcherry pick 仅合并您选择的补丁。 只需挑选除本地提交之外的所有提交到主分支即可。

You can use git cherry pick to only merge the patches you select. Just cherry pick every commit except for the local one over to the master branch.

彻夜缠绵 2024-08-09 01:13:33

技术 (Git) 解决方案将使用 git 属性,使用属性合并。

merge

属性merge会影响在git merge期间需要进行文件级合并时文件的三个版本的合并方式。

您会在SO问题中找到“我如何告诉 git 始终选择本地版本来进行特定文件上的冲突合并?”使用此类属性的示例,以强制保留合并到给定分支时某些文件的本地版本。

设置合并属性的问题是包含路径的文件可能包含我想要合并的其他更改的代码

不要忘记您可以关联任何类型的脚本来通过 git 属性来管理这些合并。 其中包括一个脚本,该脚本能够将您想要的更改保留在本地,同时合并其余部分。 编写这样的“合并管理器”比较复杂,但它是一种实现临时自动化解决方案的方法。


一种技术含量较低的解决方案是将配置值配置文件分开:

  • 配置文件仅包含要替换的名称
  • 配置值是多个文件(每个环境一个)与每个名称的实际值。

脚本用于将实际配置文件中的名称替换为给定环境所需的配置值文件之一的值。

A technical (Git) solution would be using git attributes, using the attribute merge.

merge

The attribute merge affects how three versions of a file is merged when a file-level merge is necessary during git merge.

You will find in the SO question "How do I tell git to always select my local version for conflicted merges on a specific file?" an example of using such an attribute, to force keeping the local version of certain files when merging to a given branch.

The problem with setting merge attributes is that the files that contain the paths may contain other changed code, which I want merged

Do not forget you can associate any kind of script to manage those merges through git attributes. That include a script able to keep changes you want local, while merging the rest. It is more complicated to write such a "merge manager", but it is a way toward an ad-hoc automated solution.


A less-technical solution would be to separate the configuration values from the configuration files:

  • the configuration file contains only names to be replaced
  • the configuration values are several files (one per environment) with the actual values for each name.

A script is used to replace the name in the actual config file by the values of one of the config values files needed for a given environment.

冧九 2024-08-09 01:13:33

好的。 这并不能保证每次都有效,但类似的事情可以工作(在这种情况下,无论如何你都会有一个必须解决的冲突更改):

  • 你的本地分支是否
  • 只进行本地更改,
  • 继续开发

在合并到时 master:

  • 从您的分支中rebase -i master 并将仅限本地的更改移至补丁链的END
  • 解决过程中的任何冲突。 如果仅本地更改位于配置文件中,并且您在常规开发中没有触及它们,那么您将不会有任何问题。 否则,如果你们确实存在冲突,那么这就是你们实际上在同一区域发生变化的情况,并且无论如何都需要您的关注来解决。
  • 查看master
  • 合并你的本地分支-1:

    git merge local^

这将使你的 master 拥有本地上除最后一个之外的所有更改。

如果您有多个 local=only 更改,我建议您在变基期间将它们压缩在一起。

ok. this is not guaranteed to work every time but something like this can work (and in the cases it wont you will have a conflicting changes anyway that has to be resolved):

  • do your local branch
  • do local-only change
  • continue development

when doing merge to the master:

  • rebase -i master from your branch and move the local-only change to the END of the patch chain.
  • resolve any conflicts in the process. If the local-only change is in the config files and you are not touching them in the regular development, then you will have no problems. If, otherwise, you do have a conflict, then this is a case when you actually change in the same area and it needs your attention to resolve anyway.
  • check out master
  • merge your local-branch -1:

    git merge local^

This will leave you with master having all the changes on the local except for the last one.

If you have multiple local=only changes, I suggest you squash them together during rebase.

宫墨修音 2024-08-09 01:13:33

我会针对 master 进行交互式变基,并将您的路径名修复提交移至末尾。 然后,您可以合并到该点。 只要继续将您的特殊承诺坚持到底即可。

您可能还会发现这个储藏室很有用。 您可以将它们隐藏起来,而不是实际提交路径名修复。 如果尝试这种方法,您可能需要查看如何反向应用存储上的问题。

I would do an interactive rebase against master and move your path-name-fixup-commit to the end. Then, you can merge up to the that point. Just keep moving your special commit to the end.

You may also find the stash useful. Instead of actually committing the path name fixups you could stash them away. If try this approach you may want to check out the question on How to reverse apply a stash.

十年不长 2024-08-09 01:13:33

好吧,因为到目前为止还没有答案提供直接的解决方案,所以我假设我想做的事情是不可能的,并添加到一堆偶尔有用的解决方案中:

如果您总是在功能上进行开发分支,然后您可以将 features 合并到 master,然后在 master 中,git revert local。 (其中 local 是引用您为本地环境自定义路径等的提交的标记。)

现在您绝不能将 master 合并到 features,因为这也会合并反向 local 提交。

在这种情况下,master 变成了一种部署分支,只接收来自其他分支的合并。 (理想情况下,仅来自 features 分支。)

这很容易走下坡路,只需在工作流程中添加另一个开发人员,事情就会变得非常混乱。 仍然可以通过使用显式合并策略来解决,但这通常很痛苦。

Well, because no answer so far provided a straightforward solution, I'll assume what I want to do is impossible, and add to the pile of occasionally useful solutions:

If you're always developing on the features branch, then you can merge features to master, and then, in master, git revert local. (Where local is the tag referencing the commit where you customized the paths, etc for your local environment.)

Now you must never merge master into features, because that would merge the reverse local commit too.

In this case master becomes sort of a deployment branch, only ever receiving merges from other branches. (Ideally, only from the features branch.)

This goes downhill very easily, just add another developer to the workflow and things get really messy. Still can be worked around by using explicit merge strategies, but it's generally a pain.

思念绕指尖 2024-08-09 01:13:33

我不知道这是否可行,但是:

  1. 创建一个提交,根据配置文件的“主”版本,将它们转换为您本地需要的版本。 请注意 SHA-1。 我们将其称为 MAKE_LOCAL
  2. 创建一个提交,根据您本地版本的配置文件,将它们转换为适合 master 的版本。 请注意 SHA-1。 我们将其称为 MAKE_REMOTE
  3. 当您提交时, 使用 git hooks:
    1. gitcherry-pick MAKE_REMOTE(或使用git diffpatch
    2. 允许提交开始
    3. gitcherry-pick MAKE_LOCAL(或使用git diffpatch

我认为有一种更好的方法以这种方式转换文件,但我不记得了(如果您可以从 RubyConf 找到 shacon 的 git 演示文稿,并且可以浏览 800 张幻灯片,其中有一些很棒的示例)。

I don't know if this would work, but:

  1. Create a commit that, given the "master" version of the config files, turns them into the version you need locally. Note the SHA-1. We'll call it MAKE_LOCAL
  2. Create a commit that, given your local version of the config files, turns them into the version appropriate for master. Note the SHA-1. We'll call it MAKE_REMOTE
  3. Using git hooks, when you commit:
    1. git cherry-pick MAKE_REMOTE (or use git diff and patch)
    2. Allow the commit to commence
    3. git cherry-pick MAKE_LOCAL (or use git diff and patch)

I think there is an even better way of transforming files in this manner, but I can't recall (if you can find shacon's git presentation from RubyConf, and can wade through 800 slides, it's in there with some great examples).

嘦怹 2024-08-09 01:13:33

就我个人而言,如果我必须做这样的事情,并且由于某种原因无法重构凭证,我会再添加两个分支,最终得到类似于以下的安排:

master:您从 master 继承的 localcred: 分支的原始代码

,只添加一个补丁,将所有凭证更改为您本地所需的凭证。 此后将此分支视为只读(并可能添加一个钩子以防止意外提交)。

feature:来自 master 的分支,所有修复都放在这里(可能添加一个钩子以防止与 localcred 中的补丁合并)

local:一个分支(不是标签!)它将作为 localcred 的一个分支开始,然后在您需要运行单元测试时合并功能。 所有测试都从这里进行,但这里不进行任何开发。 另外,这个分支是一次性的,因为你可能想在feature内部进行变基,处理结果的最快方法是删除分支local,然后对其进行分支在运行测试之前再次从 localcred 合并 feature 。 这在我的工作流程中可能是一个足够常见的操作,我会构建一个别名,只需敲击几下键盘即可重复执行此操作,但我对 Git 分支的可处置性感到非常困扰,这让一些人感到害怕看着我,所以YMMV。

当您认为您的修复已准备好发布时,您可以对 feature 进行最终变基以清理历史记录,转储并重新创建 local 以进行最终测试,合并 featuremaster 中,一旦被上游接受,将 master 合并到 localcred 中,并将您的凭证补丁重新设置到顶部,然后转储并重新创建本地功能并重新玩游戏。

如果您想快速测试大量代码的微小变化,而不必每次都提交和合并,请签出 local,进行更改,直到您满意为止,提交并立即从中挑选将 local 放入 feature,然后删除并重新创建 local。

这能满足您的需求吗?

Personally, if I had to do something like this and was for whatever reason prevented from refactoring credentials as I go, I'd add two more branches, ending up with an arrangement similar to the following:

master: the original code you inherited

localcred: branch from master, and add just the one patch that changes all the credentials to what you need locally. Treat this branch as read-only hereafter (and possibly add a hook to prevent accidental commits).

feature: branch from master, and all fixes go here (and possibly add a hook to prevent merging with the patch in localcred)

local: a branch (not a tag!) that will start out as a branch of localcred, and then merge feature whenever you need to run your unit tests. All testing happens from here, but no development happens here. In addition, this branch is disposable, because you might want to rebase inside of feature, and the fastest way to deal with the result will be to delete branch local, branch it again from localcred and merge feature before running your tests. This is likely to be a common enough operation in my workflow that I'd build an alias to do it repeatedly in just a few keystrokes, but I work the hell out of the disposability of Git branches, which kind of freaks out some people who watch me, so YMMV.

When you think your fixes are ready for publication, you do your final rebase of feature to clean up the history, dump and recreate local for your final test, merge feature into master, and once that's accepted upstream, merge master into localcred and rebase your credential patch to the top, then dump and recreate local and feature and play the game all over again.

If you want to rapidly test a large set of tiny variations of code without having to commit and merge each time, checkout local, make your changes until you're happy, commit, and immediately cherry-pick from local into feature, then drop and recreate local.

Does that satisfy your needs?

酷到爆炸 2024-08-09 01:13:33

这个问题是老问题了,但我仍然没有找到好的答案。 目前我面临着同样的问题,下面是我的解决方法:

我的本地存储库中有两个分支:masterlocal_settings
master 切断 local_settings 分支后,我在那里提交了所有本地路径,没有标记,也没有试图记住它们。
在本地开发期间,我切换到 local_settings 分支,因此我可以使用本地路径运行应用程序。 但是,当需要提交时,我会存储当前状态并切换到 master 分支。 然后我弹出隐藏的变更集并将其提交到 master 中。 最后一步是切换回 local_settings,从 master 合并并继续开发。
回顾一下:我只将更改提交到 local_settings 分支,这些更改将保留在本地并且永远不会进入 master ; 并且不会从 local_settings 合并到 master

现在假设我需要对之前添加的本地路径的文件添加“良好”修改,但在 master 分支中需要“良好”修改。
当工作副本是 local_settings 的头部时,我会进行更改,将其隐藏并查看 master。 尽管我已经在 master 上,但存储保留了一个与 local_settings 相关的变更集。 git stash pop 将隐藏的变更集应用到工作副本,并最终得到相对于主版本的差异,但仅限于最近的修改,不包括之前添加的且不属于工作副本的本地路径。最近隐藏的变更集。 因此,可以在不弄乱 master 分支中的路径的情况下提交它。 然后再次从 master 合并到 local_settings

The question is an old one, but I still have not found a good answer. Currently I am facing the same issue and below is my workaround to deal with it:

There are two branches in my local repo: master and local_settings.
Having cut off the local_settings branch from master I committed there all local paths, not tagging and not trying to remember them.
During local development I am switched to the local_settings branch, so I can run an application using local paths. But when it is time to commit I stash a current state and switch to the master branch. Then I pop the stashed changeset and commit it into master. And the final step is to switch back to local_settings, merge from master and continue development.
To recap: I commit into the local_settings branch only changes that will stay locally and will never go into master; and no merges from local_settings to master.

Now let's say I need to add a "good" modification to a file with a local path added earlier, but the "good" modification is wanted in the master branch.
I do my changes when the working copy is a head for local_settings, stash it and check out master. The stash keeps a changeset, that is relative to local_settings, although I am on master already. git stash pop applies the stashed changeset to the working copy and ends up having a diff relative to master, but only with the recent modification excluding the local path that had been added earlier and was not a part of the recent stashed changeset. Hence it can be committed without messing paths in the master branch. Afterwards again merge from master to local_settings.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文