Git Checkout: How to Work with Branches
The checkout command in the Git version control system is responsible for switching between different branches in a repository. Each switch updates the files in the working directory based on the data stored in the selected branch. Every subsequent commit is automatically added to the active branch chosen earlier using the checkout command.
This guide will cover various ways to use the git checkout command and other related commands (such as git branch, git reflog, and git remote show), which enable interaction with both local and remote branches.
Creating a Repository
First, let's prepare a directory for a test Git project:
mkdir project
Then, navigate to it:
cd project
Finally, initialize the Git repository:
git init
Creating a File and Committing
To understand how branch switching affects the working directory (and the repository as a whole), we’ll create a basic project source file with trivial content inside:
sudo nano file_m
The content of the file will be:
file in master
Let’s check the status of the working directory:
ls
There is only one file:
file_m
Now let’s stage the changes:
git add file_m
Then, commit them:
git commit -m "First commit"
Throughout this guide, we’ll observe how working with branches impacts the contents of the working directory — particularly the files we create or edit.
Creating a New Branch
Let’s assume we want to introduce a new feature into our project but are unsure of its necessity or effectiveness. Essentially, we want to test a hypothesis with the ability to revert changes to the stable version of the project.
To do this, Git allows us to create separate branches and switch between them. This way, we can test the project both with and without the feature. But first, let’s check which branch we are currently on:
git branch
The console will display the output with the active branch, master, highlighted:
* master
We committed the previous changes to this branch, which means the file_m file is in this branch. Now, we’ll create a separate branch for our new feature using the same git branch command but with a new branch name:
git branch feature1
It’s important to note that git branch does not automatically switch to the newly created branch. We can confirm this by rechecking the list of existing branches:
git branch
You’ll notice that the list now includes the feature1 branch, but the active branch (marked by green and asterisk) is still master:
feature1* master
Now we have multiple branches to switch between.
Switching to an Existing Branch
To manually switch to an existing branch, use the checkout command, specifying the branch name:
git checkout feature1
The console will display a message confirming the successful switch:
Switched to branch 'feature1'
Let’s check the list of existing branches again:
git branch
As you can see, the active branch is now feature1:
* feature1 master
Let’s check the working directory again:
ls
It still contains the same file that was “inherited” from the master branch:
file_m
Since the feature1 branch is for modifying the project, we’ll create another file:
sudo nano file_f1
Its content will be:
file in feature1
Let’s stage the changes:
git add file_f1
And commit them:
git commit -m "Commit from feature1"
Now, check the working directory again:
ls
You’ll see there are now multiple files:
file_m file_f1
Now, let’s switch back to the main branch:
git checkout master
After this, the working directory will only contain the original file:
file_m
Each time we switch between branches, the files in the working directory update to reflect the state of the commits that exist in the active branch.
Switching to a New Branch
Let’s assume we want to add another feature to our project, meaning we’ll need to create a new branch. First, ensure that we’re on the master branch:
git checkout master
Now, attempt to switch to a branch that hasn’t been created yet, feature2:
git checkout feature2
As expected, you’ll receive an error:
error: pathspec 'feature2' did not match any file(s) known to git
However, the git checkout command allows you to create new branches while switching to them by using the -b flag:
git checkout -b feature2
The console will display a message confirming the successful switch:
Switched to a new branch 'feature2'
In essence, git checkout with the -b flag is equivalent to running the following two commands:
git branch feature2git checkout feature2
Recheck the list of existing branches:
git branch
Now we have the feature2 branch, which became active immediately upon its creation:
feature1* feature2 master
The new branch is based on the branch (its working directory and commit history) that was active before it was created. Since we switched to the master branch before creating feature2, the working directory should only contain the file file_m but not file_f1.
Deleting a Branch
You cannot delete a branch that is currently active:
git branch -d feature2
The -d flag indicates the request to delete the specified branch. The console will display an error message:
error: Cannot delete branch 'feature2' checked out at '/root/project'
So, first, switch to another branch:
git checkout master
Then proceed with the branch deletion:
git branch -d feature2
This time, the console will display a message confirming the successful deletion of the branch:
Deleted branch feature2 (was 24c65ff).
The list of existing branches will now look like this:
feature1* master
Creating a Branch from Another Branch
Git allows you to specify which branch to base a new branch on without switching branches first.
Let’s first ensure we're currently on the master branch:
git checkout master
At this point, the special HEAD pointer points to the active master branch, which, in turn, points to the latest commit of this branch.
Previously, we created the feature2 branch from the active master branch. However, now we’ll create the feature2 branch from the feature1 branch (instead of master) without explicitly switching to it — we'll stay on master:
git checkout -b feature2 feature1
Now the active branch is feature2. Let’s check the contents of the working directory:
ls
As you can see, the state of the directory matches feature1, not master:
file_m file_f1
We can also look at the commit history:
git log
The feature2 branch contains both the commits from master and from feature1:
commit fb1b1616c85c258f647df4137df535df5ac17d6c (HEAD -> feature2, feature1)Author: root <[email protected]>Date: Tue Feb 13 02:18:02 2024 +0100 Commit from feature1commit 24c65ffab574a5e478061034137298ca2ce33c94 (master)Author: root <[email protected]>Date: Mon Feb 12 11:30:56 2024 +0100 First commit
Resetting a Branch to Another Branch
In addition to creating a branch from another, the checkout command can reset an existing branch to match the state of another branch.
For example, we can reset the feature2 branch to match the state of master:
git checkout -B feature2 master
Note the use of the -B flag instead of -b.
The console will show the following message:
Reset branch 'feature2'
Check the working directory:
ls
Only one file remains:
file_m
The list of "inherited" commits in the feature2 branch will now match the commits of the master branch:
git log
In the console, there will only be one commit — the very first one:
commit 24c65ffab574a5e478061034137298ca2ce33c94 (HEAD -> feature2, master)Author: root <[email protected]>Date: Mon Feb 12 11:30:56 2024 +0100 First commit
Viewing Checkout History
Switching branches is not just a read operation; it makes changes to the repository, creating a new record in the checkout history.
Git has a special command to display the full history of branch switches:
git reflog
The history of operations is displayed from bottom to top, with the most recent switches at the top:
fb1b161 (HEAD -> feature2, feature1) HEAD@{1}: checkout: moving from master to feature224c65ff (master) HEAD@{2}: checkout: moving from feature1 to masterfb1b161 (HEAD -> feature2, feature1) HEAD@{3}: commit: Added the first feature24c65ff (master) HEAD@{4}: checkout: moving from master to feature124c65ff (master) HEAD@{5}: checkout: moving from feature2 to master24c65ff (master) HEAD@{6}: checkout: moving from feature1 to feature224c65ff (master) HEAD@{7}: checkout: moving from master to feature124c65ff (master) HEAD@{8}: commit (initial): First commit
Switching to a Remote Branch
Adding a Remote Repository
Suppose we have a remote GitHub repository we are working with over HTTPS:
git remote add repository_remote https://github.com/USER/REPOSITORY.git
Alternatively, we could access it via SSH:
git remote add repository_remote [email protected]:USER/REPOSITORY.git
In this case, an SSH key needs to be generated beforehand:
ssh-keygen -t rsa -b 4096 -C "GITHUB_ACCOUNT_EMAIL"
The public key (.pub), located in the /.ssh/known_hosts/ directory, is copied into the GitHub account settings under SSH Keys.
In our case, the remote repository will be Nginx:
git remote add repository_remote https://github.com/nginx/nginx
Fetching Files from a Remote Branch
After adding the remote repository, we can check the list of all its branches:
git remote show repository_remote
Before switching to a remote branch, we first need to retrieve detailed information about the remote repository — branches and tags:
git fetch repository_remote
You can also fetch from all remote repositories at once:
git fetch --all
Now, we can switch directly to a remote branch and retrieve its files into the working directory:
git checkout branches/stable-0.5
In older Git versions, it was necessary to specify the remote repository explicitly:
git checkout repository_remote/branches/stable-0.5
Now, if you run the command:
git branch
You will see the remote branch listed as active:
* branches/stable-0.5 feature2 feature1 master
Check the state of the working directory:
ls
Now it contains the following directories:
auto conf contrib docs misc src
You can delete a remote branch just like a local one. First, switch to a different branch:
git checkout master
Then, delete the remote branch:
git branch -D branches/stable-0.5
Now the branch list looks like this:
feature2 feature1* master
Switching to a Specific Commit
Just like switching branches, you can switch to a specific commit. However, it's important to understand the difference between commits and branches.
Branches diverge from the project's timeline without disrupting the sequence of changes, while commits are more like progress points, containing specific states of the project at particular times.
Let’s first switch to the latest branch, which contains the most commits:
git checkout feature2
To switch to a specific commit, provide the commit hash (ID) instead of the branch name:
git checkout fb1b1616c85c258f647df4137df535df5ac17d6c
To find the hash, use the command:
git log
In our case, the commit history looks like this (only the hashes may differ):
commit fb1b1616c85c258f647df4137df535df5ac17d6c (HEAD -> feature2, feature1)Author: root <[email protected]>Date: Tue Feb 13 02:18:02 2024 +0100 Commit from feature1commit 24c65ffab574a5e478061034137298ca2ce33c94 (master)Author: root <[email protected]>Date: Mon Feb 12 11:30:56 2024 +0100 First commit
After switching to a commit, you can check which branch is currently active:
git branch
The list of branches will now look like this:
* (HEAD detached at fb1b1616c) feature2 feature1 master
This results in a "detached HEAD" state. Any subsequent commits won’t belong to any existing branch.
However, this mode is risky — the lack of a specific branch in the HEAD pointer may result in data loss. For this reason, it's common to "wrap" the chosen commit in a new branch to continue project modifications.
Switching to a specific commit is usually used to review changes made at a particular stage of development.
Difference Between checkout and switch
In later Git versions (2.23 and above), there’s another command for working with branches — switch.
These commands are quite similar, but switch is more specialized:
git switch is a new command focused more on branch operations. At the same time, git checkout is an older command that can also handle "peripheral" tasks, such as creating new branches while switching or modifying the working directory to match a specific commit's state.
git checkout has a more universal (and less standardized) syntax, which can make it seem more complex and prone to errors compared to git switch.
Conclusion
In this guide, we’ve covered the git checkout command, primarily used for switching between different branches in a repository.
Here’s a complete list of what the checkout command can do:
Switch between existing local branches.
Create new local branches,
Create new local branches based on other branches.
Reset existing local branches to the state of other branches.
Switch between existing remote branches (and download their files into the working directory).
Switch to a specific commit from a local or remote branch.
After switching to another branch, the use of commands like git add and git commit typically follows to index changes and update the repository state within that branch.
Always be cautious — switching branches after making changes in the working directory without committing can result in data loss.
For more information on working with Git, refer to the official documentation.
26 September 2024 · 11 min to read