Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

git remove old branches merge with --no-ff

I'm housekeeping my repo and want to delete some old branches that are not used anymore. There are plenty info on how to delete those. However, I'm not sure if these solutions would be safe for me as I merged branches using --no-ff.


Imagine my log looks like this:

master     *--*--*--------*--*--*--*
               \         /     /
                \       /     |
feature_a        *--*--*      |  
                     \        |
                      \       /
feature_b              *--*--*

Question: How to remove feature_a and feature_b branches without losing neither data nor log-structure?

Basically I want that git branch returns only *master, and that the log graph still looks like above (i.e. no rewriting commits of feature_a and _b onto master).

like image 317
ebosi Avatar asked Mar 24 '17 15:03

ebosi


1 Answers

All you need to do is delete the extra branch names (git branch -d feature_a feature_b for instance). The commits themselves are safe, protected by the branch name master. The accepted answer to the linked question is one way to automate this.


It is, I think, helpful to draw the branches differently, because Git works differently (from most other version control systems, and from what you're thinking). Instead of:

master     *--*--*--------*--*--*--*
               \         /     /
                \       /     |
feature_a        *--*--*      |  
                     \        |
                      \       /
feature_b              *--*--*

draw them like this:

*--*--*--------M--*--m--*    <-- master
    \         /     /
     *---*---*  <- / ------ feature_a
          \       /
           *--*--*   <-- feature_b

The reason to do this is that this is how Git works: the names actually point directly to specific commits. Git calls these three pointed-to commits the tip commits of the three branches. The commits themselves—all of them; all the *s in the graph—exist on their own; but they are found by starting with one of the names. The name locates the tip commit, and then Git works backwards (leftwards) from there to find the rest of the commits on the branch.

Merge commits—I've marked the two in this graph with the letters M and m—have the feature of pointing backwards to two commits at the same time. When Git follows commits backwards, it must follow all paths "simultaneously" (or as close as it can get). This means that the bottom-row commits are not just on branch feature_b, but also on branch master. Two of the middle-row commits are on all three branches.

When you delete a branch name, the commit to which it points—its tip—lose its name. If that's the only name by which you can find that commit, it also loses its protection against the Grim Reaper of Commits, Git's garbage collector (git gc: GC = garbage collector, or as I like to call it, the Grim Collector :-) ). But if it has some other branch name, or even a tag or anything else that lets you find it, it remains protected.

The same goes for all the commits "behind" that tip commit: if they're reachable from some name, they are protected from the Grim Collector. So the names do have a dual purpose, but once the branch tip is merged, the "protect the commits" purpose is no longer required. (Of course if you add new commits, that moves the branch tip, and its dual purpose resumes.)

Note as well that remote-tracking branch names like origin/master work exactly the same way, except that instead of moving when you add new commits, they move when you git fetch new commits from the corresponding remote.

like image 82
torek Avatar answered Sep 23 '22 01:09

torek