2

I'm trying to write a bash script called by a daily cron schedule that will cycle all sub directories looking for a .git directory, run a git pull --all on that repository to update the repo and grab any new branches, track those branches, and then move on to the next repo. I'm a bash script beginner and familiar with some linux. My problem is the for loop which I found at How to clone all remote branches in Git? but doesn't seem to run properly now that I've introduced it into my script and the call to cd ${line}.. which doesn't seem to work as anticipated. I'm wondering what i've done wrong and some direction on how to fix it. Thank You.

My current script gitcron.sh:

#!/bin/bash

find . -maxdepth 2 -name .git -type d -print > .gitrepos

while read -r line; do
  cd ${line}/..
  git pull --all
  for branch in $(git branch --all | grep '^\s*remotes' | egrep --invert-match '(:?HEAD|master)$'); do
     git branch --track "${branch##*/}" "$branch"
  done
done < ".gitrepos"

.gitrepos generated file:

./codeigniter/.git
./magento/.git
./magento2/.git

My problem:

gitcron.sh: line 6: cd: ./magento2/.git/..: No such file or directory
fatal: it does not make sense to create 'HEAD' manually
error: unknown switch `>'
Nate
  • 259
  • 1
  • 6
  • 18

4 Answers4

2

The problem is that you have changed the working directory (and keep changing) with cd ${line}/.. i.e. going to the parent of the {line}. Everything is done within teh same shell session. After first run, the directory would be changed to the parent of the first value of line variable, so next run(s) would (prsumably) fail because of failing to find the parent (and hence .git would not also be present).

To solve this, you can:

  • Use absolute paths to the directories in find:

    find /foo/bar/ ...
    

    and then use while as is now.

  • Or run cd and successive block(s) in a subshell:

    while ...; do (cd ...; ...); done <file
    

As a side note, (always) quote your variable expansions to prevent word splitting and pathname expansion.

heemayl
  • 39,294
  • 7
  • 70
  • 76
1

Problem is that you've already changed directory to first repo i.e. codeigniter using cd in your while loop. After that your script is not going back to parent directory using cd - at the end.

You can even avoid temporary file creation by using process substitution:

while IFS= read -d '' -r line; do
  cd "$line"/..
  git pull --all

  while read -r branch; do
     git branch --track "${branch##*/}" "$branch"
  done < <(git branch --all | grep '^\s*remotes' | egrep --invert-match '(:?HEAD|master)$')

  cd -
done < <(find . -maxdepth 2 -name .git -type d -print0)
anubhava
  • 761,203
  • 64
  • 569
  • 643
  • I'm getting `syntax error near unexpected token '<'` on line `done < <(find . -maxdepth 2 -name .git -type d -print0)` OS: CentOS 7 x64 – Nate Jul 09 '17 at 07:32
  • You should execute script as: `bash ./gitcron.sh` – anubhava Jul 09 '17 at 08:02
  • @anubhava Better shebang: `#! /usr/bin/env bash` in gitcron.sh. – phd Jul 09 '17 at 11:39
  • I thought sh and bash were similar but come to find out there are some differences. https://stackoverflow.com/questions/5725296/difference-between-sh-and-bash – Nate Jul 10 '17 at 02:27
  • Yes indeed there are many differences. I suggested `-print0` because that will support directory names with whitespaces and special meta characters as well. Also I notices question was tagged as `bash` so suggested a `bash` solution. Let me know if using `bash` is a problem then I can suggest `sh` only script also. – anubhava Jul 10 '17 at 04:10
1

As other users have already posted, the problem seems to be not using absolute directories and cding twice.

You could simplify your script by getting rid of the temp file and using pushd and popd as opposed to cd some/path, cd -.

#!/usr/bin/env bash
set -uo pipefail

find . -maxdepth 2 -name ".git" -type d -print0 | while IFS= read -r -d '' repo; do
    # Use bash parameter substitution to remove .git from end of path
    pushd "${repo%.git}" >/dev/null
    # Could also use dirname to get the parent directory
    #pushd "$( dirname "$repo" )" >/dev/null
    # Now in correct directory; run all git commands"
    # git ...
    popd >/dev/null
done
Ian Walker
  • 61
  • 3
0

As a workaround, try the same find command, but with absolute paths:

find $(pwd) -maxdepth 2 -name .git -type d -print > .gitrepos

That way, cd will access those folder through their full and complete path.
Even if your first cd changed the current path, it won't maater.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250