【发布时间】:2011-08-05 17:40:46
【问题描述】:
我一直在我的本地 master 分支上开发一个尚未准备好投入生产的新功能。但是,我刚刚在我的实时应用程序中发现了一个单独的错误,所以我很快在本地修复了它。但是,我想将这个 bug 修复推送到我的远程 master 分支,而不是推送我一直在开发的这个新功能。我该怎么做?
【问题讨论】:
标签: git version-control github
我一直在我的本地 master 分支上开发一个尚未准备好投入生产的新功能。但是,我刚刚在我的实时应用程序中发现了一个单独的错误,所以我很快在本地修复了它。但是,我想将这个 bug 修复推送到我的远程 master 分支,而不是推送我一直在开发的这个新功能。我该怎么做?
【问题讨论】:
标签: git version-control github
通过时光倒流并为您的开发使用适当的分支模型。停止在生产分支上工作并开始使用功能分支。您描述的情况正是您应该使用分支的原因:能够将开发工作搁置一旁,签出 master,执行错误修复并返回您的开发分支。忽略分支,你就忽略了很多让 Git 很棒的东西。
更实际的是,您可以重新排序提交,将本地 master 指向您要推送、推送的那个,然后将您的开发提交作为新分支签出。
如果你的提交历史是这样的:
A <-- (master) bug fix, you want to push this
B <-- you don't want to push this
C <-- (origin/master)
您可以使用git rebase -i HEAD~2 重新排序最后两个提交,使其看起来像这样(只需在出现的编辑器中切换行的顺序):
B <-- (master) you don't want to push this
A <-- bug fix
C <-- (origin/master)
记下B 的 SHA1,您将暂时将其从主分支中删除。记录 SHA1 后,您可以使用 git update-ref refs/heads/master [SHA1 of A],这会导致
A <-- (master) bug fix
C <-- (origin/master)
您现在可以git push 将A 合并到origin/master 并将结果发送到origin。
最后,为了恢复您的开发工作(提交 B),创建一个新的开发分支(您应该首先完成)指向您的 B 提交:git branch development [SHA1 of B]
您的存储库现在将如下所示:
B <-- (development) you don't want to push this
A <-- (master,origin/master) bug fix
C <-- where origin/master *was* before pushing
当您准备好将该开发工作合并到 master 中时,您可以:
git checkout master
git merge development
【讨论】:
git log --pretty=oneline --decorate=full 来获得类似于我上面使用的伪格式的日志格式。您必须将真正的 SHA1 哈希替换为 A、B、C,它们只是占位符。我没有向您展示的唯一步骤是变基,这真的很简单。你的编辑器应该包含两行;只需颠倒它们的顺序。
A - (HEAD, refs/remotes/origin/master, refs B - apointer css C - added play image
你得到了一些很好的答案,但有点复杂。我希望能简化一点。以下假设您仅在一次提交中提交了您的修复,最后一次在您的本地 master 分支上。
git branch new-feature master~1
git checkout -b bugfix
git branch -f master origin/master
git rebase --onto master new-feature
这会将你的新特性的工作放在它自己的名为 new-feature 的分支上,将你的错误修复放在它自己的名为 bugfix 的单独分支上,重置你的主分支以匹配你上次推送的内容,然后变基以删除新特性从您的错误修复分支工作。
这就是您通常希望您的分支在这一点上看到的方式。我是如何得到它的还不是很重要,因为将来您只需使用 git checkout -b another-new-feature master 创建一个新分支,然后再开始处理另一个新功能或错误修复。
现在,仅推送您的错误修复,只需执行以下操作:
git checkout master
git merge bugfix
git push
然后要继续开发新功能,请执行以下操作:
git checkout new-feature
如果您想在新功能分支中包含错误修复,请执行以下操作:
git rebase master
否则,它将在您进行下一次推送之前被合并。当您准备好推送新功能时,只需执行与 bugfix 分支相同的合并,但使用 new-feature 分支。
【讨论】:
你可以只将你想要提交的文件添加到暂存区,然后将它们提交到你的分支上。然后创建另一个分支并提交您不想提交的文件。然后你只需推送带有修复的分支。
当您提交时,它只会将文件添加到您的暂存区,以便您可以在一个分支上提交您的一部分工作,而在另一个分支上提交您的另一部分工作。
如果您想在有未提交的文件时切换分支,您可以将它们添加到暂存区,签出另一个分支,然后将暂存区提交到另一个分支。
如果你做了一些你想要推送的更改和一些你不想推送到同一个文件的更改,那么它会变得更加复杂。
【讨论】:
Here 是遍历命令的答案。我写了一个类似的问题。
它的基础是,将你的错误修复移动到一个单独的分支,并将该分支与你的 master 和 topic 合并。与其在第 4 步中修复更改,不如使用git cherry-pick bugFixSHA,而不是从您的功能分支中删除提交。
【讨论】:
我今天没有正常的测试机器,所以语法来自记忆。如果有,请有人给我打电话。
不确定 ascii 艺术是否可行:
[origin/master] hash_0 blah
[new_feature ] hash_1 feature progress: more widgets!
hash_2 feature progress: less widgets, what was I thinking?
hash_3 bug fix: fixed memory management
命令可能如下所示:
我假设我们正在使用分支新功能。
git rebase -i origin/master
此时你会得到一个提交列表:
hash_1 feature progress: more widgets!
hash_2 feature progress: less widgets, what was I thinking?
hash_3 bug fix: fixed memory management
您将重新排序提交:
hash_3 bug fix: fixed memory management
hash_1 feature progress: more widgets!
hash_2 feature progress: less widgets, what was I thinking?
如果您在此处遇到错误,那么您的错误修复取决于您的新功能更改。很难就此给出建议。在这种情况下,通常我只是在 origin/master 中重新创建错误修复。
[origin/master] hash_0 blah
[new_feature ] hash_3 bug fix: fixed memory management
hash_1 feature progress: more widgets!
hash_2 feature progress: less widgets, what was I thinking?
我们的顺序应该是正确的,我通常会打开 gitk 并直观地验证它是否符合我的预期。您希望在 origin/master 之后的下一次提交中看到您的错误修复。
git checkout hash_3
git branch some_bug_fix
git push origin some_bug_fix:master
git checkout new_feature
git rebase origin/master
最后一次变基可能是多余的。
[origin/master] hash_0 blah
hash_3 bug fix: fixed memory management
[new_feature ] hash_1 feature progress: more widgets!
hash_2 feature progress: less widgets, what was I thinking?
或者,
不过,这会复制您本地分支中的代码,因此您需要将其从本地分支中删除并基于 master 进行 rebase。
我总是用 gitk 来挑选,帮不了你的命令。
【讨论】:
使用 Git 的“交互式添加”功能仅将错误修复添加到“暂存区”。然后,您可以隐藏其余的更改,测试错误修复以确保其有效,然后自行提交。
以下是一个示例演练。起初它可能看起来很长而且很复杂,但实际上很容易。一旦你这样做了几次,它就会成为第二天性,你可能会发现自己经常使用这种技术。
在此示例中,bar.c 和 foo.c 都有更改,但只有 foo.c 具有与错误修复相关的更改。首先,我使用git add -i 以交互方式仅添加错误修复:
test(master *)$ git add -i
staged unstaged path
1: unchanged +1/-0 bar.c
2: unchanged +1/-1 foo.c
*** Commands ***
1: status 2: update 3: revert 4: add untracked
5: patch 6: diff 7: quit 8: help
我选择“p”选项告诉 Git 我希望选择单独的“补丁”添加到“暂存区”:
What now> p
staged unstaged path
1: unchanged +1/-0 bar.c
2: unchanged +1/-1 foo.c
接下来我输入2 告诉它我想从foo.c 中选择单个补丁:
Patch update>> 2
staged unstaged path
1: unchanged +1/-0 bar.c
* 2: unchanged +1/-1 foo.c
由于foo.c 是唯一一个有我想要添加到“暂存区”的补丁的文件,现在我已经完成了文件选择,所以我只需在提示符处按 Enter:
Patch update>>
Next Git 向我显示“foo.c”中的各个补丁,并询问我是否要将它们添加到索引中。在这个例子中,只有一个变化,所以我暂存它:
diff --git a/foo.c b/foo.c
index 7bc741e..ec7ddfc 100644
--- a/foo.c
+++ b/foo.c
@@ -1 +1 @@
-Here is foo.c; it has a bug.
+Here is foo.c.
Stage this hunk [y,n,q,a,d,/,e,?]? y
我现在已经上演了作为错误修复一部分的所有内容。于是我退出了“交互添加”模式:
*** Commands ***
1: status 2: update 3: revert 4: add untracked
5: patch 6: diff 7: quit 8: help
What now> q
Bye.
注意状态。 foo.c 已将更改添加到“暂存区”,但“bar.c”没有:
test(master *+)$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: foo.c
#
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: bar.c
#
接下来我告诉 Git 隐藏我的工作,但要保留我添加到“暂存区”(a.k.a. “索引”):
test(master *+)$ git stash --keep-index
Saved working directory and index state WIP on master: ba84dec Adding bar.c with new "bar feature"
HEAD is now at ba84dec Adding bar.c with new "bar feature"
test(master +$)$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: foo.c
#
现在请注意,我的工作树中唯一的东西是错误修复,它已准备好在下一次提交中提交。现在我可以在提交之前对错误修复进行任何我想做的测试。
当我对我的更改感到满意时,我现在可以提交错误修复:
test(master +$)$ git commit
[master 79acd00] Commit bugfix for foo.c
1 files changed, 1 insertions(+), 1 deletions(-)
现在我将存储区弹出以将我在 bar.c 中所做的其他工作返回到我的工作树中:
test(master $)$ git stash pop
# On branch master
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: bar.c
#
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (d306d098a272335ed31c14f07cf57e62ffc13151)
我已经完成了。我现在可以推送到远程存储库,它会得到错误修复,而我的其余工作仍在我的工作树中并且不会被推送。
【讨论】: