From 77323523c9aed412bf119bf7b827ce7f53350c77 Mon Sep 17 00:00:00 2001 From: Silvio Rhatto Date: Thu, 23 Nov 2017 11:53:51 -0200 Subject: Refactor checkout branch scripts --- git-checkout-branch | 88 ++++++++++++++++++++++++++++++++++++++++ git-submodule-checkout-branch | 33 --------------- git-submodules-checkout-branch | 7 ++++ git-submodules-checkout-branches | 7 ---- 4 files changed, 95 insertions(+), 40 deletions(-) create mode 100755 git-checkout-branch delete mode 100755 git-submodule-checkout-branch create mode 100755 git-submodules-checkout-branch delete mode 100755 git-submodules-checkout-branches diff --git a/git-checkout-branch b/git-checkout-branch new file mode 100755 index 0000000..625cbd1 --- /dev/null +++ b/git-checkout-branch @@ -0,0 +1,88 @@ +#!/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 diff --git a/git-submodule-checkout-branch b/git-submodule-checkout-branch deleted file mode 100755 index 6613bf4..0000000 --- a/git-submodule-checkout-branch +++ /dev/null @@ -1,33 +0,0 @@ -#!/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 diff --git a/git-submodules-checkout-branch b/git-submodules-checkout-branch new file mode 100755 index 0000000..773ab21 --- /dev/null +++ b/git-submodules-checkout-branch @@ -0,0 +1,7 @@ +#!/bin/bash +# +# Checkout branches in all submodules, recursivelly. +# + +# Run recursivelly for each submodule +git submodule foreach --recursive git-checkout-branch diff --git a/git-submodules-checkout-branches b/git-submodules-checkout-branches deleted file mode 100755 index f254ca9..0000000 --- a/git-submodules-checkout-branches +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -# -# Checkout branches in all submodules, recursivelly. -# - -# Run recursivelly for each submodule -git submodule foreach --recursive git-submodule-checkout-branch -- cgit v1.2.3