Getting started

Cloning a repo and setting a custom email address for commit logs:

git clone https://git.mycorporatecompany.example.com/monolith
cd monolith
git config user.email tdonohue@mycorporatecompany.example.com

Enabling the credential cache, timeout in seconds (default cache timeout = 15 minutes):

git config --global credential.helper cache
git config --global credential.helper 'cache --timeout=3600'

Working with files (checkouts and commits)

Undo changes (check out) to a file filename.txt (discard unstaged changes):

git checkout -- filename.txt

Check out the branch stuff:

git checkout stuff

Add more files to the last commit (when you’ve just committed but you forgot about a few files that need to go into the same commit):

git add <left_out_files>
git commit --amend --no-edit

Stashing

Stash unfinished changes:

git stash push -m "My stash description" <pathspec>
# or just 'git stash'

To include untracked files in a stash:

git stash --all

List all stashes:

git stash list

Apply the most recent stash and keep the changes in the stash stack:

git stash apply

Apply the most recent stash and remove the changes from the stash stack:

git stash pop

Create a new branch from a stash:

git stash branch new_branch_name stash_id

Branches and Tags

List all branches (including remotes):

git branch -a

Check out a new local branch mybranch which tracks the remote branch mybranch (if it exists):

git checkout mybranch

Fetch branches from the remote repository called origin:

git fetch origin

Fetch all branches and tags from all remote repositories:

git fetch --all --tags --prune

Create (checkout) a new branch:

git checkout -b mybranch

Create a new branch and commit existing, uncommitted work to it:

git checkout -b mynewbranch
git add .
git commit -m "New branchy stuff"

Push a branch remotely (to the origin remote repository):

git push -u origin mybranch

Delete a branch both locally and remotely:

git push origin -d mybranch
git branch -d mybranch

List all tags:

git tag

Check out a tag:

git checkout tags/<tag_name> -b <branch_name>

Merging and rebasing

Merge changes from the development branch into master, and squashing the intermediate commits:

git checkout master
git merge --squash develop
git commit

Merge changes from origin’s master branch into a public or shared development branch:

git checkout my-branch-name
git fetch origin
git merge origin/master

Or, use rebase, if:

  • Your branch is local only, and hasn’t been pushed to origin.

Rebase:

git checkout my-feature-branch
git merge origin/master

Merge conflicts

When Git sees a conflict between two branches being merged, it adds merge conflict markers into the code and marks the file as conflicted to let you resolve it.

Git conflict markers follow this syntax:

<<<<<<< HEAD
text from branch you are merging INTO (i.e. your local changes)
=======
text from commit that you are trying to merge IN (i.e. the remote changes, being merged in)
>>>>>>> somebranchidentifiergoeshere

Remote repositories

List all remote repositories:

git remote -v

Forks

Sync a fork to keep it up-to-date with its upstream repository:

cd my-forked-repo
git remote add upstream https://github.com/username/original-repo
git fetch upstream
git checkout master
git merge upstream/master
git push origin master

Exporting/packaging

Export a Git repository to a zip:

git archive -o /path/to/ouputfile.zip HEAD

Submodules

Add a submodule to an existing project:

git submodule add https://github.com/username/example-repo new-directory-name

Emergency panic stations

Revert the repository to the last committed state (any uncommitted work will be lost!):

git reset --hard HEAD

Remove a file from a historical commit:

git filter-branch --index-filter \
    'git rm --cached --ignore-unmatch path/to/file/to/be/deleted.xml' \
    --tag-name-filter cat -- --all

Advanced

Configure Git to use a SOCKS5 proxy (e.g. when using an SSH tunnel to reach the host), where the hostname lookup itself also must go through the proxy:

git config --global http.proxy socks5h://hostname:port

Hooks

Example hook to build a Jekyll site when pushed

Add the following to your remote repo’s hooks/post-receive file.

Note the use of bash -l which inherits your login shell and all the path settings required for running commands like bundle, etc:

#!/bin/bash -l

GIT_REPO=$HOME/git/myrepo.git
TMP_GIT_CLONE=$HOME/tmp/mysite.co.uk
PUBLIC_WWW=/var/www/mysite.co.uk/public_html
BRANCH_NAME=master
export JEKYLL_ENV=production

git clone -b $BRANCH_NAME $GIT_REPO $TMP_GIT_CLONE

echo "Cloned repo. Running timestamp update..."

pushd $TMP_GIT_CLONE
IFS="
"
# TODO fix this, because git ls-files clearly returns nothing
# when this script is invoked using git push REMOTE branch
for FILE in $(git ls-files)
do
    TIME=$(git log --pretty=format:%cd -n 1 --date=iso -- "$FILE")
    TIME=$(date --date="$TIME" +%Y%m%d%H%M.%S)
    echo "Touching $FILE to $TIME"
    touch -m -t "$TIME" "$FILE"
done
popd

pushd $TMP_GIT_CLONE
bundle install
bundle exec jekyll build -s $TMP_GIT_CLONE -d $PUBLIC_WWW
popd

echo "Site built. Removing temp dir..."

rm -Rf $TMP_GIT_CLONE

echo "Done."
exit

Branching models

Some different thoughts on Git branching:

Forking Flow

  • Developers fork from the master in the main repository
  • Create a feature branch in the developer’s fork.
  • Feature branches should be short-lived, i.e. merged after a few days max
  • Merge frequently to master, either manually or automatically from the CICD pipeline after passing tests.

Master branch is always deployable to production/master is always stable

  • Only working, tested code makes it into master
  • Anything in master can be deployed by anyone at any time
  • Have Jenkins create a build artifact on every commit to master, and then move this artifact through staging and production.
  • Rebase on master before merging