25

Using git fetch --prune deletes local remote tracking branches when the branch on the remote machine has been deleted. Setting remote.origin.prune to true using the following...

git config --global fetch.prune true

...makes using the fetch command always implicitly use the --prune option.

I am putting together a best-practices/introduction to git for some developers in my group who aren't quite familiar with it. I want to be sure I know this is not a dangerous behavior before advising them to do so. I at least give them a heads up of what to watch out for if there is some extraneous mishap case.

It doesn't seem like this is a destructive operation because it doesn't delete any local (non-remote) branches. It also seems like this is a great way to not build up remotes that aren't in use anymore without periodically specifying git fetch --prune or git remote prune.

If this is all true, why is this not the default behavior for git?

timfoil
  • 415
  • 4
  • 11
  • 3
    If you remove the branches, you can't use them as a backup when your colleague discovers they deleted a branch by mistake. – choroba Oct 04 '16 at 21:28
  • 1
    Wouldn't a local branch be sufficient for backup if it was an important enough branch? Why would you want the remote? I don't think that you should be counting on others devs for backup if you make a dumb mistake in deleting a remote. – timfoil Oct 04 '16 at 21:33
  • What advantages does pruning the branches give? – choroba Oct 04 '16 at 21:37
  • You might not have a corresponding local branch if you haven't done anything on that branch yourself, yet. And if you have done something on it, your local branch might have diverted and you might not have merged in the latest changes from the corresponding remote branch because you might have postponed that for later. – das-g Oct 04 '16 at 21:41
  • 1
    @choroba I personally push a lot of branches to the remote machine so I would say it unclutters number of branches that are left on everyone else's machines. I would also say it also lets new devs know which branches aren't relevant or actively being developed anymore without them having to periodically run 'git fetch --prune' (confusing for beginners). With the git config you have a one and done command. – timfoil Oct 04 '16 at 21:45

1 Answers1

26

It's not fundamentally dangerous, and I have been tempted to set it in my own --global settings, but I never have: primarily because it also has relatively little value to me.1

As you note, the intent is, in essence, to remove origin/zorg once branch zorg is no longer present on origin. Since this has no direct effect on your own zorg, if you have one at all, it's generally harmless, and declutters your view of remote-tracking branches. The only two possible downsides are:

  1. If someone mistakenly deletes zorg on the upstream repository, and all the downstream copies prune their origin/zorg, you lose the ID of the commit (and perhaps many commits themselves) that was the tip of zorg in that upstream repository. So it magnifies the effect of some mistakes. (Of course, if the upstream repository is that important, you probably should be making backups anyway—though if you choose to use the clones as backups, automatic pruning has a downside there. Of course, you can always disable automatic pruning specifically on these backups.)

  2. Suppose you do have your own zorg that is tracking origin/zorg, and the upstream zorg gets deleted (on purpose this time). Note that it has been your origin/zorg that provides you with git status information telling you how many commits you were ahead of origin/zorg. Suppose that you would now like to move these commits, but not commits-that-used-to-be-upstream, to another of your own branches. In this case, automatically pruning your own origin/zorg makes it difficult to tell which commits were yours, and which were already in the upstream.

Note that in case 2, you do not lose any commits. What you lose is the ability to tell that, e.g., only the last three commits currently on your zorg were your own, with several before that (that are not on any other branch now) having originally been on origin/zorg.


1Over the years, the pruning code has sometimes been somewhat broken, mostly in terms of failing to prune. This implies that the Git developers themselves probably don't use it much (but things should be better now since there are now tests in Git's internal test-suite to make sure pruning works correctly in new releases). So between "low value" and "insufficient testing in the past", that was, at least at the time, sufficient reason not to set it.

torek
  • 448,244
  • 59
  • 642
  • 775
  • 1
    I'm a bit confused what you mean by only moving specific commits to another branch for the second reason you listed. If one of your commits depends on the commits previously made (those upstream) shouldn't those changes/commits get tacked on along for the ride? – timfoil Oct 05 '16 at 13:15
  • 1
    @timfoil: If you *depend* on them, probably—but maybe the reason someone deleted the branch upstream is that those commits were broken and should not be used, and maybe you do not depend on them (or not much) after all. – torek Oct 05 '16 at 20:13
  • Since git commits can be thought of as a linked list/tree kind of structure I was thinking that any commits made after the bad commits would depend on each other. However, since you italicized _depend_ I think you are saying that commits do not always depend on previous commits (maybe editing different files or different parts of the same file would be independent commits) and the tip of an arbitrary branch can be moved to another arbitrary branch? What command would do this operation? I'm guessing not merge since that would include the bad commits. Rebase maybe? – timfoil Oct 05 '16 at 22:18
  • 1
    More precisely, the parent IDs in commits act as outgoing edges (one way arcs). Treating the commit IDs as nodes (or vertices) and parent IDs as directional edges, we get a directed graph, which Git guarantees is also acyclic. Hence commits form a DAG. However, each commit has its own `tree` object with a complete snapshot of the source as-of-that-commit. `git cherry-pick` converts a snapshot to a changeset by diffing the snapshot against its parent (for a merge you must specify *which* parent), and `git rebase` is essentially a series of cherry-picks. Hence, yes, primarily rebase. – torek Oct 05 '16 at 22:26