Why compare branches?
If you have a solid branch process, you know the importance of comparing them before a merge. Whether you’re working on a new feature or bug fix, having the ability to see how branches differ can provide a ton of insight. Beanstalk allows you to do this with ease by using the Compare View feature. But if you’re using the terminal to get things done, Git offers a few handy commands for comparing branches.
This guide will help you understand how comparing branches can help your development process. First, we’ll discuss how to compare branches on the command line. Then we’ll talk about Git merge tools and branch workflows later.
Comparing branches
Using git status
does well at informing you of what has changed at a high level, but there are other commands that can help you identify changes in detail. Before we get into the specific commands, let’s discuss a common scenario where comparing branches could be useful.
Let’s say you’ve been working on a feature for a few weeks. You’re almost ready to merge your code, but you want to compare the files on your feature
branch with the master
to see if anything has changed. You can use the git diff
command to compare branches.
git diff
The git diff
command allows you to view the differences of two versions. For this example, let’s compare the local master
branch with the remote master
branch. To do this, run the git fetch
command so that you’ll have the latest changes from your remote branch, and then run:
git diff (local-branch) (remote-branch)
If you’d like to get more specific, you can compare two different revisions. Run:
git diff 0023cdd..fcd6199
Reading the output
Reading the output of the git diff
can intimidating at first, but let’s break this down in detail. Let’s say we want to compare two branches where the about.html file were changed. After running the git diff
command, here’s the output:
diff --git a/about.html b/about.html
index db35ae1..6a0d181 100755
--- a/about.html
+++ b/about.html
@@ -8,8 +8,8 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta content="width=device-width, initial-scale=1, maximum-scale=1,
user-scalable=no" name="viewport">
<meta content="black" name="apple-mobile-web-app-status-bar-style">
-
- <title>Selfy </title>
+
+ <title Selfyness </title>
<meta name="description" content="">
<link rel="stylesheet" type="text/css" href="css/normalize.min.css">
At the top, Git tells you the two files that have changed. In this case, here are the files we’re comparing: a/about.html b/about.html. These are the compared files. There is an A version, and a B version. There are instances where version A and B are two different files, but in most cases they are the same. To be sure, Git will always define what file represents item A or B. The symbols to the left of the about.html are file markers. These markers help you to identify which file the changes belong to. In this example -
represents the A version of the about.html. Each time there is a -
mark next to a set of changes, then that means those modifications belong to the A version of the about.html file. The +
represents the b/about.html file.
Chunks and changes
Chunks are a set of related changes. When you see the @@
symbol, that means you’re at the beginning of a new chunk or set of modifications. In this example, the a/about.html version is showing the title to be:
<title> selfy </title>
But the B version (b/about.html) shows that the title is <title> Selfyness </title>. Remember, we know that this change relates to the B version because of the + symbol.
Note: The diff command shows modified changes, not the entire file itself. This allows you focus on comparing changes, without having to sift through several hundred lines of code to locate them.
There are scenarios where your output could look different. What if your output looks like this:
diff --git a/about.html b/about.html
index db35ae1..6a0d181 100755
--- a/about.html
+++ b/about.html
@@ -16,5 +21,6 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<meta content="black" name="apple-mobile-web-app-status-bar-style">
+ <p>I graduated from college in 2001</p>
+ <p>After college, I moved to France.</p>
<meta name="description" content="">
<link rel="stylesheet" type="text/css" href="css/normalize.min.css">
It looks like the changes were only made to one version of the file. But there is more to what we’re seeing here. The +
symbol indicates that there were lines of code added to version B. Since the -
symbol is missing, this indicates that the code does not exist, at least on version A. This means that the code represented by the +
was added to the file and does not exist on version A.
If the scenario was reversed, and a line has been deleted, the output may look like this:
diff --git a/about.html b/about.html
index db35ae1..6a0d181 100755
--- a/about.html
+++ b/about.html
@@ -16,5 +21,6 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<meta content="black" name="apple-mobile-web-app-status-bar-style">
- <p>I graduated from college in 2001</p>
- <p>After college, I moved to France.</p>
<meta name="description" content="">
<link rel="stylesheet" type="text/css" href="css/normalize.min.css">
The changes in a/about.html were not found in the b/about.html because the lines were deleted.
Comparing with Beanstalk
You can also compare branches within Beanstalk itself. The compare-view feature allows you to compare branches, and specific revisions in just a few clicks.
Benefits
- View the latest changes without using the command line, or a diff tool.
- Search for specific commits within a branch, and compare them to another.
- Filter commits based on the user to see who made what changes.
- Assign code reviews while comparing branches to review and collaborate with your team.
In the brief video below, we demonstrate how using the compare-view feature can save you time, and minimize your toolset.
Using a diff tool
If you’re looking for an easier way to read Git diff output, then you have a few options. You can use a Git diff merge tool to help you see changes with ease. A diff tool is an application that allows you to view comparisons in a clear, and understandable format. There are several different tools available, but they all share the same purpose. Here are a list of a few diff tools that can help you inspect differences and change code if needed.
Mac
- Kaleidoscope
- Araxis Merge
- Apple FileMerge (comes pre-installed with Xcode)
Windows
Now that you know how to compare the differences in branches. Let’s talk about how the types of branches that exist and how to use them in your workflow.
Branches
In the ideal world, branches should only last a few days. But if you’re working with a large team, there are scenarios where you may need a longer running branch. In this section, we’ll discuss the difference between short and long lived branches.
Short lived
Short lived branches focus on specific topics like bug fixes, features, or experiments. These branches are normally safe to delete after merging into the master
branch. Once a short lived branch is complete it can be integrated into the develop
or master
branch depending on your workflow.
Short lived branches are also known as topic branches, and can be useful in any project. It’s important to know that all branches are created equal. There are no specific command you can use to create a short or long lived branch. But you can decide the purpose of a branch. If you want to use a branch to fix a bug and merge your changes, then you have a short lived branch. Here's the main takeaway when working with short lived branches.
- Short lived branches can usually be deleted at some point after completion. Remember, branches are pointers to a single commit. Deleting the branch does not undo all your hard work as long as it was merged to stable.
Long lived
Long lived branches are on the opposite end of the totem pole. These branches are stable and often have a permanent place in your repository. One example of a long lived branch is the master
branch. The master
branch is for production ready code.
Depending on your workflow, the develop
branch can be used in a similar way. It can be used for testing features or bug fixes before they go into production. For example, once you complete a feature you can merge it into your develop
branch. Once the code is reviewed and tested, then it's merged into the master. Are you noticing the pattern here?
Long lived branches are rarely (if ever) deleted because they represent different points in your project’s lifecycle. Here are a few general rules to keep in mind when working with them, but none of these points are set in stone.
- Short lived branches are often integrated into long lived branches. It’s rare that you’ll need to work on a long lived branch directly.
- Set clear rules on how to create and use long lived branches. If there is confusion over where feature branches should be merged, problems will exist.
Workflows
Now that we know the beauty of short and long lived branches, how are they used? There are so many workflows that exist, how do you choose what’s best for your team? Since each team is unique, this will not be a one size fits all scenario. But there is a basic workflow that could be helpful to a team starting out.
Things to keep in mind
- Always create a branch if you’re working on a new feature, bugfix, or experiment. This is true even if the bugfix or feature you’re adding seems insignificant or hardly noticeable. Try your best to avoid committing directly on the
master
branch. - When creating a topic branch, always base from the
master
branch. - After completing a topic branch, merge your changes into your
develop
branch for testing. Remember, thedevelop
branch is often a long lived branch. This branch is for testing features and bug fixes before pushing tomaster
. - Review and test all code before merging into the
master
. We recommend using some form of quality assurance. For example, code reviews are helpful for double checking work, and mentoring inexperienced developers. - The
master
branch should always be stable.
Sometimes when working on a feature, the master
branch gets updated before you’ve completed your task. This can cause confusion, and even worst, merge conflicts. We’ve written articles on how to deal with merge conflicts. In this guide though, we’ll focus on one way to conflicts as a whole.
Keep changes in sync
Let’s go back to our example above. You’re working on a feature
branch and your colleague just pushed his new bugfix to production. Now you’re out of sync with the master
, and you’ve already started working on your new feature. For this reason, we recommend merging new changes from the master
into develop
branch as often as possible. This keeps changes stable, and avoids conflicts when your code is ready to go into production.
Another way to prevent conflicts and confusion is to be consistent when naming branches. For example, let’s say you’re trying to fix a bug on your client’s website. You create a new branch and name it “bug fix”. While this name describes the purpose of the branch, it doesn’t do much else. Does the bug fix have a reference number? If so, include it when naming the branch. If you’ve named a branch “bug-fix-223” on your local environment, be consistent by giving it the same name in your remote repository.
Popular workflows
As we’ve discussed, there are a lot of options when it comes to branching workflows. One popular workflow is Git flow. Git flow is often used by larger teams that have more specific needs. It’s a strict branching model that is useful for managing larger projects. You can find several different workflows online, but here are a few others that are well known.
- Homebrew Workflow (we use this here at Wildbit)
- Forking Workflow
- Centralized Workflow
Rebasing
What is rebasing?
In simple terms, rebasing is the process of moving one branch from one place to another. Rebasing is another way to integrate branches. The git merge
command is the easiest way to integrate branches, but there are scenarios where some prefer the rebase method. Let’s consider how rebasing works and why some teams use this approach.
Why some teams rebase
If you’ve ever merged one branch into another, you’ve seen a merge commit. A merge commit is a commit that’s made by Git (not a developer) to tie two branches together. For example, let’s say you’re working on a feature
branch and you’re ready to merge it into your develop
branch.
When you merge, Git automatically creates a new commit that joins both branches together.
This single, merge commit includes all the code in your feature
branch and adds it to the develop
branch. In a sense, it’s a knot that joins two branches together.
This process is completely acceptable to some developers. But there are instances where you may prefer to go without an automatic merge commit. Why? Not having merge commits can keep your repository history clean.
Some teams prefer to have their history look as if it was not composed of multiple branches at some point. If you want to avoid merge commits you can fix this by rebasing. But there are some risks involved when taking this approach.
What we recommend
Rebasing does have drawbacks. One common misconception about rebasing is that you’re simply moving commits around without changing the history. This is not correct as rebasing will rewrite your history. In essence, when you rebase you change all the commits made since splitting from the last branch. Why? In Git, commits are immutable and can’t be moved.
Each commit has its own unique parent and properties. If you could (hypothetically) move a commit, you’d be changing it’s parent commit. This simply is not possible with Git, hence, why when you rebase you’re making new commits. This can easily become a problem if the commits that have been rebased are published. Another developer may have already produced work referencing the old commits, before the rebase occurred.
In this light, we do not recommend rebasing. As stated earlier, rebasing will rewrite your history and is often too much risk for very little gain. It can affect the workflow of your entire team. If there is one gap of miscommunication, and a developer rebases commits that have been published, you could find yourself in a lot of trouble.
Conclusion
There are many different workflows, and tools available when working with Git. Understanding how to compare branches, and use merge tools will solidify your process and help you write better code. Knowing the difference between merging, rebasing, and it’s drawbacks will keep you from making trivial mistakes, and save you loads of time.