152

这个问题不仅与如何完成此任务有关,还与 Git 的好坏习惯有关。

考虑到在本地,我在 master 分支上完成了大部分工作,但我创建了一个名为“topical_xFeature”的主题分支。在处理“topical_xFeature”和来回切换在master分支上做其他工作的过程中,结果我在“topical_xFeature”分支上做了不止一次的提交,但是在每次提交之间,我都没有做任何事情推。

首先,您会考虑这种不好的做法吗?每次推送坚持每个分支一次提交不是更明智吗?在什么情况下,在推送之前在一个分支上进行多次提交会更好?

其次,我应该如何最好地完成将 topical_xFeature 分支上的多个提交带入主分支进行推送?不担心它并只是在推送多个提交的地方进行推送是否令人讨厌,或者以某种方式将提交合并为一个然后推送是否不那么烦人?再次,如何做到这一点?

4

6 回答 6

153

对于您的第一个问题,不,一次推送多个提交没有任何问题。很多时候,你可能想把你的工作分解成几个小的、合乎逻辑的提交,但只有当你觉得整个系列都准备好了时才把它们推上来。或者您可能在断开连接时在本地进行多次提交,并且在再次连接后将它们全部推送。没有理由将自己限制为每次推送一次提交。

我通常发现让每个提交保持一个单一的、合乎逻辑的、连贯的更改是一个好主意,其中包括它需要工作的所有内容(因此,它不会使您的代码处于损坏状态)。如果您有两个提交,但如果您只应用第一个提交,它们会导致代码被破坏,那么将第二个提交压缩到第一个提交可能是个好主意。但是,如果您有两个提交,每个提交都进行了合理的更改,那么将它们作为单独的提交推送就可以了。

如果您确实想将多个提交压缩在一起,您可以使用git rebase -i. 如果你在 branch topical_xFeature,你会跑git rebase -i master。这将打开一个编辑器窗口,其中列出了一堆以 . 为前缀的提交pick。您可以将除第一个之外的所有更改更改为squash,这将告诉 Git 保留所有这些更改,但将它们压缩到第一个提交中。完成后,签出master并合并到您的功能分支中:

git checkout topical_xFeature
git rebase -i master
git checkout master
git merge topical_xFeature

或者,如果您只想将所有内容压缩topical_xFeature到中master,则可以执行以下操作:

git checkout master
git merge --squash topical_xFeature
git commit

你选择哪一个取决于你。一般来说,我不会担心有多个较小的提交,但有时你不想为额外的小提交而烦恼,所以你只需将它们压缩成一个。

于 2011-04-19T19:52:37.087 回答
75

这是我在推送代码之前通常遵循的将多个提交合并为一个提交的方式。

为此,我建议您使用GIT 提供的' squash ' 概念。

请按照以下步骤操作。

1) git rebase -i master(代替master你也可以使用特定的提交)

打开 rebase 交互式编辑器,它将显示您的所有提交。基本上,您需要确定要合并到单个提交中的提交。

想象一下这些是您的提交,并在编辑器中显示了类似的内容。

pick f7f3f6d changed my name a bit    
pick 310154e updated README formatting and added blame   
pick a5f4a0d added cat-file  

请务必注意,这些提交的列出顺序与您通常使用 log 命令看到的顺序相反。意味着,较旧的提交将首先显示。

2)将“pick”更改为“squash”以获取上次提交的更改。如下所示。这样做,您的最后 2 个提交将与第一个合并。

pick f7f3f6d changed my name a bit         
squash 310154e updated README formatting and added blame   
squash a5f4a0d added cat-file

如果您有很多提交要组合,您也可以使用简短形式:

p f7f3f6d changed my name a bit         
s 310154e updated README formatting and added blame   
s a5f4a0d added cat-file

对于编辑使用“i”,它将启用编辑器进行插入。请记住,最重要(最旧)的提交不能被压缩,因为没有以前的提交可以结合。所以它必须被挑选或'p'。使用“Esc”退出插入模式。

3) 现在,使用以下命令 保存编辑器。:wq

当您保存它时,您有一个提交,其中介绍了所有三个先前提交的更改。

希望这会帮助你。

于 2012-11-28T11:58:06.357 回答
12

首先:没有什么告诉您每次推送每个分支只有一个提交:推送是一种发布机制,允许您在远程仓库上发布本地历史记录(即提交的集合)。

第二git merge --no-ff topical_xFeature在 push 之前,将在 master 上记录您的主题工作的单个提交master
(这样,您可以继续topical_xFeature进行进一步的演变,您可以master在下一次合并时将其记录为单个新提交 --no-ff。
如果摆脱topical_xFeature是目标,那么git merge --squash是正确的选择,如Brian Campbell中所述的答案。)

于 2011-04-19T19:51:40.703 回答
12

切换到主分支并确保您是最新的。

git checkout master

git fetch这可能是必要的(取决于您的 git 配置)以接收有关 origin/master 的更新

git pull

将功能分支合并到主分支。

git merge feature_branch

将主分支重置为原始状态。

git reset origin/master

Git 现在将所有更改视为非暂存更改。我们可以将这些更改添加为一次提交。添加 . 还将添加未跟踪的文件。

git add --all

git commit

参考:https ://makandracards.com/makandra/527-squash-several-git-commits-into-a-single-commit

于 2016-07-25T19:36:34.400 回答
10
  1. 首先选择您希望所有内容都在哪个提交之后。

    git reflog
    5976f2b HEAD@{0}: commit: Fix conflicts
    80e85a1 HEAD@{1}: commit: Add feature
    b860ddb HEAD@{2}: commit: Add something
    
  2. 重置为您选择的头部(我已选择HEAD@{2}

    git reset b860ddb --soft
    
  3. git status(只是要确定)

  4. 添加你的新提交

    git commit -m "Add new commit"
    

注意:HEAD@{0}&HEAD@{1}现在合并为 1 个提交,这也可以用于多个提交。

git reflog应该再次显示:

git reflog
5976f2b HEAD@{0}: commit: Add new commit
b860ddb HEAD@{1}: commit: Add something
于 2018-11-16T10:48:34.807 回答
0

将多个提交自动合并为一个的工具

正如Kondal Kolipaka 所说。使用“git rebase -i”

“git rebase”的逻辑

使用“git rebase -i”时,git在当前.git/rebase-merge目录下生成git-rebase-todo文件,然后调用git编辑器让用户编辑git-rebase-todo文件进行处理。所以工具需要满足:

  1. 将git编辑器修改为我们提供的工具;
  2. 该工具处理 git-rebase-todo 文件。

修改默认git编辑器

git config core.editor #show current default git editor
git config --local --replace-all  core.editor NEW_EDITOR # set the local branch using NEW_EDITOR as git editor

因此,该工具需要更改 git 编辑器并处理 git-rebase-todo 文件。下面使用python的工具:

#!/usr/bin/env python3
#encoding: UTF-8

import os
import sys

def change_editor(current_file):
    os.system("git config --local --replace-all  core.editor " + current_file) # Set current_file as git editor
    os.system("git rebase -i") # execute the "git rebase -i" and will invoke the python file later with git-rebase-todo file as argument
    os.system("git config --local --replace-all core.editor vim") # after work reset the git editor to default

def rebase_commits(todo_file):
    with open(todo_file, "r+") as f:
        contents = f.read() # read git-rebase-todo's content
        contents = contents.split("\n")
        first_commit = True
        f.truncate()
        f.seek(0)
        for content in contents:
            if content.startswith("pick"):
                if first_commit:
                    first_commit = False
                else:
                    content = content.replace("pick", "squash") # replace the pick to squash except for the first pick
            f.write(content + "\n")

def main(args):
    if len(args) == 2:
        rebase_commits(args[1]) # process the git-rebase-todo
    else:
        change_editor(os.path.abspath(args[0])) # set git editor

if __name__ == "__main__":
    main(sys.argv)

参考:https ://liwugang.github.io/2019/12/30/git_commits_en.html

于 2019-12-30T10:16:03.613 回答