简体   繁体   中英

Git: how force push a file to all branches

I have a sample android project repository where I create samples for each topics in Android and keep in different Git branches, So that at any time I can switch to the corresponding branch and get sample code for a specific topic.

Basically I have a single Android project in Git, but each branch has different code base. Currently I have some 20+ branches in that.

But there are some configuration files like build.gradle which keeps the library versions , sdk version etc. These files I need to update regardless of any branch.

So, whenever I make a change to this config files , I want it to be pushed to all the branches, overwriting the old config files.

Say, if a library update comes, I update the version in master branch and push. But it affects only inn the master branch.

All the remaining 19 branches will have the old version code. I will have to switch to each branch and merge this config file from master.

Is there any single command for doing this? So that I update the file in one branch and I can force push it to all other branches?

Is there any single command for doing this? So that I update the file in one branch and I can force push it to all other branches?

No. Conceptually, the way Git works, this makes no sense: git push does not push files . git push pushes commits .

A commit is a full snapshot of every file. That is, given some commit hash ID—the hash IDs are the actual names of the commits—you can have Git enumerate every file with its content, frozen forever in time in whatever form it had in the index / staging-area at the time you (or whoever) ran git commit . More usefully, given that same hash ID, you can git checkout <hash-ID> and populate your work-tree (and your index / staging-area!) with those files.

Hence, if build.gradle exists in a snapshot (ie, a commit), extracting that commit will get you that build.gradle file, in the form it had in that snapshot. So far, this should all be pretty straightforward and mostly what you want. So now let's look at what it means to make a new commit.

We already noted that each commit has a hash ID. These IDs are seemingly random, though in fact they're actually a cryptographic hash of the complete contents of the commit. They are too difficult for humans to use directly. So Git provides a way to store the latest hash ID of some branch—what Git calls the tip of the branch —under a human-readable name, like master .

These names store one (1) hash ID. So how can you, or Git, find any earlier commit if all you know about is the latest one? The answer is that each Git commit stores, as part of itself—and this goes into the computation of its own hash ID since it's part of the commit contents—the hash ID of the previous tip of the branch. That is, given some sequence of commits:

... <-E  <-F  <-G   <--master

(with uppercase letters standing in for the actual hash IDs), you will add a new commit by having Git make a commit H whose parent is G 's hash. Git then writes the new hash into the name master so that master points to H :

... <-E  <-F  <-G  <-H   <--master

Since git push pushes commits (not files), you will need to extract each branch tip, including all of its files, and then construct a new commit to take over as the tip of that branch, with the updated build.gradle file:

          o--...--o   <--branch1
         /
...--o--o--...--o   <--branch2
      \
       o--...--o   <--branch3

becomes:

          o--...--o--X   <--branch1
         /
...--o--o--...--o--Y   <--branch2
      \
       o--...--o--Z   <--branch3

where X , Y , and Z are new commits added to each branch. You can now push all of these new commits at once:

git push origin branch1 branch2 branch3

which will send the new commits to the server. As long as you avoid using --force , the server's Git will ensure that these commits strictly add on to their branches, so that the server 's branches grow in exactly the same way that your own branches grew.

Note that if the underlying file contents (the build.gradle file) exactly matches in any two commits, those two commits share the Git object holding those contents. That means that making dozens ( "some 20+ branches" ) of new commits takes very little space—the actual size of a commit is small as the files get shared (this is possible because no Git object can ever be changed once it is committed). Even if the file contents vary, Git objects are highly compressed once they appear in Git's pack files (which is how smart-protocol pushes work).

If build.gradle really has the same contents in every branch (this seems unlikely to me) you can use a simple script to copy the file from some other branch into each branch and commit:

... build the base commit on some branch BR1, then:
for branch in BR2 BR3 BR4 BR5 BR6; do
    git checkout $branch &&
        git checkout BR1 -- build.gradle &&
        git commit --reuse-message=BR1 || break
done

This loop checks out each branch tip by name, extracts the build.gradle from the tip commit of BR1 , and commits it, re-using the commit message from that commit.

If the files are (potentially) different but the idea is to apply the same change to each:

for branch in BR2 BR3 BR4 BR5 BR6; do
    git checkout $branch &&
        git cherry-pick BR1 || break
done

This is a bit fancier: we turn whatever was changed in the tip of BR1 into a patch (sort of—Git is smarter about this than the patch utility) and apply that to the currently checked out branch, and make a new commit, re-using the commit message as before. Note that this also works if all the build.gradle files are identical.

The || break || break lines in these shell scripts make the loops terminate early if some part of the process fails. In this case, you must manually clean up, then restart the loop with whichever branches remain to have a new commit added.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM