Home > Back-end >  How to checkout git branches programatically via bash script?
How to checkout git branches programatically via bash script?

Time:06-08

Let's say I've got the following directory structure.

.ProjectFolder
|-A
|-B
|-C

Where A,B and C are all github projects.

I could do

git submodule foreach git checkout <branch> && git submodule foreach git pull

to checkout and pull each one to the most recent version of . However, let's say that I have some work done on A locally, and git will tell me that it cannot check out to because of my uncommited work.

I would like to write a bash script that goes inside of each folder, checks out said branch if possible, then performs a set of actions. Say

for item in $(ls);do
  cd ./$item 
  git checkout <branch>
  git pull
  cd ..
done

The issue with that is "cd". When I try doing that, it doesn't seem to go into the folder. I've browsed a bit around and I've seen that happens because "cd" runs a subshell, then exits, leaving me in the same place as before. I've tried sourcing the file rather than running it, and that didn't seem to work either.

Does anybody have any alternative? (preferably, alternatives that don't use aliases for "cd").

The reason why i'd like to do it this way is because I'd like to extend that functionality at some point I.E. checkout to a new local branch, commit the changes to that branch, then attempt to checkout the branch I was interested in.

CodePudding user response:

When I try doing that, it doesn't seem to go into the folder...

If I have the same project structure:

$ tree ProjectFolder
ProjectFolder/
├── A
├── B
└── C

And inside that folder I run your script, but I replace the git commands with pwd, we can see that it works exactly as intended:

$ cd ProjectFolder
$ for item in $(ls); do
> cd ./$item
> pwd
> cd ..
> done
.../ProjectFolder/A
.../ProjectFolder/B
.../ProjectFolder/C

So we know the cd is working. If we run the git commands instead, it also seems to work:

$ for item in $(ls); do
> cd ./$item
> git checkout testbranch
> git pull
> cd ..
> done
branch 'testbranch' set up to track 'origin/testbranch' by rebasing.
Switched to a new branch 'testbranch'
Already up to date.
branch 'testbranch' set up to track 'origin/testbranch' by rebasing.
Switched to a new branch 'testbranch'
Already up to date.
branch 'testbranch' set up to track 'origin/testbranch' by rebasing.
Switched to a new branch 'testbranch'
Already up to date.

That also seems to work.

I would make suggest a few changes to your script:

  • Don't use ls; just use wildcard expansion:

    for item in *; do
    
  • Consider some defensive coding to make sure your targets are git repositories:

    for item in *; do
      [ -d "$item/.git" ] || continue
    done
    

    This prevents your script from breaking or throwing up errors if ProjectFolder contains files or non-git-repository directories.

  • You don't need to use cd at all if you use the -C option to git:

    for item in *; do
      [ -d "$item/.git" ] || continue
      git -C "$item" git checkout testbranch
      git -C "$item" git pull
    done
    

CodePudding user response:

I tried your script in git bash and it worked. To avoid cd, we have at least 2 options.

One is to export GIT_DIR and GIT_WORKTREE,

for item in $(ls);do
  export GIT_DIR=${item}.git
  export GIT_WORKTREE=${item}
  git checkout <branch>
  git pull
done
unset GIT_DIR
unset GIT_WORKTREE

The other is to use --git-dir and --work-tree,

for item in $(ls);do
    git --git-dir=${item}.git --work-tree=${item} checkout <branch>
    git --git-dir=${item}.git --work-tree=${item} pull
done

In order to run the script in any directory, it's better to pass the absolute paths to gitdir and worktree.

  • Related