Best practice of deleting branches on Github

We are currently actively developing a package and create a few new branches (pull requests) per day.

When would you delete these branches? Immediately after merging the pull request, or rather later?

And how can I delete a remote branch in the first place?

I would flip the question. Why would you keep branches which has already been merged in?

While it can make sense to keep old branches for a while, after some time the main branch has deviated so much, that the knowledge held in the older branches (which have already been merged in!) is not that useful.

I, personally, would recommend to not keep older PR branches around, since I have rarely found it useful.

Would be interesting to hear what others think.

Kind regards

3 Likes

You can configure GitHub repos to auto-delete the remote branch when a PR is merged, and I think you can configure git to delete tracking branches locally when that happens (How Core Git Developers Configure Git)

5 Likes

Well, I usually sqash the branch of a pull request before merging it. But sometimes a bug squeezed in, and then it is useful to undo just the one commit that caused the bug.

2 Likes

If really needed you can restore a branch after it has been deleted using GitHub UI.

One big reason for deleting all branches of merged and closed PRs is that when cloning a repository all branches are fetched, fetching branches that aren’t going to be used for literally anything is a big waste of everybody’s bandwidth.

9 Likes

I did not know that you can restore a deleted branch. How is Github doing that?

Any way, knowing that this is possible I will now always delete my branches after merging.

1 Like

GitHub never deletes any content ever pushed to it. Which makes restoring “deleted” branches possible.

  • Are you sure restoring a branch will always be possible? Perhaps Github does some garbage collection for very old branches?
  • One thing I know is that deleting the fork prevents restoring the branch. I can see that in some of my old PRs.
  • In any case, I think it’s possible to access a merged PR’s commits even if the original branch is deleted: Checking out pull requests locally - GitHub Docs

They don’t officially promise not to garbage-collect, but it’s pretty well-established that GitHub never ever deletes anything. It would mess majorly with their entire infrastructure if they changed that. So it’s a pretty safe assumption.

2 Likes

Well, I can’t speak for future GitHub’s plans, but that’s what it is now.

They don’t do that. In the backend GitHub doesn’t have a standard git repo, but a rather sophisticated “database” which can interact with standard git clients.

You should still be able to fetch the ref of a PR.

2 Likes

There seem to be some misconceptions on what a branch is. As far as I understand, a branch has no internal meaning to git. Instead it is just a convenient way to (temporary) name a particular stream of commits (see here for details). I.e., as also explained in this stackoverflow post deleting a branch just deletes the corresponding file in .git/refs/heads/<mybranch> which only content was a git hash referring to the commit currently named <mybranch>.
Thus, you can always delete and restore branches (from any commit) at will. Internally, git only cares about commits and deleting those is more complicated and requires rewriting the git history.

Sorry but I have to nitpick your post, there are dangerous statements there.

  • It’s true that a branch is only a pointer to a commit.
  • It’s true that deleted branches can be recreated from the last commit in that branch - provided that the commit still exists.
  • When you delete a branch, any commits that were unique to that branch become “unreachable”, and are only accessible by commit hash/id (which can be found in the reflog for 30 days, by default).
  • Unreachable commits (i.e. not tagged or in a branch) more than 30 days old are permanently deleted when git performs garbage collection.
  • Garbage collection can be triggered by common git operations such as fetch and merge when there are too many “loose objects”.
  • So branches cannot “always” be restored at will - only for 30 days, or if the commits exist in ancestor trees of other branches and tags.
  • Deleting commits isn’t complicated. Delete all branches they occur in and they’ll be gone in the next garbage collection after 30 days.
  • A git rebase is often called “rewriting history”, but it doesn’t actually rewrite anything. It creates new commits and makes the old ones unreachable, but you can still find them in the reflog (for 30 days).
5 Likes

But is this true on Github, assuming Github is not a normal git server but uses some other way of storing the commits?

Thanks for clarifying that branches still matter as references to commits, i.e., in order to render them reachable. Yet, in the end it’s the structure of commits that matters and a commit from a merged branch (as in the OP) stays reachable even after deleting its branch, i.e., name. Also “delete all branches they occur in” might not be easy to do (or imply deleting main and most of the repo) when the commit is part of a long merged history. In this case, it must probably be made unreachable explicitly by creating essentially a new chain of commits around it, e.g., via git rebase.

No.

and a commit from a merged branch (as in the OP) stays reachable even after deleting its branch,

The OP specifically said they are squash merging. Are the commits of squashed pr considered ancestors of the merge commit? I always assumed squash merge means to „copy“ the pr, sqash history and merge a single squashed commit. Or is squash merging something more clever?

Sorry had missed that part. Guess you are right, squashing simply creates a new commit and leaves the old ones dangling.
Should stop commenting on that thread. At least for me the discussion has cleared some things up, though.

To me this sounds more like an argument against squash merging more than anything else :upside_down_face:

1 Like