git commit to all branches

12,762

Solution 1

No—or strictly speaking, "yes, but it's just as much work any other way". New commits are always added to "the current branch", even if that's a "detached HEAD". (Below, I'll show a way of doing this with the "detached HEAD" state, although if you're adding commits to existing branch-tips, that's more work than just checking them out.)

Assuming you have something like this:

A-B-C          <-- br1
   \
    D   F      <-- br2
     \ /
      E
       \
        G      <-- br3

and you have some fix X that must be applied on top of C, F, and G to give:

A-B-C-X1       <-- br1
   \
    D   F-X2   <-- br2
     \ /
      E
       \
        G-X3   <-- br3

(note that all 3 Xn commits are different commits as they have different parents), then you must add "patch X" to commit C, and then add "patch X" to commit F, and then add "patch X" to commit G.

Note that there is no guarantee that, e.g., the change from C to X1 exactly matches the change from F to X2 here, either. You may construct any of the three Xn commits first, in the usual way. Then (as in your question) you just move to the other branches and git cherry-pick to create (and possibly resolve conflicts in) the other X-es. But you need to be "on" those branches to add commits to them, so:

$ git checkout br1
... make fix, "git add", "git commit" to create X1

$ git checkout br2
Switched to branch 'br2'.
$ git cherry-pick br1    # take diff of `C`-vs-`X1`, apply to `F` to make `X2`
... etc

Repeat for all branches to which the patch must be copied (and, if necessary, modified to fit that branch).


There are alternatives to doing this. For instance, suppose branch br1 is actually OK, and what you've discovered is that commit E is broken and needs to be fixed, affecting commits F and G. Suppose further that either no one else has commits F and G—or, you're willing to force those who do have those two commits, to do a bunch of work to recover from what you are about to do. In that case, you can check out commit D and make a new commit, E', that comes off of D. Let's draw the starting point, leaving out A through C. We'll git checkout D (by its SHA-1, or—equivalently with this graph—by using br2~2 to name it) to get a "detached HEAD" there:

D       <-- HEAD
|
|   F   <-- br2
 \ /
  E
   \
    G   <-- br3

Now:

$ git cherry-pick -n br2^  # make a copy of E but don't commit yet
# edit to fix it
$ git commit               # make new commit E' that's fixed

Once the commit finishes, we have this (still with the "detached HEAD):

  E'    <-- HEAD
 /
|
|
D
|
|   F   <-- br2
 \ /
  E
   \
    G   <-- br3

Now we can copy commit F to F':

$ git cherry-pick br2

giving:

    F'  <-- HEAD
   /
  E'
 /
|
|
D
|
|   F   <-- br2
 \ /
  E
   \
    G   <-- br3

We're now ready to make br2 refer to commit F':

$ git branch -f br2 HEAD

giving:

    F'  <-- HEAD, br2
   /
  E'
 /
|
|
D
|
|   F   [abandoned]
 \ /
  E
   \
    G   <-- br3

(This is the "or strictly speaking" part I wrote above: you can add commits to a repository, then move branch labels around so that they label the new commit chains, rather than their old ones. All added commits move HEAD: it's just that if HEAD is a reference to a branch, they also move the branch one step forward as you work. Having HEAD refer to a branch is the "normal" way of working, but you can fake it after-the-fact in "detached HEAD" mode with git branch -f. In this case, I'm doing that to build the new br2 without using a branch name, then moving the branch name to the new chain of commits once it's ready.)

Now we need to copy G to G', attaching G' to E'. Here are the steps:

$ git checkout br2^      # get back on E' as a detached HEAD
[git says stuff here about moving the detached HEAD]
$ git cherry-pick br3    # copy commit G
$ git branch -f br3 HEAD # and move br3 label here

(this is what we did for copying F to F', more or less) giving:

    F'  <-- br2
   /
  E'
 / \
|   G'  <-- HEAD, br3
|
D
|
|   F   [abandoned]
 \ /
  E
   \
    G   [abandoned]

Once you're all done, you should probably git checkout somebranch to get back "on a branch", and away from this "detached HEAD" state.

As noted in comments, needing to apply a patch as wide-spread-ly (is that a word?) as this suggests that maybe there's something wrong with the whole process. (But this is what fixing a "day zero bug" looks like when you have a bunch of different products that all share the code-base with the day-zero bug.)

Solution 2

This doesn't exist, because each merge can potentially result in separate conflicts that require user intervention. You would need to write a script to do what you want.

Share:
12,762

Related videos on Youtube

greole
Author by

greole

Πάντα ῥεῖ

Updated on September 27, 2022

Comments

  • greole
    greole over 1 year

    If I fixed a bug in a file in branch branch_a, which should be applied to all branches. Is there a way to apply the change to all branches without having to checkout the branches individually.

    git commit -m 'commit msg' # on branch a 
    
    git checkout branch_b
    git cherry-pick branch_a
    
    git checkout branch_c
    git cherry-pick branch_a
    

    What i would like to have is a git commit --to-all-branches which tries to propagate the changes to all branches if possible.

    Edit

    To clarify a bit my situation, I write code to for computational problems. Often I end up in the situation where it is unclear which approach is the best solve a given problem. So i create a branch. These branches tend to diverge and are more like forks. However to keep all the files in place I just use one git repository with multiple branches. In the situation of a bug relevant for all branches/forks I was looking for away to update all branches automatically.

    • Magnus Bäck
      Magnus Bäck about 10 years
      To frequently have to apply the same patch to a multitude of branches is an indication that you're doing something weird that you shouldn't be doing. For example, you might be using branching where static configuration would be more suitable.
    • Todd A. Jacobs
      Todd A. Jacobs about 10 years
      Atlassian Stash supports automatic branch merging, but it isn't a native Git feature.
    • Dohn Joe
      Dohn Joe almost 5 years
      Another use-case for the problem described by the OP would be a repo-side ReadMe file in the root of the repo. The branches "branch off" at different points in time, so the feature branches may have an antiquated ReadMe in the repo-root. So, when I update the ReadMe in the repo-root, I would like to update that ReadMe in all branches, since it contains only general information in the repo itself.