Git: Commit to multiple branches at the same time

2019-01-10 21:06发布

问题:

I want to make a single commit on different branches at the same time, since, in my project, I have different branches for different clients.

Say, I have made a new feature commit on Branch A. Can I also make this commit on Branch B, Branch C and D at the same time as well? Is there any short cut command for this? This is very troublesome to checkout one branch, and cherrypick the commit everytime, sometime, if the commit is necessary on many branches, it would be a nightmare for me.

Any simple bash script for this operation?

There is a similar question in there. but rebasing is not that I want.

回答1:

The cherry-pick feature will do the job and will only apply the last commit:

Considering branches A, B

git checkout A
git commit -m "Fixed the bug x"
git checkout B
git cherry-pick A

hope this helps!



回答2:

Since there is no typical answer for my question, I have written a simple script to automate this process. Feel free to comment this code.

#!/bin/bash
BRANCHES=(
master_branch
develop_branch
testing_branch
)
ORIGINALBRANCH=`git status | head -n1 | cut -c13-`
git commit -m $1
CHERRYCOMMIT=`git log -n1 | head -n1 | cut -c8-`
for BRANCH in "${BRANCHES[@]}";
do
    git stash;
    git checkout $BRANCH;
    git cherry-pick $CHERRYCOMMIT;
    git checkout $ORIGINALBRANCH;
    git stash pop;
done


回答3:

Something you might want to have a look at: git stash your changes, git commit, git checkout another branch, git stash apply, git commit, etc.

See the man pages.



回答4:

I think you can write a post-commit hook to merge or cherry-pick with the other branches. But it will of course not be a single commit.

Hook will automatize what you want to achieve.

http://git-scm.com/docs/githooks



回答5:

Maybe this will help some people,

I used the "Kit Ho"s method above and added it to .gitconfig as alias.

; commitall commits to the current branch as well the all the branches mentionedin BRANCHES array var as shown below.
commitall = "!f()  { \
    BRANCHES=( \
    branches1 \
    branches2 \
    );  \
    usage() \
    { \
        echo \"Usage: git commitall -m 'JIRA: BRANCHNAME:<comment to check in>'\"; \
        exit 1; \
    }; \
    OPTIND=1; \
    DFNs=\"\"; \
    while getopts \"h:m:\" opt; \
    do \
        case \"$opt\" in \
            h) \
            usage; \
            ;; \
            m)  export Comment=$OPTARG; \
            ;; \
        esac; \
    done; \
    ORIGINALBRANCH=`git symbolic-ref HEAD|cut -d/ -f3- `; \
    echo \"Current branch is $ORIGINALBRANCH \" ; \
    echo $Comment | grep \"^JIRA: $ORIGINALBRANCH:\"  > /dev/null 2>&1 ; \
    if [ $? -ne 0 ]; then \
        usage; \
    fi; \
    LOGMSG=`git log -1 --pretty=%B --grep=\"JIRA: $ORIGINALBRANCH:\" `; \
    MSG='commit first time in this branch is a success' ; \
    if [ \"$LOGMSG\" == \"\" ]; then \
        git commit -m \"$Comment\"; \
    else \
        git commit -a --amend -C HEAD; \
        MSG='commit with amend succeeded' ; \
    fi; \
    if [ $? -ne 0 ]; then \
        echo \"git commit failed!\"; \
        exit 1; \
    else \
        echo \"$MSG\" ; \
    fi; \
    CHERRYCOMMIT=`git log -n1 | head -n 1 | cut -c8- ` ; \
    if [ \"$CHERRYCOMMIT\" == \"\" ]; then \
        echo \"'git log -n1 | head -n 1 | cut -c8-' no commits for this branch\"; \
        exit 1; \
    fi; \
    echo \"found this commit -> $CHERRYCOMMIT for current branch\"; \
    stashes=`git stash list | grep \"WIP on $ORIGINALBRANCH\" ` ; \
    for BRANCH in \"${BRANCHES[@]}\"; do \
        if [ \"$stashes\" ]; then \
            git stash; \
        fi;\
        git checkout $BRANCH; \
        if [ $? -ne 0 ]; then \
            echo \"git checkout $BRANCH failed!\"; \
            exit 1; \
        fi; \
        git cherry-pick $CHERRYCOMMIT; \
        git checkout $ORIGINALBRANCH; \
        if [ \"$stashes\" ]; then \
            git stash pop; \
        fi; \
    done; \
    }; \
f"


回答6:

No, I don't think you can do this. Your best option would be to commit to one of your branches (or a master branch) and then either merge the commit into the others one by one, or cherry-pick the commit into each of the other branches.



回答7:

I don't think there is a way to that. Maybe you need to write a bash script for it or make a different repo for different branches.



回答8:

You can also do nice automation using some Python:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Populate specified or latest commit across all branches in the repository.

"""

import os
import re
import sys
import sh


git = sh.git.bake(_cwd=os.curdir)

if len(sys.argv) > 1:
    if '-h' in sys.argv or '--help' in sys.argv:
        print('Usage: git-populate.py [commit] [-h|--help]')
        sys.exit(0)
    else:
        commit = sys.argv[1]
else:
    # By default choose latest commit.
    git_log = git('--no-pager', 'log', '-n', '1', '--no-color').stdout
    commit = re.search(r'[a-f0-9]{40}', git_log).group()

print('Commit to be populated: {0:.6s}'.format(commit))

git_branch = git.branch('-a', '--no-color').stdout
source_branch = re.search(r'\*\s(\w+)$', git_branch, re.MULTILINE).group(1)
print('Source branch: {0}'.format(source_branch))

branches = re.findall(r'remotes/\w+/([\w-]+)$', git_branch, re.MULTILINE)
# Exclude current branch from target branches.
branches = [i for i in branches if i != source_branch]
print('Target branches: {0}'.format(branches))


print('Stashing local changes')
git_stash = git.stash().stdout

for branch in branches:
    print('Ading commit {0:.6s} to branch {1}'.format(commit, branch))
    git.checkout(branch)
    try:
        result = git('cherry-pick', commit)
    except sh.ErrorReturnCode_1:
        # Ignore diplicate cherry pick and discard changes.
        git.reset()


git.checkout(source_branch)
print('Return to branch {0}'.format(source_branch))

if not git_stash.startswith('No local changes to save'):
    print('Restoring local changes')
    git.stash.pop()


回答9:

It might depend on how you are going to push your commits. I mean you always have the chance to push into multiple branches with one git push command.

Do the following .git/config settings to ensure that the master branch will always be pushed to the foobar branch, too.

[remote "origin"]
        url = git@github.com:mgerhardy/example.git
        fetch = +refs/heads/*:refs/remotes/origin/*
        push = refs/heads/master:refs/heads/master
        push = refs/heads/master:refs/heads/foobar 

But the problem here might be that if these two branches diverged, you can't push to them. You still could reorganize your code that you client detection is based on the branch you are building. That way you could maintain dozens of clients and always keep all the branches in sync.



回答10:

To simultaneously push to multiple branches, simply install & configure SourceTree and select "Push" and the branches you wish to deploy to.



标签: git branch