diff --git a/dev-support/rebase_all_git_branches.sh b/dev-support/rebase_all_git_branches.sh new file mode 100755 index 00000000000..19364709e60 --- /dev/null +++ b/dev-support/rebase_all_git_branches.sh @@ -0,0 +1,204 @@ +#!/bin/bash + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This script assumes that your remote is called "origin" +# and that your local master branch is called "master". +# I am sure it could be made more abstract but these are the defaults. + +# Edit this line to point to your default directory, +# or always pass a directory to the script. + +DEFAULT_DIR="EDIT_ME" + +function print_usage { + cat << __EOF + +$0: A script to manage your Apache HBase Git repository. + +If run with no arguments, it reads the DEFAULT_DIR variable, which you +can specify by editing the script. + +Usage: $0 [-d ] + $0 -h + + -h Show this screen. + -d The absolute or relative directory of your Git repository. +__EOF +} + +function get_all_branches { + # Gets all git branches present locally + all_branches=() + for i in `git branch --list | sed -e "s/\*//g"`; do + all_branches+=("$(echo $i | awk '{print($1)}')") + done +} + +function get_tracking_branches { + # Gets all branches with a remote tracking branch + tracking_branches=() + for i in `git branch -lvv | grep "\[origin/" | sed -e 's/\*//g' | awk {'print $1'}`; do + tracking_branches+=("$(echo $i | awk '{print($1)}')") + done +} + +function check_git_branch_status { + # Checks the current Git branch to see if it's dirty + # Returns 1 if the branch is dirty + git_dirty=$(git diff --shortstat 2> /dev/null | wc -l|awk {'print $1'}) + if [ "$git_dirty" -ne 0 ]; then + echo "Git status is dirty. Commit locally first." >&2 + exit 1 + fi +} + +function get_jira_status { + # This function expects as an argument the JIRA ID, + # and returns 99 if resolved and 1 if it couldn't + # get the status. + + # The JIRA status looks like this in the HTML: + # span id="resolution-val" class="value resolved" > + # The following is a bit brittle, but filters for lines with + # resolution-val returns 99 if it's resolved + jira_url='https://issues.apache.org/jira/browse' + jira_id="$1" + status="$(curl -s $jira_url/$jira_id | \ + grep resolution-val | \ + sed -e "s/.*class=\"value\ //" | \ + cut -d'"' -f 1)" + if [ $? -ne 0 ]; then + echo "Could not get JIRA status. Check your network." >&2 + exit 1 + fi + if [ "$status" = "resolved" ]; then + return 99 + fi +} + +# Process the arguments +while getopts ":hd:" opt; do + case $opt in + d) + # A directory was passed in + dir="$OPTARG" + if [ ! -d "$dir/.git/" ]; then + echo "$dir does not exist or is not a Git repository." >&2 + exit 1 + fi + ;; + h) + # Print usage instructions + print_usage + exit 0 + ;; + *) + echo "Invalid argument: $OPTARG" >&2 + print_usage >&2 + exit 1 + ;; + esac +done + +if [ -z "$dir" ]; then + # No directory was passed in + dir="$DEFAULT_DIR" + if [ "$dir" = "EDIT_ME" ]; then + echo "You need to edit the DEFAULT_DIR in $0." >&2 + $0 -h + exit 1 + elif [ ! -d "$DEFAULT_DIR/.git/" ]; then + echo "Default directory $DEFAULT_DIR is not a Git repository." >&2 + exit 1 + fi +fi + +cd "$dir" + +# For each tracking branch, check it out and make sure it's fresh +# This function creates tracking_branches array and stores the tracking branches in it +get_tracking_branches +for i in "${tracking_branches[@]}"; do + git checkout -q "$i" + # Exit if git status is dirty + check_git_branch_status + git pull -q --rebase + status=$? + if [ "$status" -ne 0 ]; then + echo "Unable to pull changes in $i: $status Exiting." >&2 + exit 1 + fi + echo "Refreshed $i from remote." +done + +# Run the function to get the list of all branches +# The function creates array all_branches and stores the branches in it +get_all_branches + +# Declare array to hold deleted branch info +deleted_branches=() + +for i in "${all_branches[@]}"; do + # Check JIRA status to see if we still need this branch + # JIRA expects uppercase + jira_id="$(echo $i | awk '{print toupper($0)'})" + if [[ "$jira_id" == HBASE-* ]]; then + # Returns 1 if the JIRA is closed, 0 otherwise + get_jira_status "$jira_id" + jira_status=$? + if [ $jira_status -eq 99 ]; then + # the JIRA seems to be resolved or is at least not unresolved + deleted_branches+=("$i") + fi + fi + + git checkout -q "$i" + + # Exit if git status is dirty + check_git_branch_status + + # If this branch has a remote, don't rebase it + # If it has a remote, it has a log with at least one entry + git log -n 1 origin/"$i" > /dev/null 2>&1 + status=$? + if [ $status -eq 128 ]; then + # Status 128 means there is no remote branch + # Try to rebase against master + echo "Rebasing $i on origin/master" + git rebase -q origin/master > /dev/null 2>&1 + if [ $? -ne 0 ]; then + echo "Failed. Rolling back. Rebase $i manually." + git rebase --abort + fi + elif [ $status -ne 0 ]; then + # If status is 0 it means there is a remote branch, we already took care of it + echo "Unknown error: $?" >&2 + exit 1 + fi +done + +# Offer to clean up all deleted branches +for i in "${deleted_branches[@]}"; do + read -p "$i's JIRA is resolved. Delete? " yn + case $yn in + [Yy]) + git branch -D $i + ;; + *) + echo "To delete it manually, run git branch -D $deleted_branches" + ;; + esac +done +git checkout -q master +exit 0 diff --git a/src/main/docbkx/developer.xml b/src/main/docbkx/developer.xml index bc598d2e388..4a59cf45596 100644 --- a/src/main/docbkx/developer.xml +++ b/src/main/docbkx/developer.xml @@ -1780,6 +1780,40 @@ justification="I know what I'm doing") +
+ Git Best Practices + + + Use the correct method to create patches. See . + + + Avoid git merges. Use git pull --rebase or git + fetch followed by git rebase. + + + Do not use git push --force. If the push does not work, fix + the problem or ask for help. + + + Please contribute to this document if you think of other Git best + practices. +
+ <code>rebase_all_git_branches.sh</code> + The dev-support/rebase_all_git_branches.sh script is + provided to help keep your Git repository clean. Use the -h + parameter to get usage instructions. The script automatically refreshes your + tracking branches, attempts an automatic rebase of each local branch against its + remote branch, and gives you the option to delete any branch which represents a + closed HBASE- JIRA. The script has one optional configuration + option, the location of your Git directory. You can set a default by editing the + script. Otherwise, you can pass the git directory manually by using the + -d parameter, followed by an absolute or relative directory + name, or even '.' for the current working directory. The script checks the + directory for sub-directory called .git/, before + proceeding. +
+
Submitting Patches @@ -1787,7 +1821,8 @@ justification="I know what I'm doing") contribute patches in our new GIT context, caveat the fact that we have a different branching model and that we don't currently do the merge practice described in the following, the accumulo doc - on how to contribute and develop after our move to GIT is worth a read. + on how to contribute and develop after our move to GIT is worth a read. + See also . If you are new to submitting patches to open source or new to submitting patches to Apache, start by reading the Git git format-patch is preferred because it preserves - commit messages. Use git squash first, to combine - smaller commits into a single larger one. + commit messages. Use git rebase -i first, to combine + (squash) smaller commits into a single larger one. Do not use --no-prefix, even if you were in the habit of doing it previously.