Git Branches allow us to develop features independently of "production" code to avoid affecting what our teammates and users see before we are ready.
How to create a new branch
How to move between branches
How to merge 1 branch to another and resolve merge conflicts
A Git Branch is an independent series of commits. Every Git repo starts with a single branch, typically main
, which we can imagine to be a linear series of commits.
Using multiple branches allows software engineers to develop new features based on production code in main
without affecting main
, even after pushing to GitHub. We typically refer to non-main
branches as "feature branches". Feature branches can be for changes as small as a typo and as large as new products.
Feature branches are independent series of commits that typically "branch" from main
, and merge back into main
after we have completed and tested the new feature.
We can delete feature branches after merging them to main
.
At large tech companies, 1000s of engineers can be working on independent feature branches that branch from main
and merge back to main
at different points in time.
Create new branches with git checkout -b
. git checkout
is the command to switch, or "checkout" branches, and the -b
flag creates a new branch and checks it out. Branch names use kebab-case (lowercase with hyphens between words) by default, and we will use my-feature
as our example branch name.
Verify we are on the new my-feature
branch with git branch
.
Now we can make commits on my-feature
that build on the state of main
when we created my-feature
. Changes on my-feature
will not affect any other branch in our repo.
While working on feature branches we may wish to periodically checkout other branches such as main
to verify our changes are still compatible with theirs. To change back to the main
branch, run git checkout main
. We may also wish to git pull
when on main
to pull any new changes from GitHub to main
. Run git checkout my-feature
to go back to the my-feature
branch.
main
Once done with our feature on our feature branch, we can merge our changes to main
for teammates and users to use. When working independently we can perform the merge locally, but when working in a team we typically perform the merge via GitHub to give teammates a chance to review our changes through a "pull request".
Checkout the branch we want to merge into. For example, if we want to merge changes on our feature branch to main
, checkout main
.
Run git merge
followed by the source branch name, e.g. git merge my-feature
.
Git will combine commits from both branches and create a "merge commit" to resolve any differences. Run git log
to view the merge commit and verify merge success.
Delete the feature branch locally with git branch -d
followed by the feature branch name, e.g. git branch -d my-feature
.
Once ready, push latest changes in main
to GitHub.
Checkout and verify we are on our feature branch with git branch
Run git push
to push latest commits from our feature branch to GitHub. If this is our first time pushing this feature branch to GitHub, we may have to run git push --set-upstream origin
followed by our feature branch name, e.g. git push --set-upstream origin my-feature
.
See instructions in 0.3.1: Pull Requests for how to create and merge a pull request in GitHub.
We may see the following output when pushing a feature branch to GitHub for the first time with git push
. To resolve, enter the command Git suggests: git push --set upstream origin my-feature
, where my-feature
is the name of our feature branch.
upstream
refers to where our code should be hosted. origin
refers to our GitHub repo or where we cloned our repo from. my-feature
tells Git to create a new branch called my-feature
in GitHub and by default push changes from the local my-feature
branch to the GitHub my-feature
branch.
After setting upstream once for a branch, we can run git push
without arguments for subsequent pushes from this branch.
main
to feature branchIn addition to merging feature branches to main
, another common workflow is to merge latest changes from main
into our feature branch. This minimises chances of merge conflicts when we merge our feature branch to main
, especially if there have been big changes to main
since we started our feature.
Checkout main
with git checkout main
and pull latest changes from GitHub with git pull
.
Checkout our feature branch, e.g. git checkout my-feature
.
Run git merge main
to merge main
into our feature branch. If we're lucky Git will merge the changes automatically. If not we will need to resolve merge conflicts manually.
Merge conflicts are situations when Git cannot automatically merge changes from 2 branches, for example if 2 branches change the same line of code differently. We can minimise merge conflicts by actively communicating with teammates to work on different files or functions, but generally merge conflicts are a standard feature of software engineering.
VS Code highlights differences in files with conflicts. The lines surrounded by <<<<<<< HEAD
and =======
are changes from the branch we are on, and the lines surrounded by =======
and >>>>>>> main
are changes from the incoming branch.
Once we have a merge conflict we must resolve it before writing new code, such that each commit in our commit history continues to describe a specific change. If we are unable to resolve conflicts now or merged by accident, we can abort merge with git merge --abort
, which will revert our repo state to just before we ran git merge
.
After Git tells us we have merge conflicts, use git status
to confirm which files have conflicts.
Open each file with conflicts and resolve conflicts in each file by removing lines starting with <<<<<<<
, =======
and >>>>>>>
and updating the code to what it should be. We can use VS Code's Accept Current/Incoming/Both Changes buttons and/or manually edit the files.
Once we have resolved all conflicts, verify our app still works as expected. Once satisfied with our changes, git add
the resolved files to add them to staging area for commit.
Commit changes to finalise Git's merge commit and complete merge.
After committing, git status
should no longer mention conflicts.
We can verify merge success by checking commit history in Git Logs with git log
.
main
without merge conflictsCreate a new repo.
Create a poem about water in water-poem.txt
. Commit this file to the repo.
Create and checkout a new branch to edit the water poem.
Edit the water poem and commit it to the new branch you just created.
List all branches.
Checkout main
.
Verify water-poem.txt
has reverted to the version on main
.
Create a new poem about sandwiches in a new file and commit it.
Checkout the water poem branch.
Verify the sandwich poem does not exist in the water poem branch.
Checkout main
and merge the water poem edits from the water poem branch.
Verify water-poem.txt
contains changes from the water poem branch.
Delete the water poem branch with git branch -d water-poem-edits
.
Start from the same repo as the previous exercise.
Make a new branch for edits to the sandwich poem.
While on the sandwich branch, add a line to the poem and change the line that's currently there.
Commit the changes on the sandwich branch.
Checkout main
. To create a merge conflict we will commit new changes to the same lines on the main
branch.
Make a change to the sandwich poem and commit it.
Merge the sandwich branch into main
.
We should observe a merge conflict.
Open the sandwich poem file to see merge conflict symbols from Git.
Resolve the merge conflict as per instructions above.