--- /dev/null
+#!/bin/bash
+#
+# Checkout branche a submodule.
+#
+# Searches a branch whose latest commit is the given branch and checkout to
+# that branch.
+#
+# Intended to run only in development environments. In production, always use
+# "git submodule update" only.
+#
+# There are a git-submodule feature to help tracking subdmoule branches:
+#
+# Git submodules: Specify a branch/tag - Stack Overflow
+# https://stackoverflow.com/questions/1777854/git-submodules-specify-a-branch-tag
+#
+# But note that using "git submodule add -b" coupled with "git submodule update --remote"
+# does not leverage the additional security of using the commit ID of the submodule's
+# commit recoreded in the parent commit.
+#
+# Given that the benefits of using git-submodule is both tracking sub-repository state
+# and ensuring a basic integrity check on it's contents, the following implementation
+# is different from the sollution given by the article above by ensuring we only checkout
+# to the branch if it's latest commit is the one having the revision recorded by the parent
+# repository.
+
+# Parameters
+BASENAME="`basename $0`"
+
+# Checkout the branch containing a commit
+function checkout_branch {
+ # Check if we are in a detached HEAD
+ if git branch | grep -q '* (HEAD detached'; then
+ # Determine the commit we're in
+ local commit="`git log -n 1 | head -1 | cut -d ' ' -f 2`"
+
+ # Get all branches were the commit occurs
+ local branches="`git branch -r --contains $commit 2> /dev/null | grep -v 'HEAD'`"
+
+ # Get the first branch whose last commit is our commit
+ if [ ! -z "$branches" ]; then
+ for branch in $branches; do
+ branch_commit="`git log $branch -1 | head -1 | cut -d ' ' -f 2`"
+
+ # In the future some criteria might be stablished to determine how to decide
+ # if the comment is present in more than one branch. Which one to prioritize?
+ #
+ # - A branch recorded in `config -f $toplevel/.gitmodules submodule.$name.branch`?
+ # - A topic branch in the form of "feature/"?
+ # - The "develop" branch?
+ #
+ # This whole business is getting too complicated!
+ if [ "$commit" == "$branch_commit" ]; then
+ # Remove an eventual remote name from branch name
+ local_branch="`echo $branch | sed -e 's|^[^/]*/||'`"
+
+ # Get the commit of the local branch for the case the matching branch is a remote one
+ if git branch | grep -q " $local_branch$"; then
+ local_commit="`git log $local_branch -1 | head -1 | cut -d ' ' -f 2`"
+ else
+ # Branch does not exist
+ local_commit="$branch_commit"
+ fi
+
+ # Checkout to the given commit
+ #
+ # Note that there's space for a race condition here during this
+ # checkout and the merge from the next statement block.
+ #
+ # So be careful and use this script just in development.
+ git checkout $local_branch
+
+ # Update the local branch if needed
+ if [ "$branch_commit" != "$local_commit" ]; then
+ git merge $branch
+ fi
+
+ # Done
+ break
+ fi
+ done
+ else
+ echo "$BASENAME: no such branch containing $commit as it's latest commit"
+ fi
+ fi
+}
+
+# Dispatch
+checkout_branch
+++ /dev/null
-#!/bin/bash
-#
-# Checkout branche a submodule.
-#
-
-# Parameters
-BASENAME="`basename $0`"
-
-# Checkout the branch containing a commit
-function checkout_branch {
- # Check if we are in a detached HEAD
- if git branch | grep -q '* (HEAD detached'; then
- # Determine the commit we're in
- local commit="`git log -n 1 | head -1 | cut -d ' ' -f 2`"
-
- # Get the first remote branch that contains our commit
- #
- # In the future some criteria might be stablished to determine how to decide
- # if the comment is present in more than one branch. Which one to prioritize?
- # A topic branch in the form of "feature/"? The "develop" branch?
- local branch="`git branch -r --contains $commit 2> /dev/null | grep -v 'HEAD' | head -1 | sed -e 's|^[^/]*/||'`"
-
- # Checkout to the given commit
- if [ ! -z "$branch" ]; then
- git checkout $branch
- else
- echo "$BASENAME: no such branch containing dangling commit $commit"
- fi
- fi
-}
-
-# Dispatch
-checkout_branch