13

First this is different because this is for all branches not one. (All the ones I have found only specify this for a single branch at a time)

I work on a repo that has about 20-30 branches (long story bad practice I know I know)

anyways I was deleting some old commits using the git bfg cleaner and after using it you need to either delete and reclone the code or reset every branch.

I know how to set branches using

git fetch origin
git reset --hard origin/master

But is there a way to reset every branch with one command or do I have to do it one branch at a time?

I have a lot of local files ignored that I do not want to deal with copying and rewriting. (like IDE files, computer config files, etc...)

dtracers
  • 1,534
  • 3
  • 17
  • 37
  • A bad, but probably working idea is to copy all files from `.git/refs/remotes` to `.git/refs/heads` and checkout again. – jofel Mar 15 '16 at 18:43

5 Answers5

9

First this is different because this is for all branches not one

You can use a script which which will loop over your branches and then do what ever you want on each one:

# Loop on all the desired branches
for ref in $(git branch -a)
do
   # ... DO ANYTHING YOU NEED HERE
   # You have the $ref which stores the branch name
   # ex: remotes/origin/master
done

Is there a better ans simple solution?

Yes, simply clone the project again and you will have the same content as your origin.
This is exactly what you do manually per branch.


Note:

You can fetch all the data at once using this command:

# Fetch all remotes, branches and remove deledted content
git fetch --all --prune
Community
  • 1
  • 1
CodeWizard
  • 128,036
  • 21
  • 144
  • 167
  • I have a ton of local files that would be deleted by cloning it again that would be more work than just going through the 30 branches – dtracers Mar 15 '16 at 18:42
  • Ok so you can use the loopp script. This will do the work – CodeWizard Mar 15 '16 at 18:43
  • git 1.9.1 tells me that "fetch --all does not take a repository argument"; it should be `git fetch origin --prune` – Ansa211 Oct 12 '18 at 07:48
7

Why not just delete all local branches, and then whenever a remote branch gets re-checked-out, you'll be synced?

 # BE CAREFUL!!!
 # THIS COMMAND WIPES OUT ALL LOCAL BRANCHES
 ​git branch | xargs git branch -D

xargs runs the provided command against every line of stdin, in this case "git branch -D" against every line of the original "git branch" command.

The "git branch -D" command will fail for current branch, but every other branch will get wiped.

Alternatively you could cook something up with "xargs" that runs "git branch -f" to force-set every local branch to its remote copy, but I think the "git branch -D" approach above is the simplest, and also the most straight-forward as far as consequences, potential hazards are concerned.

G. Sylvie Davies
  • 5,049
  • 3
  • 21
  • 30
  • git branch will **only** work on local checkouted branches. `git branch -a` or `git branch -r` will work on all branch and remote branches – CodeWizard Mar 15 '16 at 18:47
  • 3
    yes, I think OP only wants to reset the local branches – G. Sylvie Davies Mar 15 '16 at 18:51
  • 1
    Side note: you'll get two errors for the current branch, one from the `*` (harmless, just a complaint) and one because git won't let you delete the branch you're standing on. You'll need to either `git reset --hard` the current branch, or get into detached HEAD state. Overall this is a fine solution though, as long as you don't mind giving up your reflogs. – torek Mar 15 '16 at 19:38
4

Delete the directory completely, then clone the repository.

rm -R repodir
git clone https://github.com/repodir/repodir
snesgx
  • 449
  • 3
  • 4
3

Just wrote a small tool that does this.

Advantages of this tool:

  1. Supports multiple remotes in one repository.
  2. Supports having different names for the local and tracking branches.
  3. No error-prone regex parsing/editing.

Here's the script:

#!/bin/bash

ENOENT=2
EEXIST=17

curref=$(git symbolic-ref HEAD 2>&-)

fetch_all() {
  git fetch --all
  ret=$?
  [ "$ret" -eq 0 ] || {
    return "$ret"
  }
}

hard_reset_all() {
  ret=0

  while read -r line; do
    branch=$(echo "$line" | cut -c 8- | rev | cut -c 8- | rev)
    remote=$(git config --local --get "branch.$branch.remote")
    upstream=$(git config --local --get "branch.$branch.merge" | cut -c 12-)

    if ! git rev-parse --verify --quiet "$remote/$upstream" 1>&- 2>&-; then
      ret=$(( $ret | $ENOENT ))
      echo "Branch $branch"
      echo "  skipped: upstream is absent"
      continue
    fi

    if [ -z "$(git log --oneline -1 "$branch...$remote/$upstream")" ]; then
      continue
    fi

    echo "Branch $branch"

    echo "  $(git log --oneline -1 $branch)"
    if [ "refs/heads/$branch" = "$curref" ]; then
      git reset --hard "$remote/$upstream" 1>&- 2>&-
    else
      git update-ref "refs/heads/$branch" "$remote/$upstream" 1>&- 2>&-
    fi
    echo "  -> $(git log --oneline -1 $remote/$upstream)"
  done < <(git config --local --name-only --get-regex '^branch.*remote$')

  return "$ret"
}

fetch_all
excode=$?
[ "$excode" -eq 0 ] || {
  exit "$excode"
}

if output=$(git status --porcelain) && [ -n "$output" ]; then
  echo "Skipped: repo is not clean"
  exit $EEXIST
fi

hard_reset_all
excode=$?
[ "$excode" -eq 0 ] || {
  exit "$excode"
}
Shimmy Weitzhandler
  • 101,809
  • 122
  • 424
  • 632
Johann Chang
  • 1,281
  • 2
  • 14
  • 25
1

You can use the following oneliner (based on that other answer):

git branch --format='%(refname:short)' | xargs -i sh -c "git checkout {} && git reset --hard origin/{}"

  • --format='%(refname:short)' removes the annoying * from git branch
  • -i allow to use {} as a placeholder for the branch

WARNING: I didn't use that command a lot so it may not be entirely safe, see that answer if you want to make it safer.

CidTori
  • 351
  • 4
  • 17