How to insert a git branch between branches (and possibly rename) without affecting other users?

202 views Asked by At

I have a repo that I share with other users, so I'd love a solution that minimizes what others have to do, if there's a way they can just pull the changes, I'd love that.

Here's my current git branching:

A--B---------F (master)
    \       /
     C--D--E (develop)

Ideal goal:

A (master)
 \
  B---------F (stable)
   \       /
    C--D--E (develop)

But if that's too hard to achieve, specially without incurring complicated changes for everyone else who already has a clone (because of renaming), then I wouldn't it mind it looking like this or similar:

A--B-------------F (master)
    \           /
     B'--------F' (stable)
      \       /
       C--D--E (develop)

In short: Ideally I'd love to create a new master that is empty, waiting for a release pull request from stable, and rename the old master to stable. The new master should be the parent of the old master.

Alternatively, if renaming complicates things for other people who have the repo cloned: I'd like the current master and develop to remain unchanged, but I want a new branch called stable between them so that stable develop commits get merged to stable in the future, and releasable stable commits are merged to master.

I guess there's a possibility that the alternative might end up looking more like this instead:

A--B---------F-------------M (master)
   |         |\           /
   |         | `---I-----L (stable)
    \       /     /     /
     C--D--E--G--H--J--K (develop)

Where G,H,J,K are new develop commits, I and L are merge commits from the stable H and K, and M is a releasable merge commit from L. This I assume I can easily do by branching off of master as stable, then making sure I'm merging from develop to stable, and stable to master, in the future. Or does git have a concept of parent-child relationship between branches?

Thank you in advance for any solutions!

EDIT: based on the answer given by torek, we could

  1. checkout develop which won't change
  2. create stable from master
  3. create main from master at commit A

And it would look like this:

    ,---------------------M (main)
   /                     /
  /         ,-----I-----L (stable)
 /         /     /     /
A---------F     /     / (deprecated master)
 \       /     /     /
  B--C--D--G--H--J--K (develop)

After doing the following:

git checkout develop
git branch stable master
git branch main <first commit hash>
1

There are 1 answers

3
torek On

The parent/child relationships in Git are tied solely to the commits. Each branch name, in Git, is really just a label for the last commit in that branch (which may be a middle commit in some other branch: that's the case with K and L in your last example, for instance).

Branch names can be moved arbitrarily, so you could move your master back from F to A in your first example, after creating new name stable pointing to commit F:

git switch develop        # so that we're on one that doesn't move
git branch stable master
git branch -f master <hash-of-A>

The tricky part here is that everyone who has a clone of this repository will have their clone's origin/master remote-tracking name pointing to commit F too. Those clones will update their own origin/master names on git fetch origin, so that they point to A, but if they have made their own master name pointing to existing commit F, their Git software will not move their master branch name just because origin/master moved—and their Git software will be reluctant to move their master "backwards", from F back to A, and will require some application of force.

Should you be inclined to rename master to main, this is a good time to do it: leave master alone, create new name main pointing to existing commit A, and tell people to run git fetch. They will get a new origin/main pointing to A. Tell them to create their own main in the usual way, if they wish—they don't need one if they're not using it. Tell them the name master is now dead and will be removed and they should remove their own master once they have their commits on some other branch name(s) as necessary.

Remember that every commit is 100% read-only, so you can't change the existing F to have F' as a parent. You can always make new commits, and those can contain whatever snapshot you like, and whatever list of parent hash IDs you like (so that they point backwards to any commit(s) that exist at the point you make the new commit). Any branch name can point to any commit, but:

  • each clone has its own branch names;
  • Git is willing to add commits to a branch (by sliding the name "forwards" to new commits, as long as it doesn't lose old ones in the process, or by merging in new commits) at any time. It requires applying force (as in git branch -f or git reset) to move a branch name "backwards" (the way the commit linkages actually go, from children backwards to parents).

So it's really easy to get other clones of this repository to add on stuff. That happens automatically: people get the new commits whether they want to or not. But it is hard to get people to retract branches: that takes manual work by each person.


Regarding your edit:

... And it would look like this:

    ,---------------------M (main)
   /                     /
  /         ,-----I-----L (stable)
 /         /     /     /
A---------F     /     / (deprecated master)
 \       /     /     /
  B--C--D--G--H--J--K (develop)

It would eventually, once you've made more commits and made the merge M, yes. (To get there from what you start with, you'd just run:

git branch main <hash-of-A>   # or git branch main master~2
git branch stable master

and on any GitHub-style servers, use the web interface to set up the "default branch" as main now, optionally also deleting master entirely. To set the default branch, GitHub will run git symbolic-ref HEAD refs/heads/main, or whatever is equivalent in whatever software they're using.)