A Tool To Automate Multiple Commits Into One

If we have multiple commits or some patches from the software supplier, and want to synthesize one commit and retain the commit message of each one, someone knows that git rebase -i can be used to solve it, but this method requires manual for operations. If we have a lot to do, it will took a lot of time. Here I will introduce a tool which to automate multiple commits into one.

The logic of “git rebase”

When using “git rebase -i”, git generates the git-rebase-todo file in the current .git/rebase-merge directory, and then invokes the git editor to let users edit the git-rebase-todo file for processing. So the tool needs to meet:

  • Modify the git editor to the tool which we provided;
  • The tool processes the git-rebase-todo file.

Modify the default git editor

Git provides the config command to view the configuration and modify the configuration, and the editor can also be set in this way.


    git config core.editor  #View the currently used editor
    git config --local --replace-all core.editor NEW_EDITOR # Set the editor to NEW_EDITOR

Process the git-rebase-todo file

graph

Now we have three commits and use “git rebase -i”, git uses the git editor to open the git-rebase-todo file, as follows:


pick 310b091 First commit
pick 757b5e5 The second commit
pick c5ca4a9 The third commit

# Rebase a7e56eb..c5ca4a9 onto a7e56eb (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -c <commit> to reword the commit message.
#
# 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

There are three commit in the above. We need to merge the next two commit into the first one. As you can see from the comments, squash merges the commit into the previous commit. Therefore, you need to change the last two pick to squash, which is as follows:

pick 310b091 First commit
squash 757b5e5 The second commit
squash c5ca4a9 The third commit

The tool

The tool is a python script.

#!/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 to this tool

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

Result

After to execute the tool, it merges above three commits into one and retains the messages of them.

On branch master
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean

Just one commit.


commit e1ac798cfe0162f38c908d47720fcadc2d3b0ddd (HEAD -> master)
Author: liwugang <liwugang@163.com>
Date:   Mon Dec 30 22:04:56 2019 +0800

    First commit
    
    The first commit message
    
    Signed-off-by: liwugang <liwugang@163.com>
    
    The second commit
    
    The second commit message
    
    Signed-off-by: liwugang <liwugang@163.com>
    
    The third commit
    
    The third commit message
    
    Signed-off-by: liwugang <liwugang@163.com>

The three commit messages are preserved.

打赏

取消

感谢您的支持!

扫码支持
扫码支持
扫码打赏,您说多少就多少

打开支付宝或微信扫一扫,即可进行扫码打赏哦