# Tag specific Git commits based on commit message contents

I am using GNU Bash 4.3, and Git 2.1.4, to tag certain git commits based on their commit messages. Specifically, I want to tag:

• as v0.0.1 the commit with message "Commit 5",
• as v0.0.2 the commit with message "Commit 10",

given the following commit log:

$git log --format=oneline --decorate 924d9120dec9f61d0f99a5155c51bad63d24b37f (HEAD, master) Commit 10 c62b2f68ea6b8ff047dd5f1e99010ebf434709ef Commit 9 feec2d76977521018ff33fd6e109530fdb69291d Commit 8 9a25b2ea11d0aa19af33761c7c81bd10d0f17949 Commit 7 c7b02e556738e7cd2a2459e07af731e5677ab090 Commit 6 1bf5b98cf90340e714bd0bd13c7721f94749fa5f Commit 5 8a9c25eda46b3424a870584e4a8a4f90f6e5aa2c Commit 4 0ecf2464ec82a3aa74abb6f6c2127315ef4c2495 Commit 3 e18bdb2ae52f2149421965ff69d96dba46cd7c0a Commit 2 cf10d47067bef60ae787e5421e32b4541bdf52be Commit 1  Here is my initial solution: $ v=(1 2); c=(5 10); for i in 0 1; do git tag -a v0.0.${v[$i]} -m "Tag v0.0.${v[$i]}" $(git rev-parse :/'Commit '${c[$i]}); done  This clearly works as desired: $ git log --format=oneline --decorate
c62b2f68ea6b8ff047dd5f1e99010ebf434709ef Commit 9
feec2d76977521018ff33fd6e109530fdb69291d Commit 8
9a25b2ea11d0aa19af33761c7c81bd10d0f17949 Commit 7
c7b02e556738e7cd2a2459e07af731e5677ab090 Commit 6
1bf5b98cf90340e714bd0bd13c7721f94749fa5f (tag: v0.0.1) Commit 5
8a9c25eda46b3424a870584e4a8a4f90f6e5aa2c Commit 4
0ecf2464ec82a3aa74abb6f6c2127315ef4c2495 Commit 3
e18bdb2ae52f2149421965ff69d96dba46cd7c0a Commit 2
cf10d47067bef60ae787e5421e32b4541bdf52be Commit 1


But it seems excessively verbose and fragile.

Questions:

• Instead of separately defining three arrays (v=(1 2); c=(5 10); for i in 0 1), can I create a single array for the two independent variables (e.g. in pseudocode: vc=((1 5) (2 10))) and then determine the incrementor's value automatically upon each iteration?

• Instead of using a for loop, can I use brace expansion or some other more concise and elegant approach?

I would prefer a one-liner, but I do not require a one-liner.

• Does it need to be a one-liner? – chicks Jun 18 '17 at 14:46
• @chicks, thanks for asking! Question updated accordingly :) – sampablokuper Jun 18 '17 at 19:19

You could extract the main operation to a function:

tag() { local v=$1 c=$2; git tag -a v0.0.$v -m "Tag v0.0.$v" $(git rev-parse :/"Commit$c"); }


And then call this function with the desired values:

tag 1 5
tag 2 10


This is very easy to read and understand.

As per using nested arrays, you cannot do that in Bash, but you can always emulate a 2D-array with appropriate indexing, for example:

vc=(1 5 2 10); for ((i = 0; i < ${#vc[@]}; i+=2)); do v=${vc[i]}; c=${vc[i+1]}; git tag -a v0.0.$v -m "Tag v0.0.$v"$(git rev-parse :/"Commit $c"); done  If I had to write a "single-use" quickie command, I would write it like this: while read version commit; do git tag -a v0.0.$version -m "Tag v0.0.$version"$(git rev-parse :/'Commit '\$commit)
done


Then, as the script awaits input, type:

1 5
2 10
^D


In contrast to your parallel arrays, the main advantage of this approach is that each line constitutes a commit-version pair.

• Nifty! Not quite what I had in mind, but I like it :) Upvoted accordingly :) – sampablokuper Jun 19 '17 at 13:21