如何推送部分本地git提交?

200

假设我有5个本地提交,我想只将其中的2个推送到集中式仓库(使用类似SVN的工作流程)。我该怎么做?

以下方式无效:

git checkout HEAD~3  #set head to three commits ago
git push #attempt push from that head

这最终会推送所有五个本地提交。

我想我可以使用git reset来撤销我的提交,然后使用git stash,接着再使用git push --,但是我已经编写了提交信息并组织好文件,我不想重做它们。

我觉得在push或reset时传递一些标志可能会起作用。

如果有帮助的话,这是我的git配置。

[ramanujan:~/myrepo/.git]$cat config 
[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
[remote "origin"]
        url = ssh://server/git/myrepo.git
        fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
        remote = origin
        merge = refs/heads/master
5个回答

241
假设您的提交位于主分支上,并且您想将它们推送到远程主分支:
$ git push origin master~3:master

如果您正在使用git-svn:

$ git svn dcommit master~3
在git-svn情况下,您也可以使用HEAD~3,因为它期望一个提交。在纯git的情况下,您需要使用分支名称,因为HEAD在refspec中不能正确评估。
您还可以采用较长的方法:
$ git checkout -b tocommit HEAD~3
$ git push origin tocommit:master

如果你经常这样工作,那么就应该考虑在一个独立的分支中完成你的工作。然后你可以像这样做:

$ git checkout master
$ git merge working~3
$ git push origin master:master

请注意,“origin master:master”部分对于您的设置可能是可选的。


20
请注意:您不必使用 master~3。任何涉及所需“到达”提交的参考都是有效的,例如 HEAD~3HEAD~~~,或特定的 SHA 或标记,该标记标记了该提交。 - Kaz
2
好东西。不过要提醒一下:这些示例会推送到 origin master 分支。如果你复制并粘贴这个解决方案,可能会意外更新主分支。(当然,在执行 git push 命令之前,你应该始终小心并仔细检查你的命令...) - nofinator
似乎这会推送提交,但不会在远程添加分支。 - Nateowami
@Nateowami,你需要为参考规范的远程侧指定其他内容,例如 git push origin tocommit:newbramch - Ryan Graham
我明白了。分支名称已经存在于本地;我想它不喜欢那样。但是远程仓库还没有这个分支名称。 - Nateowami

46

简短回答:

git push <最新提交SHA1直到您想要发布的提交>

示例:

git push origin fc47b2:master

git push origin HEAD~2:main

详细回答:

提交被链接在一起,通过父子机制形成链。因此,推送一个提交实际上也会将所有父提交推送到尚未知道的远程位置。当您git push当前提交时,这个过程是隐式完成的:所有以前的提交也被推送了,因为这个命令相当于git push HEAD

因此,问题可能被重写为如何推送特定的提交,例如HEAD~2.

如果您需要推送的提交不连续,请在具体推送之前使用git rebase -i重新排序它们。


1
这个不行,对吧?你会得到“致命错误:'HEAD~2'似乎不是一个git仓库”的提示。 - Mark VY
3
应该是 git push origin fc47b2:main - Maxim Kamalov
1
@MarkVY 更新以明确远程存储库和远程分支。 - Tim

16

默认情况下,git-push会推送所有分支。 当你这样做时:

 git checkout HEAD~3  #set head to three commits ago
 git push #attempt push from that head

你切换到了一个分离的HEAD(并未在任何分支上),然后你将包括本地master(仍在原处)在内的所有分支推送到远程master。
手动解决方案是:
 git push origin HEAD:master

如果你觉得默认将所有分支推送的行为令人困惑(并且很危险!),请将以下内容添加到你的 ~/.gitconfig 文件中:
 [remote.origin]
    push = HEAD

只有你当前所在的分支才会被推送。在你的例子中(一个游离的 HEAD),你将收到此错误消息,而不是意外地推送错误的提交:

 error: unable to push to unqualified destination: HEAD

16

我的做法是在一个名为“work”的本地分支上工作。这个分支包含所有的临时提交(比如解决方法、私有构建选项等),我不打算将它们推到上游仓库。我在这个分支上工作,然后当我想要提交时,我切换到主分支,挑选出我想要提交的适当提交,然后推送主分支。

从上游仓库拉取更改到我的主分支后,我使用git checkout workgit rebase master命令。这会将我所有的本地更改重写到历史记录的末尾。

实际上,在这个工作流程中,我正在使用git svn。因此,我的“push”操作涉及到git svn dcommit命令。我还使用了一个很好的文本模式gui仓库查看器tig来将合适的提交挑选到主分支。


使用 git svn dcommit 命令,您可以指定要提交的 commit,因此使用 git-svn 实现所需的效果非常简单。 - Ryan Graham
1
这种方法存在一些缺点(在此处概述https://dev59.com/aHNA5IYBdhLWcg3wpfmu#881014)。一个好的替代方案是为你正在开发的每个功能创建分支和一个`work`分支。然后,将特定的分支合并到`master`中,以便不会丢失它们的历史记录。当使用`work`时,将所有分支合并到其中。这会增加一些额外的工作量,但在某些情况下可能是值得的。 - Hudon

11

1) 如果你需要,可以使用 "git rebase" 对提交的顺序进行重新排序。

git rebase -i

这个命令会在你的编辑器中显示类似于这样的内容(我正在使用vim)。
pick 4791291 commitA
pick a2bdfbd commitB
pick c3d4961 commitC
pick aa1cefc commitD
pick 9781434 commitE

# Rebase ..............
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out




^G Get Help         ^O WriteOut         ^R Read File        ^Y Prev Page                ^K Cut Text         ^C Cur Pos
^X Exit             ^J Justify          ^W Where Is         ^V Next Page            ^U UnCut Text       ^T To Spell

2)通过简单的剪切粘贴,按照您的选择重新排序提交。假设新的顺序是:

pick 9781434 commitE

pick c3d4961 commitC

pick 4791291 commitA

pick aa1cefc commitD

pick a2bdfbd commitB

在编辑器中进行这些更改,然后按下 ctrl+O(写出)。

或者您也可以使用

git rebase -i HEAD~<commitNumber>

您可以通过以下方式检查新的序列:
git log

3) 现在使用

git push <remoteName> <commit SHA>:<remoteBranchName>

如果远程(origin)只有一个分支并且本地(master)也只有一个分支,那么只需使用以下命令:

git push <commit SHA>
git push aa1cefc

这将推送提交B和提交D。

您的顺序有误。正如描述中所说,“它们从上到下执行”。 因此,当您从2)获得新的顺序并执行“git push <commitD sha>”时, 这实际上会推送commitE、commitC、commitA和commitD。 否则,感谢您的帮助。 - Pavol

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接