5

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).

Community
  • 1
  • 1
ebosi
  • 759
  • 1
  • 13
  • 26
  • 2
    The answer you linked to will do that. Clone the repo and test it out. – Jeff Puckett Mar 24 '17 at 15:41
  • Forgive me if I misunderstand you, but do you want to delete `feature_a` and `feature_b` without affecting `master`? AFAIK there is nothing wrong with deleting those other branches, this is done all the time in most repos. – Tim Biegeleisen Mar 24 '17 at 15:46
  • @TimBiegeleisen Yes I want, indeed. But since I'm not fully comfortable with `git` yet, I'm afraid that if I delete `feature_a` and `feature_b`: *(1)* this would mess up with master (e.g. doing as if `feature_a` had never existed, and thus removing files I created in these branches); and *(2)* the history tree will look 'flat' (i.e. not showing `feature_a` and `feature_b` 'diverting' anymore). – ebosi Mar 24 '17 at 15:52
  • I can't vouch for the history, but deleting the feature branches does _not_ necessarily mean deleting their commits. If `master` still refers to these commits, then Git won't garbage collect them, at least this is my understanding. If feature branches could never be deleted, Git would have a serious technical limitation. – Tim Biegeleisen Mar 24 '17 at 15:55
  • @TimBiegeleisen I should have been a bit more clever and simply try it by myself: doing `git checkout -b test & touch test.txt & git add test.txt & git commit -m "test commit" & git checkout master & git merge --no-ff test & git branch -d test` reproduce what I want to do... and when looking at either `git branch` or `git log --graph --oneline`, it shows it's what I'm expecting. **TL;DR: `git branch -d ` is what I was looking for.** – ebosi Mar 24 '17 at 16:05

1 Answers1

6

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.

torek
  • 330,127
  • 43
  • 437
  • 552