#!/bin/bash # # Checkout a branch in a submodule whose latest commit matches the commit id # recorded in the parent repository submodule config. # # 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 { # Ensure we have the right ssh command GIT_SSH_COMMAND="`git config core.sshCommand`" # Fetch from all repositories if [ ! -z "$GIT_SSH_COMMAND" ]; then GIT_SSH_COMMAND="$GIT_SSH_COMMAND" git fetch --all else git fetch --all fi # 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