Home > Software engineering >  Find all Git Bare and non-Bare repositories?
Find all Git Bare and non-Bare repositories?

Time:06-11

I want to loop over all the directories and find out all the git repositories (both Bare and non-Bare). However, I need to identify which one is Bare and which one is non-Bare so that I can run different operations on them.

I checked Check if current directory is a Git repository and How do I check if a repository is bare?.

The script I came up with is:

#!/bin/bash

for d in $(find /media/ismail/8TBRaid0/ABC -path '*/.git*' -prune -o -print -type d); do

 if git --git-dir=$d rev-parse --is-inside-work-tree > /dev/null 2>&1; then

 echo ${d} is a non-Bare Repository

 elif git --git-dir=$d rev-parse --is-bare-repository > /dev/null 2>&1; then

 echo ${d} is a is a Bare Repository

 fi

done

The output I am getting is:

/media/ismail/8TBRaid0/ABC/d is a non-Bare Repository
/media/ismail/8TBRaid0/ABC/e is a non-Bare Repository

The problem is:

ABC has 5 directories.

ABC% ls -la
total 28
drwxrwxr-x  7 ismail ismail 4096 Jun  9 16:44 .
drwxr--r-- 16 ismail ismail 4096 Jun  9 16:44 ..
drwxrwxr-x  3 ismail ismail 4096 Jun  9 16:44 a
drwxrwxr-x  3 ismail ismail 4096 Jun  9 16:44 b
drwxrwxr-x  3 ismail ismail 4096 Jun  9 16:44 c
drwxrwxr-x  7 ismail ismail 4096 Jun  9 16:44 d
drwxrwxr-x  7 ismail ismail 4096 Jun  9 16:44 e

Here a,b,c are non-Bare Repositories with .git directory. And d,e are Bare Repositories without .git directory.

So, the output I am getting is definitely wrong. What i need for my case is:

a is a non-Bare Repository
b is a non-Bare Repository
c is a non-Bare Repository
d is a Bare Repository
e is a Bare Repository

How can I get the expected result?

Update 1:

My directory structure is actually deeper and scattered. The structure I gave above is as an example. For example: the structure can be:

.
├── 1
│   └── a
├── 2
│   └── 3
│       ├── b
│       └── e
│           ├── branches
│           ├── config
│           ├── description
│           ├── HEAD
│           ├── hooks
│           │   ├── applypatch-msg.sample
│           │   ├── commit-msg.sample
│           │   ├── fsmonitor-watchman.sample
│           │   ├── post-update.sample
│           │   ├── pre-applypatch.sample
│           │   ├── pre-commit.sample
│           │   ├── pre-merge-commit.sample
│           │   ├── prepare-commit-msg.sample
│           │   ├── pre-push.sample
│           │   ├── pre-rebase.sample
│           │   ├── pre-receive.sample
│           │   └── update.sample
│           ├── info
│           │   └── exclude
│           ├── objects
│           │   ├── info
│           │   └── pack
│           └── refs
│               ├── heads
│               └── tags
├── c
├── d
│   ├── branches
│   ├── config
│   ├── description
│   ├── HEAD
│   ├── hooks
│   │   ├── applypatch-msg.sample
│   │   ├── commit-msg.sample
│   │   ├── fsmonitor-watchman.sample
│   │   ├── post-update.sample
│   │   ├── pre-applypatch.sample
│   │   ├── pre-commit.sample
│   │   ├── pre-merge-commit.sample
│   │   ├── prepare-commit-msg.sample
│   │   ├── pre-push.sample
│   │   ├── pre-rebase.sample
│   │   ├── pre-receive.sample
│   │   └── update.sample
│   ├── info
│   │   └── exclude
│   ├── objects
│   │   ├── info
│   │   └── pack
│   └── refs
│       ├── heads
│       └── tags
└── f
    └── ADOC Document.adoc

CodePudding user response:

For starters, look at the output of your find command:

$ find ABC -path '*/.git*' -prune -o -print -type d
ABC
ABC/a
ABC/b
ABC/c
ABC/d
ABC/d/branches
ABC/d/hooks
ABC/d/hooks/applypatch-msg.sample
ABC/d/hooks/commit-msg.sample
ABC/d/hooks/post-update.sample
[...]

That's not really useful at all. If everything contained in .../ABC is a git repository, you can just get a list of directories:

$ find ABC/* -maxdepth 0 -type d
ABC/a
ABC/b
ABC/c
ABC/d
ABC/e

That's going to be much easier to work with. We can then iterate over those results like this:

find ABC/* -maxdepth 0 -type d |
while read dir; do
  # note that --is-bare-repository returns results as a string
  # not an exit code
  is_bare=$(git -C $dir rev-parse --is-bare-repository 2> /dev/null)

  if [ "$is_bare" = true ]; then
    echo "$dir is a bare repository"
  else
    echo "$dir is not a bare repository"
  fi
done

Running that code produces:

ABC/a is not a bare repository
ABC/b is not a bare repository
ABC/c is not a bare repository
ABC/d is a bare repository
ABC/e is a bare repository

If some things in ABC may not be git repositories, we can add an additional clause inside the while loop:

  ...
  is_bare=$(git -C $dir rev-parse --is-bare-repository 2> /dev/null)
  if [ $? -ne 0 ]; then
    echo "$dir is not a git repository"
    continue
  fi
  ...

Update

If your directory structure is more varied, we can find git repositories by looking for a file named HEAD:

$ find ABC -name HEAD -printf "%h\n"
ABC/c/.git
ABC/1/a/.git
ABC/1/d
ABC/2/b/.git
ABC/2/e

That means we can rewrite the script to look like:

find ABC -name HEAD -printf "%h\n" |
while read dir; do
  if [[ $dir =~ /\.git/ ]]; then
    dir=${dir%/.*}
  fi

  # note that --is-bare-repository returns results as a string
  # not an exit code
  is_bare=$(git -C "$dir" rev-parse --is-bare-repository 2> /dev/null)

  if [[ $is_bare = true ]]; then
    echo "$dir is a bare repository"
  else
    echo "$dir is not a bare repository"
  fi
done

Note that I'm using some bash-specific syntax here to make our lives easier, but that shouldn't be a problem in most environments.

Given this directory tree:

ABC/
├── 1
│   ├── a   # non-bare repository
│   └── d   # bare repository
├── 2
│   ├── b   # non-bare repository
│   └── e   # bare repository
├── c       # non-bare repository
└── z       # not a repository

The script above produces:

ABC/c is not a bare repository
ABC/1/a is not a bare repository
ABC/1/d is a bare repository
ABC/2/b is not a bare repository
ABC/2/e is a bare repository

CodePudding user response:

You can probably do all this with a bit of find bash machinery:

$ cat myScript
#!/usr/bin/env bash

iiwt() {
  [[ $(git -C "$1" rev-parse --is-inside-work-tree 2> /dev/null) == "true" ]] &&
  echo "$1 is a non-Bare Repository"
}

ibr() {
  [[ $(git -C "$1" rev-parse --is-bare-repository 2> /dev/null) == "true" ]] &&
  echo "$1 is a Bare Repository"
}

export -f iiwt ibr

find . -type d -exec bash -c 'iiwt "$1" || ibr "$1"' _ {} \; -prune

Explanations:

  • The iiwt helper function tests if its parameter is inside a working tree. If yes it prints a message and its exit status is true. Else its exit status is false.

  • The ibr helper function tests if its parameter is inside a bare repository. If yes it prints a message and its exit status is true. Else its exit status is false.

  • The find command searches all directories and executes the bash script iiwt "$1" || ibr "$1" on each of them. If the exit status is true find stops the recursion here (this is what -prune is for). Else it recurses inside.

Demo:

$ mkdir -p foobar/bar foobar/a/b/c; cd foobar
$ git init --bare foo.git
$ git clone foo.git
$ git init --bare bar/foo.git
$ git clone bar/foo.git bar/foo
$ find . -type d
.
./a
./a/b
./a/b/c
./foo.git
./foo.git/refs
./foo.git/refs/heads
...
./foo/.git/refs/heads
./foo/.git/refs/tags
./foo/.git/branches
$ myScript .
./foo is a non-Bare Repository
./foo.git is a Bare Repository
./bar/foo.git is a Bare Repository
./bar/foo is a non-Bare Repository

Note that we could also use one single helper function:

$ cat myScript
#!/usr/bin/env bash

hlp() {
  if [[ $(git -C "$1" rev-parse --is-inside-work-tree 2> /dev/null) == "true" ]]; then
    echo "$1 is a non-Bare Repository"
  elif [[ $(git -C "$1" rev-parse --is-bare-repository 2> /dev/null) == "true" ]]; then
    echo "$1 is a Bare Repository"
  else
    return 1
  fi
}

export -f hlp

find . -type d -exec bash -c 'hlp "$1"' _ {} \; -prune
  • Related