"Git checkouter" - A Simple Tool to Checkout, List or Rebase Your Git Projects
Tags: git, bash
Published: 2020-03-15 07:51:36

The project is available on GitHub, on the [following link](https://github.com/MarounMaroun/git-checkouter).

`git-checkouter` is a tool that iterates on your parent Git folder, which includes all of your Git projects, and checkouts, lists, or rebase on top of master the given branch for each project.

### What?

OK, it's a common practice to keep all of your work-related Git projects in some folder, let's say `~work`:

```
.
├── project1
├── project2
├── project3
├── project4
```

Now you are working on a ticket "xxx", and you changed projects 1, 2 and 4.

Imagine you have many more projects, and you're working on many more tickets. Now you want to run local tests for the feature "xxx", but you don't remember what projects you have changed for this feature:

```bash
$ ./git-checkouter.sh -p ~/work -b xxx -d
'xxx' found in 'project1'
'xxx' found in 'project2'
'xxx' found in 'project4'
```

providing `-d` will only list which projects include the branch, and will not actually checkout.

It is also possible to set an environment variable: `export PROJECTS_DIR=~/work` and omit the `-p` flag.

After you saw which projects you changed for that specific feature, you can checkout the branch in all of them:

```bash
$ ./git-checkouter.sh -p ~/work -b xxx
project1: Switched to branch 'xxx'
project2: Switched to branch 'xxx'
project3: Switched to branch 'xxx'
project4: Switched to branch 'xxx'
```

If you want to ignore some projects, you can provide the `-e` flag:

```bash
$ ./git-checkouter.sh -p ~/work -b master -e project2,project3
project1: Switched to branch 'xxx'
Project excluded: project2
Project excluded: project3
project4: Switched to branch 'xxx'
```

Sync all branches with the remote master using the `-f` flag:

```bash
$ ./git-checkouter.sh -p ~/work -f
Rebasing project1: Current branch master is up to date.
Rebasing project2: Fast-forwarded xxx to origin/master.
Rebasing project3: Current branch xxx is up to date.
Rebasing project4: Current branch xxx is up to date.
```

The code:

```bash
#!/bin/bash

CYAN='\033[1;36m'
NC='\033[0m'
YELLOW='\033[0;33m'

display_usage() {
  echo "Usage: $(basename "$0") [OPTIONS]"
  echo ""
  echo "  -p  path to your projects' dir. If not provided,"
  echo "        will use env variable 'PROJECTS_DIR'"
  echo "  -b  git branch to checkout"
  echo "  -d  only print projects that have the given branch,"
  echo "        without actually check it out"
  echo "  -e  projects to exclude, separated by \",\" without spaces"
  echo "  -f  rebase all directories on top of remote master branch"
  echo ""
  echo "Bugs and suggestions: <https://github.com/MarounMaroun/git-checkouter/issues>"
  exit 1
}

projects=$PROJECTS_DIR
dry=''
branch=''
exclude=''
fetch=''

while getopts 'p:b:de:f' flag; do
  case "${flag}" in
    p)
      projects=${OPTARG}
      ;;
    b)
      branch=${OPTARG}
      ;;
    d)
      dry="true"
      ;;
    e)
      exclude=${OPTARG}
      ;;
    f)
      fetch="true"
      ;;
    *)
      display_usage
      exit 1
      ;;
  esac
done

if [[ (-z "$branch" || -z "$projects") && "$fetch" != true ]]; then
  echo "Error: both branch and projects dirs must be specified,"
  echo "unless you're using -f flag"
  echo ""
  display_usage
fi

if [[ ! -z "$exclude" ]]; then
  IFS=',' read -r exclude_projects <<< $exclude
fi

for d in $projects/*; do
  project_name=$(basename $d)
  if [[ ! -d "$d" ]]; then continue; fi
  cd $d
  if [ -d ".git" ]; then
    if [[ "$fetch" = true ]]; then
      printf "Rebasing branch ${CYAN}$(basename $d): ${NC}"
      git fetch && git rebase origin/master
      continue
    fi
    branches=$(git branch | cut -c 3-)
    branch_found=$(echo $branches | grep -oP "\b$branch\b")
    if [[ -z "$branch_found" ]]; then continue; fi
    if [[ ! -z "$dry" ]]; then
      printf "'$branch' found in ${CYAN}'$project_name'${NC}\n"
    else
      printf "${CYAN}$(basename $d): ${NC}"
      project_name=$(basename $d)
      if [[ $exclude_projects =~ .*$project_name.* ]]; then
        printf "${YELLOW}Project excluded:${NC} $project_name\n"
        continue
      fi
      git checkout $branch
    fi
  fi
done
```