Partial Maven clean install after Git pull I had searched for a solution to mvn clean install on my changed projects after a git pull. I even asked a question over at SO about the same thing. The answer I got pointed me into creating my own script to achieve what I wanted to accomplish. So I went, I saw, and I conquered the script that I wanted. However, it is probably not efficient, and could use refactoring. I am new to bash scripting and learned a bit while doing this.

The structure I was targeting is a Maven POM Project. That project contains several sub-POM Projects, which in turn contain Maven Java Projects.

Example Structure:

-Parent
|-Sub_Parent_A
 |-Child_1
 |-Child_2
|-Sub_Parent_B
 |-Child_3
 |-Child_4


I wanted to only call mvn clean install if a project had files that changed in the child projects. This would reduce time on going to each project by hand and calling the command or by just calling the command on the parent project. 

The code, along with revisions, can also be found on GitHub

################################################################################
#
# License:.....GNU General Public License v3.0
# Author:......CodeMonkey
# Date:........14 November 2018
# Title:.......GitMavenCleanInstall.sh
# Description: This script is designed to cd to a set Maven POM Project,
#   perform a git remote update and pull, and clean install the changed
#   files projects.
# Notice:......The project structure this script was originally set to target
#   is structured as a Maven POM Project that contains several sub-POM Projects.
#   The sub-POM Projects contain Maven Java Application projects. The targets
#   should be easy to change, and allow for others to target other structures.
#
################################################################################
#
# Change History: N/A
#
################################################################################

#!/bin/bash
#Function to check if array has element
containsElement () {
    local e match="$1"
    shift
    for e; do [[ "$e" == "$match" ]] &amp;&amp; return 0; done
    return 1
}

#Navigate to the POM Project
cd PATH/TO/POM/PROJECT
#Remote update
git remote update -p
#Pull
git pull

#Get the current working branch
CURRENT_BRANCH="$(git branch | sed -n -e 's/^\* \(.*\)/\1/p')"
#Get the output of the command git diff
GIT_DIFF_OUTPUT="$(git diff --name-status HEAD@{1} ${CURRENT_BRANCH})"

#Split the diff output into an array
read -a GIT_DIFF_OUTPUT_ARY &lt;&lt;&lt; $GIT_DIF_OUTPUT
#Declare empty array for root path
declare -a GIT_DIFF_OUTPUT_ARY_ROOT_PATH=()
FORWARD='/'
#Loop diff output array
for i in "${GIT_DIFF_OUTPUT_ARY[@]}"
do
    #Check that the string is not 1 Character
    if [[ "$(echo -n $1 | wc -m)" != 1 ]]
    then
        #Split the file path by /
        IFS='/' read -ra SPLIT &lt;&lt;&lt; $i
        #Concatenate first path + / + second path
        path=${SPLIT[0]}$FORWARD${SPLIT[1]}
        #Call function to see if it already exists in the root path array
        containsElement "$path" "${GIT_DIFF_OUTPUT_ARY_ROOT_PATH[@]}"
        if [[ $? != 0 ]]
        then
            #Add the path since it was not found
            GIT_DIFF_OUTPUT_ARY_ROOT_PATH+=($path)
        fi
    fi
done

#Loop root path array
for val in ${GIT_DIFF_OUTPUT_ARY_ROOT_PATH[@]}
do
    #CD into root path
    cd $val
    #Maven call to clean install
    mvn -DskipTests=true --errors -T 8 -e clean install
    #CD back up before next project
    cd ../../
done
Take a cleaner approach if you can

The current approach is basically a dirty hack to work around Maven's inability to do incremental builds.
I recommend to start looking into another more modern build system that is able to re-build just what needs to be rebuilt.
Such as Gradle.

Always double-quote variables in arguments

As a rule of thumb, always put double-quotes around variables used as arguments. For example in all these places, there should be double-quotes around the variables:


read -a GIT_DIFF_OUTPUT_ARY &lt;&lt;&lt; $GIT_DIFF_OUTPUT
...

GIT_DIFF_OUTPUT="$(git diff --name-status HEAD@{1} ${CURRENT_BRANCH})"
...

IFS='/' read -ra SPLIT &lt;&lt;&lt; $i
...

GIT_DIFF_OUTPUT_ARY_ROOT_PATH+=($path)
...

for val in ${GIT_DIFF_OUTPUT_ARY_ROOT_PATH[@]}
...

cd $val
...



Avoid performance pitfalls

For each path in the diff,
the script builds the array GIT_DIFF_OUTPUT_ARY_ROOT_PATH with the first two path segments,
and keeps the elements unique by not adding a value if it's already in the array.
As the size of the array grows, this becomes increasingly inefficient,
as this is quadratic time complexity.

I believe the paths printed by git diff is sorted by default.
You can take advantage of that:
track the previous element,
and only add a new value when it's different from the previous.
You will achieve better performance and without using the containsElement function.

Use the exit code directly in conditional expressions

Instead of this:


containsElement "$path" "${GIT_DIFF_OUTPUT_ARY_ROOT_PATH[@]}"
if [[ $? != 0 ]]
then
    #Add the path since it was not found
    GIT_DIFF_OUTPUT_ARY_ROOT_PATH+=($path)
fi



You could use the exit code of the function directly in a conditional:

if ! containsElement "$path" "${GIT_DIFF_OUTPUT_ARY_ROOT_PATH[@]}"
then
    #Add the path since it was not found
    GIT_DIFF_OUTPUT_ARY_ROOT_PATH+=($path)
fi


Get current Git branch name using native Git commands

Instead of this:

CURRENT_BRANCH="$(git branch | sed -n -e 's/^\* \(.*\)/\1/p')"


This is a better, native way:

CURRENT_BRANCH="$(git rev-parse --abbrev-ref HEAD)"


Use native Bash to get string length

This line tries to check the length of a variable:


if [[ "$(echo -n $i | wc -m)" != 1 ]]



Although -m is used, the possible values will not be multibyte,
so the solution is equivalent to using -c.
In this case, a better solution exists with native Bash:

if [[ ${#i} != 1 ]]


Use better variable names

i is good for simple counters. It's not a good name for anything else.

What is FORWARD? path_separator or pathsep would have been better.