问题 GIT:在new / dirty / dev分支中提交对旧/安全分支的更改,而不检出或丢失未分段的数据


在开始开发实验之前,我创建了一个新的分支。我经常忘记(这不是问题),但现在我事先做了。
从那以后,我更新了3个文件。

  • 在2中只是实验性的变化,我不想将其提交给安全分支。
  • 在1中只是安全(次要)更改,我绝对想要安全分支。我也很好,将这些最后的更改提交给新分支(但不是)。

是否有可能 - 我确定是 - (快速)从我的(脏)工作目录中提交一些未经分级的,未提交的更改到一个旧的,安全的分支?

我唯一能想到的是切换分支(没有签出),在1个文件中提交更改并切换回来,但我不知道切换回脏分支时会发生什么变化(他们还在那里还是由于承诺他们“消失”了?)......

我确定GIT有一些美丽的东西,但GIT有这么多,我找不到完全相同的东西。
(我一直在用 这个'手册'的帮助  但我不确定那里有同样的事情。如果是(并且你愿意扫描这件事),请告诉我,所以我知道下次要更加努力。)

谢谢!现在我只需要保留一张纸,方便更改'以便稍后提交安全分支'。


1317
2017-09-19 13:01


起源



答案:


所以你的情况是

x--x--x (safe)
       \
        <a,b,c> (3 private evolutions in exp branch)

你想去

x--x--x--a (a committed only in safe banch)
       \
        b,c (b and c commited in exp branch)

你可以:

git add a
git commit -m        # in exp branch, gasp!
git stash save       # save the rest of the exp work in progress
git checkout master
git merge exp        # fast-forward merge

x--x--x--a (safe,exp)
          \
           [b,c] (stashed)

git branch -f exp HEAD~1  # reset branch exp to before 'a'
git checkout exp
git stash pop
git add -A
git commit -m "..."

x--x--x--a (a committed only in safe banch)
      \
        b,c (b and c commited in exp branch)

6
2017-09-19 13:25



由于结帐不会触及修改后的文件,所以不是没有问题 checkout master; add a; commit; checkout exp 并继续?假设这些变化没有上演。 - rlduffy
@rlduffy:应该有用。对于那些分支开关,我总是更加小心。 - VonC
如果'exp'提交超出'安全'(我无法从OP的描述中做出决定),那么你不会想要 git checkout safe && git merge exp 因为它会引入其他提交。如果你只是打算从'exp'中删除'a',我会把它挑选到'安全'而不是合并它。 - Chris Johnsen
@Chris:但挑选樱桃很糟糕,不是吗?看到 stackoverflow.com/questions/881092/... 我总是尽量避免采摘樱桃。 - VonC
@Rudie:在这种情况下,避免“git branch -f exp HEAD~1“和最后的”git add, git commit“,你会在那里。 - VonC


答案:


所以你的情况是

x--x--x (safe)
       \
        <a,b,c> (3 private evolutions in exp branch)

你想去

x--x--x--a (a committed only in safe banch)
       \
        b,c (b and c commited in exp branch)

你可以:

git add a
git commit -m        # in exp branch, gasp!
git stash save       # save the rest of the exp work in progress
git checkout master
git merge exp        # fast-forward merge

x--x--x--a (safe,exp)
          \
           [b,c] (stashed)

git branch -f exp HEAD~1  # reset branch exp to before 'a'
git checkout exp
git stash pop
git add -A
git commit -m "..."

x--x--x--a (a committed only in safe banch)
      \
        b,c (b and c commited in exp branch)

6
2017-09-19 13:25



由于结帐不会触及修改后的文件,所以不是没有问题 checkout master; add a; commit; checkout exp 并继续?假设这些变化没有上演。 - rlduffy
@rlduffy:应该有用。对于那些分支开关,我总是更加小心。 - VonC
如果'exp'提交超出'安全'(我无法从OP的描述中做出决定),那么你不会想要 git checkout safe && git merge exp 因为它会引入其他提交。如果你只是打算从'exp'中删除'a',我会把它挑选到'安全'而不是合并它。 - Chris Johnsen
@Chris:但挑选樱桃很糟糕,不是吗?看到 stackoverflow.com/questions/881092/... 我总是尽量避免采摘樱桃。 - VonC
@Rudie:在这种情况下,避免“git branch -f exp HEAD~1“和最后的”git add, git commit“,你会在那里。 - VonC


没有办法将提交添加到备用分支 git commit。有一些方法可以使用低级“管道”命令来完成您所描述的操作,但这些命令形成的接口并非设计用于交互式使用1。肯定有办法做你想做的事;根据您的更改的细节和涉及的分支的内容,它可以非常简单。

简单案例:只需使用 git checkout

切换分支时, git checkout 将保留未提交的修改或拒绝切换(除非您使用 --force--merge, 要么 --conflict)。因此,只要您未提交的更改只触摸HEAD(当前分支)和目标分支中相同的文件, git checkout 将在切换分支时将这些更改保留在索引和/或工作树中。如果未提交的更改满足此条件,则可以执行以下操作:

git checkout safe-branch
git add -- files-with-safe-changes
git commit
git checkout -

你也可以使用 git add --patch 仅暂存和提交文件中的一些更改。

在此之后,您的“安全变更”将成为“安全分支”的一部分;切换回原来的分支会“让他们落后”(记住, git checkout 切换分支时仅保留未提交的更改)。

如果您的其他更改取决于“安全更改”,您可能需要将“安全分支”合并到您的工作分支中(或者,根据您的工作流程,将您的工作分支重新绑定到“安全分支”的新提示上)。要执行此操作,您必须存储未提交的更改(因为如果存在未提交的更改,则merge和rebase将拒绝运行)。

git stash save
git merge safe-branch
git stash pop --index

如果您的其他更改不依赖于“安全更改”,那么您可能不应该 麻烦与合并或rebase。最终,您将这些分支合并在一起(例如,将它们合并到'qa'分支中以进行预发布测试),但没有理由过早合并它们。

仍然很容易,但有点风险: git checkout -m

如果是第一个 git checkout 抱怨“你有本地改变 some‑file;没有切换分支。“,这意味着你有未经改变的分支 some-file 并且“安全分支”和当前分支的提示文件不同;你需要一个不同的方法。

如果您确信更改将完全适用于版本 some‑file 那是在'安全分支',然后你可以使用 -m/--merge 告诉你的选择 git checkout 尝试调整更改,以便它们适用于'safe-branch'中的文件。如果无法完全合并,那么最终会发生合并冲突,并且可能很难恢复原始更改(这就是我称之为“冒险”的原因)。

安全: git stash + git checkout -m

由于您实际上只想将更改的子集移回“安全分支”,因此最好只关注这些更改。一种方法是使用 git stash 暂时保存当前更改,这样您就不必将它们全部拖回'safe-branch'(稍后将其中的一些/大部分拖回工作分支)。

git stash save
git checkout stash -- files-with-save-changes
git checkout -m safe-branch
git commit
git checkout -
git stash pop --index

其他变化是可能的。您可以使用 git checkout -p stash -- files 只挑选出那些文件中的一些变化。如果索引中没有分阶段的更改,那么您可以先进行“安全更改”, git add -- files (再次,任选地 -p) , 使用 git stash save --keep-index,切换分支(与合并),然后提交(即替换 git checkout stash -- files 与预先安排的“安全变化”和 git stash --keep-index)。

在这种情况下我考虑 git checkout -m 为了安全,因为我们使用过 git stash 保留当前更改的副本;如果三方合并尝试导致无望的混乱,那么你可以轻易放弃将“安全更改”置于“安全分支”并重新开始工作的想法:切换回原始分支并弹出存储(git checkout -f - && git stash pop)。

同样,如果您的其他更改取决于“安全更改”,那么您将需要合并或重新绑定。您可以在弹出存储之前执行此操作(因为您需要一个干净的索引和工作树来执行合并/ rebase)。

如果你不打算立即将你的工作分支与“安全分支”合并(或将其重新绑定),那么你可能想要在弹出存储后撤消“安全更改”(“安全更改”是保存在原始存储中,您可能不希望最终提交在两个不同分支中从头开始进行相同更改的提交2)。一旦你弹出了藏匿处,请使用 git checkout -- files-with-safe-changes 将这些文件还原为工作分支顶端的版本。


1 “管道”界面设计用于脚本。在命令行上直接使用它们会很麻烦。早期版本 git commit(以及大多数其他Git命令)是基于此接口的shell脚本。它们今天仍然可以编写为shell脚本,但C版本通常要快得多。 提交备用分支所需的步骤是:
根据“安全分支”顶端的树设置备用索引,
使用“安全”更改来更新索引(如果无法干净地应用更改,那么有一个工作树让用户解决冲突是很好的),
将索引写为树对象,
创建一个指向新树的新提交对象,并将当前提示“safe branch”作为其父级,
更新“安全分支”引用指向新提交。

2 在两个分支中提交“安全更改”没有任何技术上的错误,但通常最好确保每个更改仅来自一个地方。


7
2017-09-20 06:23



我喜欢管道和细节。 +1 - VonC


由于GIT上的分支操作非常便宜,我只想使用以下工作流程:

  • 从“实验”分支创建一个新分支
  • 提交所需的任何非分段文件
  • 回到主人那里
  • cherrypick 你的“实验”和刚刚在步骤1中创建的新分支提交的任何内容。
  • 回到“实验”分支

0
2017-09-19 14:35