Turtle swimming close to a life preserver as a metaphor for developers using Git version management to stay afloat

Does this sound familiar?

It’s 5:30 pm on a Friday, and you’re glued to the screen. Your team has about a dozen pull requests to deal with, and the client is expecting a demo on Monday. Despite your best efforts, there’s still a ton of work to finish. How did this happen?

If you find yourself in this situation over and over again, you might not be using version management to the fullest. For developers with more passion than time, these tips, tricks, and aliases can act as a life preserver when you’re drowning in code.

Too busy to read? We understand. Go straight to the TL;DR section, complete with a list of all aliases mentioned.

For Repository Managers

Painless Pull Requests

If you’re the one managing a team’s central repository, you know how important it is to keep it production-ready. These strategies can help keep your central repository clean and lighten the load when merging pull requests.

Merge with Style

Merging branches and handling pull requests can be time-consuming. If git diff isn’t quite enough, you can use git mergetool to bring up an editor that will help you sort changes. There are also merge GUIs like Meld that can help you breeze past the inevitable merge conflict.

A relatively basic fix that many developers overlook is the use of git merge --no-ff. If you use the following alias, Git will maintain a history of merging in branches so that you can look back and see past merges.

alias merge = merge --no-ff

Cherry-pick prior commits

Sometimes, you need to pull in changes to a branch without merging all the commits before and after it. For example, someone made a bug-fix several commits ago that you want to merge. How can you merge only one commit while ignoring the rest?

The answer is git cherry-pick. Cherry-picking can ensure that you only merge clean commits into your central repository, and there are only a few steps necessary:

  1. Create a new branch off of your master branch.
  2. Find the commit hash (the alphanumeric id for the commit) of the commit that you want to merge in by entering the git log command.
A screenshot of a UNIX terminal with the output of the git log command to illustrate how one can retrieve a commit hash.
  1. Use git cherry-pick followed by the commit hash.

To cherry-pick a range of commits, use the following syntax:

git cherry-pick f1b62817d9^..98d2bba6d0

This stages commits f1b62817d9 through 98d2bba6d0. The ^ indicates that the first commit is included. Otherwise, only the commits after the first one will be included.

Completing Pull Requests Locally Using Bitbucket

You’ll sometimes want to pull down a fork locally before merging with your central repository. This allows you to test thoroughly in a local branch without the chance of contaminating your code in production.

Note that this tutorial covers the use of Bitbucket. If you’re using Github, there is a slightly different method that is already well-documented.

We’ll use a pretend repository for this tutorial: the ‘cuttle-repo’ from Cuttlesoft that has been forked by user emilyemorehouse. When following along, you can swap out the URL of the repository with your own.

Since we use Bitbucket, the URL of the base repository is:
https://bitbucket.org/cuttlesoft/cuttle-repo and the forked repository is https://bitbucket.org/emilyemorehouse/cuttle-repo.

A few key pieces of information:

  • Pull requests will merge in the entire branch (making branch standards VERY important)
  • New commits to a branch with an existing open pull request will also be included
  • If accepted, a new pull request on the same branch will pull in all commits from that branch, not just existing ones

Now for a step-by-step guide:

Acquire New Changes

  1. Address any outstanding changes after using git status to find them.
  2. Fetch changes from the branch you want – this will likely be a feature branch, as we encourage users to utilize standard branching structure.

Using SSH:

git fetch git@bitbucket.org:emilyemorehouse/cuttle-repo.git feature/new-thing

Using HTTPS:

git fetch https://bitbucket.org/emilyemorehouse/cuttle-repo feature/new-thing

View and Test Changes

  1. To view the changes that you’re about to merge, run: git diff master FETCH_HEAD

The master refers to the local branch we will be merging into. FETCH_HEAD contains all changes that were brought down from the forked repository, (they won’t be visible using git status quite yet).

  1. To see your new code before merging, run git checkout FETCH_HEAD. This will detach your HEAD, allowing you to test new changes in a clean environment, you should now see fetched changes when using git log.

Merge Your Pull Request/Branch

  1. Next, checkout a new branch for your changes. Don’t push this branch, but use correct naming convention: PR/new-thing. Run git checkout -b PR/new-thing.
  2. Now that your changes are populated to a new branch, merge it into your dedicated branch (or master).

You should also squash changes into a single commit using:

git checkout master
git merge --squash PR/new-thing
  1. Now it’s time to test. Make sure nothing breaks in your local repository. Test your code manually, run a test suite, and check for console errors.

Once done, commit and push:

git commit
git push

Alternative Steps

Steps 4 and 5 can be replaced, as it’s not absolutely necessary to checkout changes to a branch before merging. To merge into master you use the following commands:

git checkout master
git merge --squash FETCH_HEAD

Test thoroughly, then:

git commit
git push

Make changes to a pull request before merging

Sometimes you need to quick-fix a pull request before merging. It could be a new bug, an API key accidentally committed, or incomplete documentation. Either way, you can make fixes before merging to keep your repository clean.

Method 1: Keep Changes to the Forked Repository

Use this method to fix an error immediately.

Fetch and checkout new commits normally.

Using SSH:

git fetch git@bitbucket.org:emilyemorehouse/cuttle-repo.git feature/new-thing

Using HTTPS:

git fetch https://bitbucket.org/emilyemorehouse/cuttle-repo feature/new-thing

Then:

git checkout FETCH_HEAD

Make any changes then commit and note the commit ID:

git commit -m "[#XXXXXX] fixed the thing"<br>[detached HEAD 8bb13a2] [#XXXXXX] fixed the thing<br>1 file changed, 1 insertion(+)
git checkout master
git merge 8bb13a2
git push

Method 2: Make the changes mid-merge

Another option is to make changes while you test your merge in Step 6. Be wary, changes made here will not be made in their own commit and won’t be included in the automatic merge commit message. This will make them harder to find later on, so make sure everything is exactly right before using this option.

References:

For Developers

When pull requests go wrong, it’s usually the repository manager who feels the burn. Here are some strategies that will make life easier for your lead developer.

Branching-Off

There are several different branching strategies that teams can employ.

  • Release Branching keeps each release inside it’s own branch.
  • Feature Branching keeps work for a single feature inside a single branch
  • Task Branching is even more specific, and involves creating a new branch for every task, usually tied to a project management software like Jira or Pivotal Tracker.

In practice, most teams use a mix of these strategies, but either way, it’s important that everyone on your team is on the same page as far as what should be done in a branch. Generally, any experimental work, new features, or unstable code should be kept in a branch.

If you forgot to checkout a new branch before getting to work, you can use git checkout -b to create a new branch and check it out immediately without losing work.

Commit Constantly

Making frequent, focused commits is one of the most important things you can do to keep your repository in good shape. One logical code change should equal one commit. That way, bugs and merge conflicts are easier to find. Lumping a bunch of changes in a single commit is a great way to get yourself in hot water.

Also, you should always use git diff before committing to make sure you aren’t including any changes by accident, (a diff GUI like diff-so-fancy can help you see changes in greater detail).

Keep Messages On Point

Commit messages make your changes easier to navigate. Make sure that your commit messages indicate what parts of the code were changed, and why.

If you make a mistake in a commit message, use git commit --amend to open up your editor to correct it. Be sure to never amend a commit that has already been pushed up to a shared repository, though.

Fix Mistakes

If you get off-track while using Git, always correct course before pushing or merging with others.

Add Patch Mode

Did you dump too many changes into a single commit? Fear not. You can use “add patch mode” to select chunks of code for their own commits. Enter git add -p to sift through your changes and pick out individual lines for commits. Watch this video tutorial for add patch mode if you need help.

Stash

To switch tasks without committing (maybe to fix an urgent bug), you can use git stash to “stash” a snapshot of your files. You can then switch to another branch, make a commit, switch back, and use git stash apply to return your files to the “stashed” state.

Bisect

If you’re encountering a tricky bug and can’t identify the specific commit which caused it, you can use git bisect to pinpoint that guilty commit. We’d describe it here, but The Practical Dev already has a great tutorial that we will refer you to.

Aliases for Better Commits

amend

# Command
git amend

# Alias 
amend = commit --amend -C HEAD
  • What it does: Commits staged changes as an amendment to your previous commit while keeping the same message as your previous commit.
  • When to use it: When you need to fix a small mistake or typo without rolling back changes or creating a new commit just for a minor edit.

addmend

# Command
$ git addmend

# Alias
addmend = "!f() { git add --all && git amend; }; f"

# Note: uses amend alias
  • What it does: Adds all changes and commits them as amendment to your previous commit, keeping the same message as your previous commit.
  • When to use it: When you find yourself making corrections with ‘git amend’ and want to save a little bit of time/effort each time you do.

diffs

# Command
$ git diffs

# Alias
diffs = diff --staged
  • What it does: Allows you to diff staged files (using the --staged flag).
  • When to use it: When you want to view changes to staged files before committing. (Basically it just saves you some typing).

Aliases for Reviewing

hist

# Command
$ git hist

# Alias
hist = log --pretty=format:\"%h %ad | %s%d [%an]\" --graph --date=short
  • What it does: Shows a list of past commits, formatted in a human-friendly manner.
  • When to use it: When merging pull requests or cherry-picking commits, or when you want to easily see what commits are present in a branch.

yesterday

# Command
$ git yesterday

# Alias
yesterday = "!f() { git --no-pager hist --since=yesterday.midnight --until=today.midnight; }; f"
  • What it does: Shows changes from previous day.
  • When to use it: Daily meetings, billing, and reporting.

today

# Command
$ git today

# Alias
today = "!f() { git --no-pager hist --since=today.midnight; }; f"
# Note: uses hist alias
  • What it does: Shows list of commands made today (since 12:00 am).
  • When to use it: Reporting, wrapping up work for the day, or preparing for the weekend.

latest

# Command
$ git latest

# Alias
latest = diff HEAD~1 HEAD
  • What it does: Shows you the git diff of the last commit that was made.
  • When to use it: When you want to review the last changes you committed, especially when returning to work first thing Monday morning.

distance

# Command
$ git distance origin
$ git distance upstream master

# Alias
distance = "!f() { git --no-pager log --oneline --graph --first-parent --left-right --no-decorate HEAD...$1/${2:-$(git rev-parse --abbrev-ref HEAD)}; }; f"
  • What it does: Shows you how far your current branch is from the corresponding (or specified) branch on the given remote.
  • When to use it: When you want to see which commits you haven’t pushed up or pulled down yet.

compare

# Command
$ git compare branch1 branch2

# Alias
compare = "!f() { git log --oneline --graph --first-parent --left-right --decorate $1...$2; }; f"
  • What it does: Shows which commits differ between the provided branches.
  • When to use it: When you want to see how two branches differ, based on commits (for example, to check if all the changes in one branch are already in the other, or which ones are not).

TL;DR

  • Cherry-pick only the commits you want
  • Pull and test locally
  • Make frequent, focused commits
  • Use branches intelligently and consistently
  • Write informative commit messages
  • Fix mistakes before merging your code with others’
  • Use aliases to speed up and simplify development
# Git Aliases: copy and paste the following code into your .gitconfig file under the [alias] heading.

[alias]

  # amend
  amend = commit --amend -C HEAD

  # addmend # Note: uses "amend" alias
  addmend = "!f() { git add --all && git amend; }; f"

  # diffs
  diffs = diff --staged

  # hist
  hist = log --pretty=format:\"%h %ad | %s%d [%an]\" --graph --date=short

  # yesterday # Note: uses "hist" alias to format output
  yesterday = hist --since=yesterday.midnight --until=today.midnight

  # today # Note: uses "hist" alias to format output
  today = hist --since=today.midnight

  # latest
  latest = diff HEAD~1 HEAD

  # distance
  distance = "!f() { git --no-pager log --oneline --graph --first-parent --left-right --no-decorate HEAD...$1/${2:-$(git rev-parse --abbrev-ref HEAD)}; }; f"

  # compare
  compare = "!f() { git log --oneline --graph --first-parent --left-right --decorate $1...$2; }; f"

  # merge
  merge = merge --no-ff

You don’t necessarily need to use all of these to use Git effectively. More important than any one tip or trick is good communication between team members. If everyone is on the same page as far as when to branch, when to commit, and how to make pull requests, you’ll (hopefully) be able to go home on time.

Git can’t fix every problem, but the strategies mentioned here can at least make sure that it doesn’t cause new ones. If you have questions about Git or any of the topics mentioned earlier, please leave a comment below.

In a source control rut? Feel free to contact us.

Related Posts

August 18, 2024 • Frank Valcarcel

What makes Enterprise Software Development Different?

Enterprise software powers large organizations, handling complex tasks across departments. From robust security to scalability, these solutions face unique challenges. Explore what makes software “enterprise-ready” and how to choose the right development approach for your business.

Image for 'This Week I Learned' blog post featuring tips on managing Git branches including renaming and syncing with remote changes.
October 8, 2019 • Frank Valcarcel

TWIL 2019-10-04

Join us for ‘TWIL,’ where Emily shares innovative Git tips for managing branches with rewritten history. Learn to sync local branches with remote changes and rename them effectively.