0

I've deleted a bunch of BitBucket branches. I want my local to reflect that without having to manually delete each branch. I should be able to do something to "just make my local match my remote as if I just did a fresh clone."

edit: I don't want some several step script to grep and search things, nor do I want to just delete my project and re-clone - this should be simple functionality built into GIT. It's really only like 8 branches so I could do it manually without too much effort, but I'm trying to learn better ways to do it here.

edit2: One of the linked answers suggests:

git remote prune origin prunes tracking branches not on the remote.

This looks like what I want, but it doesn't delete any local branches. It says pruned for many origin/ branches, but git branch still shows all my old branches that don't exist in the remote.

Art Vandelay
  • 333
  • 4
  • 14
  • You might want to do a fresh clone. – evolutionxbox Feb 20 '19 at 19:27
  • Why is the "fresh clone" a solution you ruled out? – RomainValeri Feb 20 '19 at 19:27
  • This might help https://stackoverflow.com/questions/17983068/delete-local-git-branches-after-deleting-them-on-the-remote-repo – Hy L Feb 20 '19 at 19:46
  • Possible duplicate of [Remove tracking branches no longer on remote](https://stackoverflow.com/questions/7726949/remove-tracking-branches-no-longer-on-remote) – phd Feb 20 '19 at 19:48
  • https://stackoverflow.com/search?q=%5Bgit%5D+delete+local+branches – phd Feb 20 '19 at 19:48
  • https://stackoverflow.com/questions/6373277/synchronizing-a-local-git-repository-with-a-remote-one – phd Feb 20 '19 at 19:50
  • https://stackoverflow.com/search?q=%5Bgit%5D+synchronize – phd Feb 20 '19 at 19:50
  • Your branches are *yours*. Git won't delete them without direct instructions from you. You shouldn't just randomly delete branch `foo` because `origin/foo` is gone: you might have commits that should be copied (cherry-picked) to some other branch, for instance. – torek Feb 20 '19 at 20:07

2 Answers2

0

TL;DR: what you really want is the fetch.prune setting. Still, I'm going to answer the question you asked, rather than the one you should have. :-)

To turn your existing clone into what looks like a fresh clone, you need at least four steps.

  1. Run git fetch -p. Or, configure fetch.prune to true in either your local repository or your per-user settings and run git fetch. This will delete any origin/* remote-tracking names in your repository corresponding to branch names that used to, but no longer, exist in the repository at origin.

  2. Manually delete any branches that you don't want—all but one. To do this you'll have to check out the one you are willing to retain. Automate this removal if you like, but be aware that if you have commits you never sent upstream, you may lose those commits at this point. See below.

  3. git reset --hard your remaining branch(es) to their remote-tracking counterparts. You may lose some commits at this point. See below.

  4. Run git clean -dfx. See below.

You now have what resembles a fresh clone, except for these items:

  • The reflog for your HEAD still has all your recent activity, which may retain some commits. You can clean that out too, using git reflog expire --expire=all --expire-unreachable=all.

  • Any stashes you have still exist. You can drop them all with git stash clear.

  • Any unusual references you have made still exist (e.g., refs/notes/, refs/replace/ from git replace). You must delete those manually if desired.

  • Any shallow clone status persists. You can use git fetch --unshallow to clear that out.

  • Some assume-unchanged or skip-worktree bits may still be set in the index. You can use git update-index --no-assume-unchanged and git update-index --no-skip-worktree to fix these. You only need to do this if you set those bits earlier; Git never sets them on its own.

(I think that's everything but I might be missing an item or two.)

Notes

If you were to do a fresh git clone, you would:

  • create a new empty repository: mkdir name; cd name; git init
  • add to it a remote named origin using the appropriate URL: git remote add origin url
  • run git fetch to acquire all its existing branches as your origin/* remote-tracking names
  • pick one of those branches (based on your -b argument to git clone) to create locally
  • run git checkout name, which creates name from origin/name.

This new repository has the same remote-tracking names that remain after step 1 (the pruning fetch), so step 1 fixes your remote-tracking names. It has no branch names—no refs/heads/* references—other than the one created in the last step, so step 2 removes these. You are of course on that one remaining branch, so step 2 includes the git checkout name and step 3, git reset --hard, makes your branch name point to the correct commit while updating all of your index and most of your work-tree.

Of course, untracked files and directories may remain, so step 4, git clean -dfx, has Git remove them, including files ignored by .gitignore directives.

To automate removing branches that are not the current branch, you'll need a small script. There are some in the linked questions (the possible duplicates). But these delete your branches, which are not anyone else's branches. You should not just randomly delete them: you should already delete them yourself when you're done with them, so there should be nothing to delete. This is why steps 2 and 3 are listed, but shouldn't be done—at least, not automatically.

The last operation, git clean -dfx, removes build artifacts (e.g., compiled *.o files or *.pyc byte-coded Python files). Usually you want to do this manually under your own control, not just because something has changed upstream.

torek
  • 330,127
  • 43
  • 437
  • 552
  • I looked into your fetch.prune = true setting, but that doesn't work either. I did `git config remote.origin.prune true` then `git fetch`, but I still kept my local branches. Is my GIT broken or am I missing something? – Art Vandelay Feb 20 '19 at 20:11
  • Suppose you were working on a new feature and had created your local branch `feature`. You pushed to origin, so now you also have `origin/feature`. Someone else comes along and thinks you're done and *deletes* `feature` from origin. You run `git fetch -p` and your `origin/feature` goes away. But you're in the middle of working on `feature`. Should your Git delete your `feature` branch? – torek Feb 20 '19 at 20:15
  • That's exactly what I want. Our workflow is that we fork the main repo, create branches from the development branch, and create pull requests from these branches to the main repo's development branch. No one would ever delete my remote branch because I'm the only one who works in this fork. – Art Vandelay Feb 20 '19 at 20:17
  • In that case, you'll want to delete both locally and remotely as you go. You can make an alias for that: `[alias] del2 = !f() { git branch -d $1; git push origin --delete $1; } f` for instance. You now need a one-time cleanup though. `git branch` doesn't delete multiple branches, so in bash-ish, `for b in branch1 branch2 branch3; do git branch -D $b; done`. – torek Feb 20 '19 at 20:27
0

I've seen a lot of good suggestions, but it looks like the real answer is that GIT doesn't supply a simpler command than deleting the project and using GIT clone. IMO there should be a command that is git reset --hard for all branches and everything else GIT tracks.

Art Vandelay
  • 333
  • 4
  • 14