Iterate over branches in your git repository
Sometimes when we try to automate tasks, there is a need to iterate over branches or tags in a repository. Hardcoding branch names is a no-go because we want our scripts to be reusable. The first thing I do in such a case is checking the git branch
command output.
1 2 3 4 |
$ git branch development * fix/broken-layout master |
No input cannot be parsed with sophisticated enough regular expression or long enough chain of piped cut
, tr
, etc., but I want to keep my scripts simple. I want anyone to understand it at first glance. If possible – without head-scratching while looking at regular expressions or piped statements.
That can be done. git for-each-ref
is a way to go. I describe it as a more powerful git branch --list
.
1 2 3 4 5 6 7 8 |
$ git for-each-ref 95a1dd887bdc87e9b1e12951b97dbb5b1760057c commit refs/heads/development b222b496aa87bb1c884fb6088f486511ba0b53ac commit refs/heads/fix/broken-layout 083083ba032eb7884da7b400a99036e1ae3bc5ba commit refs/heads/master 95a1dd887bdc87e9b1e12951b97dbb5b1760057c commit refs/remotes/origin/development 083083ba032eb7884da7b400a99036e1ae3bc5ba commit refs/remotes/origin/master 4ef4cf8e0f119a871968357e1470f359f815ee6d commit refs/tags/v1.0.0 083083ba032eb7884da7b400a99036e1ae3bc5ba commit refs/tags/v1.0.1 |
It’s more verbose, and we get a list of all refs, not only local branches but also remote branches and tags. That output can be limited with the pattern refs which need to match.
We’re only interested in the ref name, so I define an output format.
1 2 3 4 |
$ git for-each-ref --format='%(refname)' refs/heads refs/heads/development refs/heads/fix/broken-layout refs/heads/master |
Much better, this could already be used in a for loop of any shell script. We could, however, generate the script with the --format
option. --shell
, --perl
, --python
, --tcl
will handle language-specific quoting for us.
Let’s check how many bytes README.md file has on each branch.
1 2 3 4 |
$ git for-each-ref --shell --format='echo %(refname); git checkout --quiet %(refname); wc -c README.md;' refs/heads echo 'refs/heads/development'; git checkout --quiet 'refs/heads/development'; wc -c README.md; echo 'refs/heads/fix/broken-layout'; git checkout --quiet 'refs/heads/fix/broken-layout'; wc -c README.md; echo 'refs/heads/master'; git checkout --quiet 'refs/heads/master'; wc -c README.md; |
This output can be directly eval
-ed:
1 2 3 4 5 6 7 |
$ eval $(git for-each-ref --shell --format='echo %(refname); git checkout --quiet %(refname); wc -c README.md;' refs/heads) refs/heads/development 692 README.md refs/heads/fix/broken-layout 690 README.md refs/heads/master 690 README.md |
It’s not a command you will use every day, but it will save you a lot of work when you need it.
Have you heard about GitHub deprecating short SHA when referencing actions in workflows?
I’m maintaining many workflows in our organization, and we used to be practicing short SHA to reference some 3rd party actions. Lately, I had to go over all workflow YAML files on every branch, in every repository I work with to check if it uses short SHA, and update it if needed. I used grep
instead of wc
and executed git for-each-ref
on each repository I maintain.
1 2 3 4 |
$ grep -n "uses:" ./.github/workflows/* ./.github/workflows/build.yaml:12: uses: actions/checkout@v2 ./.github/workflows/build.yaml:22: uses: actions/upload-artifact@v1 ./.github/workflows/remove-old-artifacts.yml:20: uses: c-hive/gha-remove-artifacts@24dc2338 # @v1.2.0 |
I can’t imagine how much work it would take to iterate over repositories and branches manually. It’s much more pleasant to just scroll over a list.
Feedback is welcome!
Want to join the discussion?Feel free to contribute!
Leave a Reply
Want to join the discussion?Feel free to contribute!