Search code examples
bashgit-remotegit-log

Can the url of the remote be included in the output of `git for-each-ref` command?


I am writing to a script to list all the git repositories on my system, their branches, and the latest commits on the system. So I have created this script prints out the following.

directory, branch(ref), date, commit hash, date, commit message, ref (again).

#!/bin/bash
IFS='
'
for directory in `ls -d $1/*/`; do
  # echo "$directory : $directory.git"
  if [[ -d $directory/.git ]] ; then
      # filter=/refs/heads
      filter=''
      for branch in $(git -C $directory for-each-ref --format='%(refname)' $filter); do
          echo $directory : $branch :  "$(git -C $directory log -n 1 --oneline  --pretty=format:'%Cred%h - %C(yellow)%ai - %C(green)%s %C(reset) %gD' $branch)" : $(echo $branch | rev | cut -d\/ -f-1 | rev)
      done
  fi
done

What I don't have is the repository URLs for the remotes. Can the remote's URL be printed as part of the output of git for-each-ref command?

I guess I could use git remote -C $directory -v to list the remotes for the repository into a lookup list which I would use for each value of xxxxx in refs/remotes/xxxxx into a variable which would be added to the echo command.


Solution

  • You can use git for-each-ref to generate an arbitrary script that will later be evaluated with eval. Adapting to your case the longest of the examples from the documentation on git for-each-ref, I arrived at the following:

    #!/bin/bash
    IFS='
    '
    for directory in "$1"/*/ ; do
      # echo "$directory : $directory.git"
      if [[ -d $directory/.git ]] ; then
          # filter=/refs/heads
          filter=''
          fmt="
               dir=$directory"'
               ref=%(refname)
               refshort=%(refname:short)
               h=%(objectname:short)
               date=%(authordate:iso)
               subj=%(subject)
    
               printf "%s : %s : %s - %s - %s : %s" "$dir" "$ref" %(color:red)"$h" %(color:yellow)"$date" %(color:green)"$subj" %(color:reset)"$refshort"
               if [[ $ref == refs/remotes/* ]]
               then
                   remotename="${ref#refs/remotes/}"
                   remotename="${remotename%%/*}"
                   printf " : %s" "$(git -C "$dir" remote get-url "$remotename")"
               fi
               printf "\n"
            '
    
            eval="$(git -C $directory for-each-ref --shell --format="$fmt" $filter)"
            eval "$eval"
      fi
    done
    

    Note that I got rid of the git log from your implementation, but as a result I also dropped the %gD field, since I didn't quite well understand what it meant, and couldn't find how to obtain it inside git-for-each-ref. Besides, I changed the way of obtaining the short ref from the full ref; my implementation is not equivalent yours, but produces unambiguous results).