Working With Version Control
Git
First read: Getting Started - Git Official Documentation
After installing git
using your preferred method then follow: First Time Git
Setup - Git Official Documentation
This will help you set up your identity and editor.
Get familiar with the Pro Git Book it will function as a great reference in the future and explains both simple and advanced topics.
Read: How to Write a Git Commit Message
Tricks and other useful stuff
Some of there are short versions from the Pro Git Book
Rebase
Never use git rebase
when standing in the master
branch.
Common use cases
Update the current branch with the latest changes from the base branch.
git rebase <base-branch>
Rebase - Git Official Documentation
Combine multiple commits into one commit. This is useful when creating a pull request and before merging a pull request after a review.
git rebase --interactive <base-branch>
This will bring up your configured editor with content similar to this.
pick f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file
# Rebase 710f0f8..a5f4a0d onto 710f0f8
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
Replace pick
with squash
for all the commits except the first one and save
the file. This will combine all the changes from the commits into a new commit.
pick f7f3f6d changed my name a bit
squash 310154e updated README formatting and added blame
squash a5f4a0d added cat-file
# Rebase 710f0f8..a5f4a0d onto 710f0f8
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
Now run git push --force
to update the remote version of the branch
Rewriting-History - Git Official Documentation
Useful aliases
Git Aliases used to create new git
subcommands. They are
stored in your ~/.gitconfig
file under the [alias]
entry.
A compact and pretty log:
[alias]
lg = log --date-order --graph --decorate --pretty=format:'%C(yellow)%h%Creset %C(magenta)%d%Creset %s %C(blue)(%an)%Creset %C(green)%cr%Creset'
Example:
git lg
Generate a .gitignore
file:
[alias]
ignore = "!gi() { curl -L -s https://www.gitignore.io/api/$@ ;}; gi"
Example:
git ignore vim,linux,macos,python > .gitignore
Useful scripts
Here are some useful scripts to automate some of our workflows when using Git.
Store these scripts in a directory that you have in your $PATH
and use them
as subcommands in git
e.g. git merge-pull-request
.
git-create-pull-request
Creates a new branch for your pull request and pushes that branch to the remote repository.
Command example
git create-pull-request my-branch
Script code
#!/usr/bin/env bash
set -e
branch=${1}
base="master"
echo "Creating pull request ${branch} from ${base}"
git checkout $base
git pull --rebase
git checkout -b $branch
git push --set-upstream origin $branch
git-update-pull-request
Updates the local and remote version of your branch with changes from the remote base branch.
Command example
# With the branch checked out
git update-pull-request
# Without the branch checked out
git update-pull-request my-branch
Script code
#!/usr/bin/env bash
set -e
current_branch=$(git rev-parse --abbrev-ref HEAD)
branch=${1:-$current_branch}
base="master"
echo "Updating pull request ${branch} from ${base}"
git checkout $base
git pull --rebase
git checkout $branch
git rebase $base
git push --force
git-merge-pull-request
Merges a branch into base branch and updates the remote base branch. Deletes the local and remote pull request branch if merging was uploaded. This makes sure you merge pull request correctly without using the GitHub merge button (that button often creates a merge commit).
Command example
# With the branch checked out
git merge-pull-request
# Without the branch checked out
git merge-pull-request my-branch
Script code
#!/usr/bin/env bash
set -e
current_branch=$(git rev-parse --abbrev-ref HEAD)
branch=${1:-$current_branch}
base="master"
echo "Merging pull request ${branch} into ${base}"
git checkout $base
git pull --rebase
git merge --ff-only $branch
git push
git branch -d $branch
git push origin :$branch
Shell prompt
Displaying some basic information about the git repository you are on your prompt will help you know the state of you repository without running commands.
Activation
In ~/.bash_profile
add:
source $HOME/.bash_prompt
Code
Store the code bellow in ~/.bash_prompt
# Shamelessly copied from https://github.com/gf3/dotfiles
# Screenshot: http://i.imgur.com/s0Blh.png
# -*- mode: sh -*-
# vi: set ft=sh :
BOLD="\033[1m"
RESET="\033[m"
BLACK="\033[0;30"
RED="\033[0;31m"
GREEN="\033[0;32m"
YELLOW="\033[0;33m"
BLUE="\033[0;34m"
MAGENTA="\033[0;35m"
CYAN="\033[0;36m"
WHITE="\033[0;37m"
BRIGHT_BLACK="\033[0;90"
BRIGHT_RED="\033[0;91m"
BRIGHT_GREEN="\033[0;92m"
BRIGHT_YELLOW="\033[0;93m"
BRIGHT_BLUE="\033[0;94m"
BRIGHT_MAGENTA="\033[0;95m"
BRIGHT_CYAN="\033[0;96m"
BRIGHT_WHITE="\033[0;97m"
BOLD_BLACK="\033[1;30"
BOLD_RED="\033[1;31m"
BOLD_GREEN="\033[1;32m"
BOLD_YELLOW="\033[1;33m"
BOLD_BLUE="\033[1;34m"
BOLD_MAGENTA="\033[1;35m"
BOLD_CYAN="\033[1;36m"
BOLD_WHITE="\033[1;37m"
export BLACK
export RED
export GREEN
export YELLOW
export BLUE
export MAGENTA
export CYAN
export WHITE
export BOLD
export RESET
export BOLD_BLACK
export BOLD_RED
export BOLD_GREEN
export BOLD_YELLOW
export BOLD_BLUE
export BOLD_MAGENTA
export BOLD_CYAN
export BOLD_WHITE
function parse_git_branch {
branch_prefix=""
branch=$(git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/')
git_status_clean=$(git status --porcelain 2> /dev/null)
indexed_modified=$(echo -e "$git_status_clean" | grep "^M" | wc -l | awk '{print $1}')
not_indexed_modified=$(echo -e "$git_status_clean" | grep "^.M" | wc -l | awk '{print $1}')
indexed_new=$(echo -e "$git_status_clean" | grep "^A" | wc -l | awk '{print $1}')
not_indexed_new=$(echo -e "$git_status_clean" | grep "^.A" | wc -l | awk '{print $1}')
indexed_deleted=$(echo -e "$git_status_clean" | grep "^D" | wc -l | awk '{print $1}')
not_indexed_deleted=$(echo -e "$git_status_clean" | grep "^.D" | wc -l | awk '{print $1}')
untracked=$(echo -e "$git_status_clean" | grep "^??" | wc -l |awk '{print $1}')
remote=""
curr_remote=$(git config branch.${branch}.remote 2> /dev/null)
if [[ -n $curr_remote ]]; then
curr_merge_branch=$(git config branch.${branch}.merge | sed -e "s/refs\/heads\///g");
ahead=$(git rev-list --left-only --count ${branch}...origin/${curr_merge_branch} 2> /dev/null)
behind=$(git rev-list --left-only --count origin/${curr_merge_branch}...${branch} 2> /dev/null)
if [[ -n $ahead && $ahead != 0 ]]; then
remote="$remote ${YELLOW}↑$ahead${RESET}"
fi
if [[ -n $behind && $behind != 0 ]]; then
remote="$remote ${YELLOW}↓$behind${RESET}"
fi
fi
local workspace=""
if [[ $untracked != 0 || $not_indexed_modified != 0 || $not_indexed_deleted != 0 ]]; then
workspace=" ${RED}+$untracked ~$not_indexed_modified -$not_indexed_deleted${RESET}"
fi
local staged=""
if [[ $indexed_new != 0 || $indexed_modified != 0 || $indexed_deleted != 0 ]]; then
staged=" ${GREEN}+$indexed_new ~$indexed_modified -$indexed_deleted${RESET}"
fi
if [ $branch ]; then
echo -e " on $MAGENTA$branch_prefix $branch$RESET$workspace$staged$remote"
fi
}
PREFIX=""
if [[ -n "$SSH_CLIENT$SSH2_CLIENT$SSH_TTY" ]] ; then
PREFIX="\[$RED\]\u\[$RESET\] at \[$YELLOW\]\h\[$RESET\] in "
fi
export PS1="${PREFIX}\[$BLUE\]\w\[$RESET\]\$([[ -n \$(git branch 2> /dev/null) ]])\$(parse_git_branch)\n\[$YELLOW\]❯ \[$RESET\]"
export PS2="\[$YELLOW\]→ \[$RESET\]"