Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false

[*.{yml,yaml}]
indent_size = 2
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

.idea/
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ $ git commit -m "Add Matryoshka submodule"

3. Decide if you would just like to update and leave the changes **unstaged and uncommitted** or generate a commit for each updated submodule individually.

## Options

* Use `-a` option to preselect `all` in the selection of submodules.
* Use `-c` to preselect the auto commit mode.
* Use `-m "My update message"` to specify the text of the commit message. The `<submodule>` and `<hash>` placeholders within the string will be replaced with their respective values.


## How does auto-commit work?

Expand Down
65 changes: 33 additions & 32 deletions handle_sub.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,46 +10,46 @@
# behave relative to the _submodule's root_.
# It takes one parameter (bool) which toggles auto-committing.


# highlight textsections within echo
bold=$(tput bold)
normal=$(tput sgr0)

# assign parameters
should_autocommit="$1"
stand_alone_repo="$2"
update_message="$3"

if [ ! "$stand_alone_repo" = "all" ]; then
# Only one submodule should be updated
if [ ! "$stand_alone_repo" = "$name" ]; then
# Ignore others
printf "%s > Skipping %s%s\n\n\n" "${bold}" "$name" "${normal}"
exit 0
fi
# Only one submodule should be updated
if [ ! "$stand_alone_repo" = "$name" ]; then
# Ignore others
printf "%s > Skipping %s%s\n\n\n" "${bold}" "$name" "${normal}"
exit 0
fi
fi

##
# Check if the submodule is dirty. Prevent dirty submodule commits
# under all circumstances

# Number of files added to the index (but uncommitted)
staged_count="$(git status --porcelain 2>/dev/null| grep -c "^M")"
staged_count="$(git status --porcelain 2>/dev/null | grep -c "^M")"

# Number of files that are uncommitted and not added
untracked_count="$(git status --porcelain 2>/dev/null| grep -c "^ M")"
untracked_count="$(git status --porcelain 2>/dev/null | grep -c "^ M")"

# Number of total uncommited files
total_count="$(git status --porcelain 2>/dev/null| grep -Ec "^(M| M)")"
total_count="$(git status --porcelain 2>/dev/null | grep -Ec "^(M| M)")"

# Debug-Log kept for future reference
# echo $staged_count
# echo $untracked_count
# echo $total_count

if ! [ "$staged_count" -eq 0 -a "$untracked_count" -eq 0 -a "$total_count" -eq 0 ]; then
# Dirty Working Area
printf "%s > Dirty submodule detected! Skipping %s%s\n\n\n" "${bold}" "$name" "${normal}"
exit 0
# Dirty Working Area
printf "%s > Dirty submodule detected! Skipping %s%s\n\n\n" "${bold}" "$name" "${normal}"
exit 0
fi

# Let's see if an update is necessary
Expand All @@ -67,40 +67,41 @@ upstream_status="$(git log HEAD..origin/"$active_branch" --oneline)"
# At this point we have stashed all other changes within the parent repo.
# If there are modifications left, the repo has uncommited updates
cd "$parent_root" || return
uncommited_update_count="$(git status "$name" --porcelain 2>/dev/null| grep -Ec "^(M| M)")"
uncommited_update_count="$(git status "$name" --porcelain 2>/dev/null | grep -Ec "^(M| M)")"
cd "$name" || return


# $upstream_status is empty unless changes on origin are available
if [ ! "$upstream_status" = "" ] || [ "$uncommited_update_count" -gt 0 ]; then

git pull origin "$active_branch" --quiet
git pull origin "$active_branch" --quiet

updated_hash="$(git rev-parse HEAD)"
sub_head_msg="$(git rev-list --format=%B --max-count=1 "$updated_hash")"

updated_hash="$(git rev-parse HEAD)"
sub_head_msg="$(git rev-list --format=%B --max-count=1 "$updated_hash")"
# short_old_hash="$(git rev-parse --short "$sha1")"
short_new_hash="$(git rev-parse --short "$updated_hash")"

# short_old_hash="$(git rev-parse --short "$sha1")"
short_new_hash="$(git rev-parse --short "$updated_hash")"
printf "\n > FROM: %s%s%s\n" "${bold}" "$old_head_msg" "${normal}"
printf "\n > TO: %s%s%s\n" "${bold}" "$sub_head_msg" "${normal}"

printf "\n > FROM: %s%s%s\n" "${bold}" "$old_head_msg" "${normal}"
printf "\n > TO: %s%s%s\n" "${bold}" "$sub_head_msg" "${normal}"
if [ "$should_autocommit" = "1" ]; then

if [ "$should_autocommit" = "1" ]; then
# Reference submodule new head-sha1 in commit msg
commit_msg=${update_message/<submodule>/"$name"}
commit_msg=${commit_msg/<hash>/$short_new_hash}

# Reference submodule new head-sha1 in commit msg
commit_msg="Update $name to $short_new_hash"
printf "\n > Committing: %s%s%s\n\n\n" "${bold}" "$commit_msg" "${normal}"
printf "\n > Committing: %s%s%s\n\n\n" "${bold}" "$commit_msg" "${normal}"

# Move cwd out of submodule into super-projects root
cd "$parent_root" || return
# Move cwd out of submodule into super-projects root
cd "$parent_root" || return

git add "./$name"
git commit -m "$commit_msg" --quiet
git add "./$name"
git commit -m "$commit_msg" --quiet

fi
fi

else
printf "%s > Already up to date.%s\n\n\n" "${bold}" "${normal}"
printf "%s > Already up to date.%s\n\n\n" "${bold}" "${normal}"
fi

exit 0
167 changes: 108 additions & 59 deletions matryoshka.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,45 @@

clear

#getopts "acm:"

# Parsing options
#while getopts "ac" OPTION; do
# case $OPTION in
# a)
# selection="all"
# ;;
# c)
# auto_commit=1
# ;;
# *)
# echo "Incorrect options provided"
# exit 1
# ;;
# esac
#done

while getopts "acm:" options; do
case "${options}" in
a)
selection="all"
;;
c)
auto_commit=1
;;
m)
commit_msg=${OPTARG}
;;
:)
echo "Error: -${OPTARG} requires an argument."
exit
;;
*)
exit
;;
esac
done

# highlight textsections within echo
bold=$(tput bold)
normal=$(tput sgr0)
Expand All @@ -23,108 +62,118 @@ git_version_required="2.12"
function version { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'; }

if [ "$(version "$git_version_required")" -gt "$(version "$git_version_installed")" ]; then
# Outdated git version detected
echo -e "This script requires git version >=${bold}$git_version_required${normal}.\nInstalled git version is ${bold}$git_version_installed${normal}.\n\nPlease update git."
exit 1
# Outdated git version detected
echo -e "This script requires git version >=${bold}$git_version_required${normal}.\nInstalled git version is ${bold}$git_version_installed${normal}.\n\nPlease update git."
exit 1
fi

echo -e "Initiated submodule update. Cancel with CTRL-C.\n"

# Get names of all submodules
# Using the foreach functionality removes dependence on tools like awk / sed
submodules=( "all" )
while IFS= read -r line; do
submodules+=( "$line" )
done < <( git submodule --quiet foreach --recursive 'echo $name' )

# Present selection to user
PS3="${bold}Select modules you would like to update: ${normal}"

select module in "${submodules[@]}"
do
selection=$module
printf "\n %s%s selected%s\n\n\n" "${bold}" "$selection" "${normal}"
break
done
if [ "$selection" == "" ]; then
# Get names of all submodules
# Using the foreach functionality removes dependence on tools like awk / sed
submodules=("all")
while IFS= read -r line; do
submodules+=("$line")
done < <(git submodule --quiet foreach --recursive 'echo $name')

# Present selection to user
PS3="${bold}Select modules you would like to update: ${normal}"

select module in "${submodules[@]}"; do
selection=$module
printf "\n %s%s selected%s\n\n\n" "${bold}" "$selection" "${normal}"
break
done
fi

auto_commit=0
auto_commit=${auto_commit:=0}
did_stash=0

# Ask for confirmation before auto-committing
PS3="${bold}Generate commit(s) for updated submodule(s)?: ${normal}"
select yn in "Yes" "No"; do
case $yn in
Yes ) auto_commit=1; break;;
No ) auto_commit=0; break;;
esac
done
if [ "$auto_commit" == 0 ]; then
# Ask for confirmation before auto-committing
PS3="${bold}Generate commit(s) for updated submodule(s)?: ${normal}"
select yn in "Yes" "No"; do
case $yn in
Yes)
auto_commit=1
break
;;
No)
auto_commit=0
break
;;
esac
done
fi

# Number of files added to the index (but uncommitted)
staged_count="$(git status --porcelain 2>/dev/null| grep -c "^M")"
staged_count="$(git status --porcelain 2>/dev/null | grep -c "^M")"

# Number of files that are uncommitted and not added
untracked_count="$(git status --porcelain 2>/dev/null| grep -c "^ M")"
untracked_count="$(git status --porcelain 2>/dev/null | grep -c "^ M")"

# Number of total uncommited files
total_count="$(git status --porcelain 2>/dev/null| grep -Ec "^(M| M)")"
total_count="$(git status --porcelain 2>/dev/null | grep -Ec "^(M| M)")"

# Debug-Log kept for future reference
# echo $staged_count
# echo $untracked_count
# echo $total_count

if ! [ "$staged_count" -eq 0 -a "$untracked_count" -eq 0 -a "$total_count" -eq 0 ]; then
# Dirty Working Area
# Dirty Working Area

if [ "$auto_commit" -eq 1 ]; then
# We want to auto-commit cleanly. Stashing user changes...
if [ "$auto_commit" -eq 1 ]; then
# We want to auto-commit cleanly. Stashing user changes...

##
# The working directory is listed as "dirty" when the submodules have been updated
# prior to Matryoshka. Stash will not save them, because they are already taken care of
# by Git. To avoid popping an old stash accidentally, we compare the stash counts and adjust
# actions accordingly
##
# The working directory is listed as "dirty" when the submodules have been updated
# prior to Matryoshka. Stash will not save them, because they are already taken care of
# by Git. To avoid popping an old stash accidentally, we compare the stash counts and adjust
# actions accordingly

stash_count_old=$(git stash list | wc -l)
stash_count_old=$(git stash list | wc -l)

# Stash save (q)uietly, including (u)ntracked files, also adding a description
git stash save --quiet --include-untracked "Submodule update $(date)"
# Stash save (q)uietly, including (u)ntracked files, also adding a description
git stash save --quiet --include-untracked "Submodule update $(date)"

stash_count_new=$(git stash list | wc -l)
stash_count_new=$(git stash list | wc -l)

# Debug-Log kept for future reference
# echo "Stash Count Old: $stash_count_old"
# echo "Stash Count New: $stash_count_new"
# Debug-Log kept for future reference
# echo "Stash Count Old: $stash_count_old"
# echo "Stash Count New: $stash_count_new"

# Check if a new stash has been created
if [ "$stash_count_new" -gt "$stash_count_old" ]; then
echo -e "\nDirty working directory has been auto-stashed.\n"
# Check if a new stash has been created
if [ "$stash_count_new" -gt "$stash_count_old" ]; then
echo -e "\nDirty working directory has been auto-stashed.\n"

stash_sha1="$(git rev-parse stash@\{0\})"
echo -e "Autostashed working directory sha1: ${bold}$stash_sha1\n${normal}"
did_stash=1
fi
stash_sha1="$(git rev-parse stash@\{0\})"
echo -e "Autostashed working directory sha1: ${bold}$stash_sha1\n${normal}"
did_stash=1
fi

fi
fi

fi

##
# Getting absolute script path — this is not as trivial as you may think
# REFERENCE: https://stackoverflow.com/q/4774054
containing_dir_path="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
containing_dir_path="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

commit_msg=${commit_msg:="Update <submodule> to <hash>"}

# Calling the update script for each sub. Passing the auto_commit flag
git submodule foreach "sh $containing_dir_path/handle_sub.sh $auto_commit $selection || :"
git submodule foreach "bash $containing_dir_path/handle_sub.sh $auto_commit $selection '$commit_msg' || :"

# Kept for debugging purposes
# git submodule foreach 'echo $path `git rev-parse HEAD` || :'

# Reapply stashed changes
if [ "$did_stash" -eq 1 ]; then
echo -e "\nReapplied stash.\n"
git stash pop --quiet
echo -e "\nReapplied stash.\n"
git stash pop --quiet
fi

exit 0