Sign In
Sign In

GitFlow, GitHub Flow, Trunk-Based Development: Choosing the Branching Model

GitFlow, GitHub Flow, Trunk-Based Development: Choosing the Branching Model
Hostman Team
Technical writer
Git
28.08.2024
Reading time: 9 min

Decision-making in development is rarely as significant as choosing a branching strategy. A well-known fact is that since the creation of Git in 2005 by Linus Torvalds for managing Linux kernel development, this tool has evolved from a simple version control mechanism to the foundation of modern DevOps practices. It is Git branching that has allowed developers worldwide to organize parallel work on large-scale projects, providing exceptional flexibility and control over every line of code.

The choice of branching strategy affects release timelines and determines how the team will respond to production environment errors, implement new features, and manage multiple product versions simultaneously. Nowadays, the discussion of GitFlow, GitHub Flow, and Trunk-Based Development is not just a technical consideration but a strategic choice that can determine the future of a project.

GitFlow

GitFlow is a branching model proposed by Vincent Driessen in 2010. The model is oriented toward projects with release cycles and supports multiple repository versions.

Work with this model begins by creating a develop branch from the master branch.

git init
git checkout -b develop

The develop branch will contain the code relevant to the current release. Direct commits to main or develop are not allowed; all updates are made through merging. For each feature or task, the developer will create a new branch with the prefix feature from develop.

git checkout develop
git checkout -b feature/email_verification

After the functionality in the feature/email_verification branch has been developed and tested, it is merged into develop.

git checkout develop
git merge --no-ff feature/email_verification

The --no-ff flag allows you to preserve the commit history, including branch history, even if the develop branch's history has not changed.

Currently, the branch history looks like this: the development on the feature/email_verification and feature/sms_verification branches has been completed, and the branches have been merged into develop. The feature/guest_no_verification branch is still in progress and will not be included in the current release.

Image5

According to the GitFlow model, you should send features from the current release to a release branch — these are branches with the release prefix, created from the develop branch.

git checkout develop
git checkout -b release/1.0.0

Release branches are necessary to finalize the release features. Adding new features to this branch is prohibited, but you can fix bugs, document code, or modify configurations. Once the release/1.0.0 branch is ready and tested, it can be merged into main, which always stores the latest production code.

git checkout master
git merge --no-ff release/1.0.0

While colleagues were preparing the release, the developer finished working on feature/guest_no_verification and then merged it into develop. Currently, the develop branch does not contain new changes from the release branch, nor does master. This is a common situation in the GitFlow model, as feature branches can live quite long and transition from release to release.

Image1

Therefore, the release/1.0.0 branch should also be merged into develop.

git checkout develop
git merge --no-ff release/1.0.0

Now, the develop branch includes the same changes.

Image6

Development continues, but a bug is discovered in production — a simple typo in a form that you'd rather not send through the long release cycle. For quick fixes in GitFlow, hotfix branches are used. These branches, prefixed with hotfix, contain minor fixes, are created from the production master branch, and are merged into both master and develop when ready.

git checkout master
git checkout -b hotfix/release_1.0.0_textform_fix
...
git checkout master
git merge --no-ff hotfix/release_1.0.0_textform_fix
git checkout develop
git merge --no-ff hotfix/release_1.0.0_textform_fix

Image2

Main Branches in GitFlow

  • master/main: Contains tested, stable code that is ready for production deployment.

  • develop: The main branch for development, that contains current but not yet release-ready code.

  • feature: Branches used for developing new features, which are later merged into develop.

  • release: Branches used for preparing new releases, created from develop and later merged into both master and develop.

  • hotfix: Branches for quickly fixing issues in production versions, merged into both master and develop.

Advantages of GitFlow

  • Clear version control structure.

  • Multiple parallel releases support.

  • Easy navigation through project history.

Disadvantages of GitFlow

  • Requires setting up branch rules and CI/CD.

  • Long-lived features. The develop branch often advances ahead, potentially leading to conflicts during merging.

GitHub Flow

GitHub Flow is a simpler and more flexible model designed for projects where the principle of Continuous Integration prevails. The model involves creating a new Git branch for each new task, continuous testing upon merging, and constant/frequent releases without a set release cycle.

All production code is stored in the master branch. For each new feature, the developer creates a separate branch from master. One of the unwritten rules of this model is to name branches so that they describe the main task of the feature. Adding the feature prefix (similar to the GitFlow model) is not necessary.

git init
git checkout -b account_verification_by_email

Upon completion of the work, a PR — pull request (Merge Request in GitLab) is created, a request to merge branches. This is a key feature of the model, where before merging, the system creates a code review request using Git tools, and after that, automated tests can be run, which must pass before the merge into master.

Image7

Image source: docs.github.com

Image3

Image source: theserverside.com

Main Branches in GitHub Flow

  • main/master: The single branch that contains stable code ready for deployment to production.

Advantages of GitHub Flow

  • Simplicity and clarity of the process.

  • Fast development and release cycles.

  • Ideal for CI/CD.

Disadvantages of GitHub Flow

  • Not suitable for projects with complex release cycles.

  • Can be risky for very large projects with many dependencies.

Trunk-Based Development

Trunk-Based Development (TBD) is an approach where developers merge changes directly into the main branch (trunk/main/master), minimizing the number of parallel branches. This ensures high development speed and fewer merge conflicts.

This approach describes not just working with Git but the entire development process — it should be iterative, with very short iterations for each feature. This means that when adding code to the trunk branch, the feature may not yet be complete and might be implemented as interfaces or placeholders.

git init
git checkout -b account_verification_by_email_interface

With each subsequent iteration, the feature is fleshed out and will never break the build. The trunk branch always contains stable, ready-to-release code, so you should conduct tests before merging into the trunk branch. After successful tests, merge.

git checkout master
git merge --no-ff account_verification_by_email_interface

Now you can deploy the code to production. This means that with this approach, you don't need to be tied to release cycles, similar to GitHub Flow, but the difference lies in the fact that in Trunk-Based Development, work is done directly in the main branch, with a minimum of long-term feature branches. This provides constant product updates and the ability to quickly respond to changes without significant delays. When the feature is fully implemented, a tag can be added to the branch before merging, indicating to other developers that the feature is complete:

git checkout account_verification_by_email_complete
git tag complete

The graph of branches and commits might look like this.

Image4

Feature Flags

To manage risks and ensure control over new features that may not be fully ready for use by all users, Trunk-Based Development actively uses feature flags. Feature flags allow developers to enable or disable functionality without requiring new deployments. This way, you can control access to new features, conduct A/B testing, and gradually roll out and collect feedback from users in real-world operating conditions.

Feature flag management can be handled through various platforms:

  • LaunchDarkly: A market leader in feature flag management, offering powerful tools for deployment, testing, and real-time feature analysis.

  • Split.io: A service that, in addition to managing flags, provides extensive capabilities for experimentation and A/B testing.

  • ConfigCat: A simpler and more accessible tool, suitable for small and medium-sized projects, that offers basic feature flag management and deployment capabilities.

Main Branches in TBD

  • trunk/main/master: The central branch of the project where all changes are committed.

Advantages of TBD

  • Simplifies the development process, reducing management overhead.

  • Speeds up the development cycle through frequent commits to the main branch.

  • Easy implementation of continuous CI/CD.

Disadvantages of TBD

  • High demands on developer discipline and the quality of automated tests.

  • Risk of introducing unstable code into the main branch.

  • Additional time may be needed for stabilization before release.

Comparison of Models

The table below compares the branching models based on several basic parameters:

Criterion

GitFlow

GitHub Flow

Trunk-Based Development (TBD)

Team Size

Better for large teams

Works well for teams of any size

Ideal for medium and large teams

Release Cycles

Long, with pre-releases (staging)

Short, releases can be frequent and fast

Very short, continuous releases

Feature Complexity

Supports complex features requiring long development

Better for simple or medium complexity features

Suitable for simple and streamlined features

Feature Dependencies

Manage dependencies through different branches

Features should be relatively independent

Features should be independent or use feature flags for managing dependencies

Team Experience

Suitable for teams of any experience level, but requires good process documentation within the team

Simple management, suitable for teams of any experience level

Requires CI/CD discipline and comfort with continuous integration

For small teams (around five people) and startups, GitHub Flow is often the best choice, as it is simple to implement and supports high development speed with minimal overhead on branch management.

For medium and large teams, GitFlow or Trunk-Based Development may be preferable, depending on how critical managing multiple releases and code stability are. GitFlow is well-suited for companies with clear release cycles and the need to support multiple versions of a product simultaneously.

Trunk-Based Development may be ideal for organizations that prioritize rapid delivery and innovation, as this approach accelerates CI/CD processes.

If none of the well-known options fits, you can always develop something of your own using elements from different approaches. The key is that the development culture within the team defines the way of working with the Git system.

Git
28.08.2024
Reading time: 9 min

Similar

Git

How to Use the Git Rebase Command

In Git, managing code history is important for tracking changes. For this purpose, git supports several commands, such as commit, log, diff, branch, merge, revert, and rebase. The git rebase command, in particular, is useful for keeping branch histories clean by allowing developers to reapply commits from one branch to another. In this article, we’ll discuss what git rebase is, how it differs from the git merge command, and how to use it to maintain a structured, linear commit history that’s easier to read and review. Understanding Git Rebase: What Is It? The git rebase command allows us to move, combine, reorder, edit, or remove commits. Moreover, it simplifies the project history by moving the commits of one branch onto the base of another branch. Rebase in git is especially useful when integrating changes into a feature branch, resulting in a streamlined history without unnecessary merge commits. Git Rebase vs. Git Merge: What’s the Difference? Both merge and rebase commands are used to combine branches, but they differ in how the commit history looks after one branch is added to another. Here’s a comparison to understand when to use rebase versus merge: Git Merge: It combines the histories of both branches and creates a merge commit, marking the point where they joined. This commit retains the complete history of both branches. Git Rebase: It applies changes from one branch to another and rewrites the history as though all work was done linearly. Git Merge maintains separate histories for each branch, while Git Rebase linearizes the history, making it appear as if all work was done in a straight line. When using git merge, the focus is on merging feature branches, whereas git rebase is used to rewrite and clean up the commit history for better organization and readability. Basic Syntax and Options for Git Rebase The git rebase command allows users to transfer commits from the current branch to the base of another branch. The basic syntax of the git rebase command is shown below: git rebase <target-branch> Users can use different options with the git rebase command, which are listed below:  git rebase master: This command adds all the changes from the master branch to your current branch. -I or --interactive: This option opens an editor to reorder, combine, or modify commits interactively. --onto <newbase>: This option enables us to set a new base commit for the rebase. We can use it to move several commits to a different branch or commit. --skip: This option skips a commit if there's a conflict during rebase. It tells Git to ignore that commit and continue with the rebase. --no-verify: This option ignores any pre-commit checks set up in the repository. It’s useful if we want to commit quickly without running those checks. --auto-squash: It automatically applies the fixup or squash flags to commits. This is helpful for cleaning up commit history during an interactive rebase. These git rebase options should be used carefully, as they can change the commit history of the repository. It is recommended to back up your code before running the rebase command in Git. This way, users can restore the original code if anything goes wrong. How to Perform an Interactive Rebase Interactive rebasing enables users to reorder, combine, or edit commit messages. This practice gives users precise control over their history. Go through the following steps to perform an interactive rebase: Step 1: Switch to the feature branch Users can use the git checkout command to navigate to a different branch in a Git repository: git checkout <feature-branch> This command changes the user's current working branch to the specified <feature-branch>. After switching, any subsequent Git operations, including rebase, will be performed in the context of that branch. Step 2: Start interactive rebase Users can run the rebase command with the -i option to perform an interactive rebase: git rebase -i <target-branch> When a user runs this command, it opens the default text editor. The user will see a list of commits from the current branch that are not present in <target-branch>. Each commit comes with actions to choose from, such as: pick: Keep the commit as it is. edit: Stop and allow changes to the commit (like the message or the files). squash: Combine this commit with the one before it. drop: Removes a commit.  After the user makes the desired changes and saves the file, Git will continue the rebase based on the selected choices. Handling Merge Conflicts During Rebase When rebasing, conflicts can occur if the same line of code is modified in both branches. In that case, Git pauses the rebase process, allowing users to resolve conflicts. Follow the steps below to resolve the merge conflicts during the rebase: Step 1: Identify Conflicting Files Run the git status command to see where the problem/conflict lies in a Git repository: git status This command displays a list of files that have conflicts, marked as unmerged. Step 2: Edit the Conflicted Files When there are conflicts during a Git operation, like a merge or rebase, Git marks the conflicting parts in the files with special markers: <<<<<<< HEAD: It shows the user's changes (from the current branch). =======: It separates the user's changes from the other branch's changes. >>>>>>> <branch-name>: It shows the end of the conflicting section and shows the name of the branch with the conflicting changes. To resolve the conflicts, users should open the files in a text editor and decide which changes to keep. They can choose to: Keep their changes. Keep the changes from the other branch. Combine both sets of changes. After making the edits, it's important to remove the conflict markers to clean up the code and make sure it works properly. Step 3: Stage the Resolved Files Once conflicts have been resolved, the next step is to stage the resolved files. This is done using the following command: git add <file-name> Replace <file-name> with the file’s name that was edited. If multiple files are resolved, they can be added simultaneously or individually. Step 4: Continue the Rebase After staging the resolved files, users can continue the rebase process with the command: git rebase --continue How to Abort, Skip, or Continue a Rebase Users can manage the rebase process by executing the git rebase command with the abort, skip, and continue options. Aborting the Rebase Run the git rebase command with the --abort option to cancel the ongoing rebase and return the branch to its original state: git rebase --abort Skipping the Rebase Similarly, if a user runs into unresolved conflicts during a rebase, he can execute the git rebase command with the --skip option to omit the problematic commit: git rebase --skip Continuing the Rebase If we encounter conflicts while rebasing, we need to resolve them first. After fixing the issues, we can run the rebase command with the --continue option to continue the rebasing process: git rebase --continue Common Mistakes Users can encounter several issues during Git rebase, such as merge conflicts, uncommitted changes, aborted rebase attempts, etc. Here are some common mistakes that users may face while rebasing: Merge Conflicts Users can face merging conflicts when changes in the rebased branch overlap with the base branch. These conflicts require manual resolution. Use the git add <filename> command to mark conflicts as resolved. Then, continue with the git rebase --continue command. Uncommitted Changes If you have uncommitted changes in your working directory, Git won't allow a rebase. In that case, commit or stash your changes with git stash before starting the rebase. Rebasing Shared Branches Rebasing the shared branches can create confusion and conflicts. To avoid this issue, users can rebase the branches that they own or are not currently used by anyone else. Complex History A branch with a complicated commit history can make the rebase process error-prone. In such cases, consider using git merge instead or simplify the history before rebasing. Incorrect Rebase Sequence Specifying the wrong base commit can lead to unexpected changes. Therefore, it is recommended to always double-check that you are rebasing onto the correct branch. Apart from this, the git rebase command has several disadvantages, including increased complexity compared to merging, especially with complex commit histories. It can lead to lost commits if the wrong branch is rebased or if conflicts are unresolved. Additionally, rebasing alters the commit history in public repositories, which makes collaboration difficult. Conclusion In Git, the rebase command helps maintain a clean and readable commit history. However, it requires careful usage due to certain challenges. Therefore, before making significant changes to a branch’s commit history, it’s important to carefully consider the risks and benefits of using the git rebase command.
30 October 2024 · 8 min to read
Git

How to Use the Git Reset Command

Today, it's hard to imagine the work of a programmer or IT professional without version control. Among the various SCM tools, Git stands out, having quickly gained popularity and becoming the de facto standard in the world of version control systems. Git allows you to easily track project file changes, manage branches, collaborate, and centrally store code and other files.  One of Git's strengths is its flexible ability to undo or remove changes. One such way to undo changes is with the git reset command, which supports three different modes. In this tutorial, we'll explore how to undo changes using git reset and its modes through practical examples. Prerequisites We'll focus on practical use cases of the git reset command, so it's necessary to have Git installed beforehand. We'll use a Linux-based operating system for this tutorial, specifically Ubuntu 22.04. However, any Linux distribution will work, as Git is available in nearly all modern package managers. In most distributions, Git comes pre-installed, though the version may not always be the latest. For Ubuntu-based systems, you can install Git from the official repository with the following commands: add-apt-repository ppa:git-core/ppa && apt -y install git For other Debian-based distributions (Debian, Linux Mint, Kali Linux, etc.), you can install Git using: apt -y install git For RHEL-based distributions (RedHat, CentOS, Fedora, Oracle Linux), the installation command will vary depending on the package manager: For yum package manager: yum -y install git For dnf package manager: dnf -y install git After installation, verify the Git version: git --version What is git reset? The git reset command is used to undo local changes. Technically speaking, git reset moves the HEAD pointer to a previous commit in the repository. HEAD is a pointer to the current branch and points to the latest commit in that branch. The git reset command operates with three key elements: the working directory, the HEAD pointer, and the index. These elements are often referred to as "trees" in Git, as they are structured using nodes and pointers. We'll go into detail about each of these elements below. It's worth noting that various Git-based web services like GitHub, GitLab, and Bitbucket offer the ability to undo actions through their web interface. However, they typically use a safer alternative, git revert, which preserves the entire project history, unlike git reset which can permanently remove commits. The Working Directory The working directory is where files are stored and tracked by Git. When you run the git reset command, Git knows which directory is being tracked because of a hidden .git folder created when you initialize a repository with git init. Here's how the working directory works in practice: Create a new directory and navigate into it: mkdir new_project && cd new_project Initialize a new Git repository: git init Once you initialize the repository, a hidden .git folder containing Git configuration files is created in the root directory. The HEAD Pointer HEAD points to the current branch and the latest commit in that branch. Every time you switch branches with git checkout, HEAD updates to point to the latest commit in the new branch. Here's a practical example: Create a new file: touch new1.txt Add the file to the repository: git add new1.txt Commit the file: git commit -m "Initial commit" To see where HEAD is pointing, use the git cat-file command: git cat-file -p HEAD Since there's only one commit, HEAD points to it. Now, let's modify the file and add it again. Modify the file: echo "This is a test file" > new1.txt Stage the file: git add new1.txt Commit the changes: git commit -m "Added content to new1.txt" Check the HEAD pointer again: git cat-file -p HEAD As you can see, HEAD now points to the new, latest commit. The Index The index (or "staging area") is where files go after being added with git add. Think of it as a pre-commit area. Files in the index are tracked by Git but not yet part of the actual commit. You can remove or modify files in the index before they are committed. Create a new file: touch new2.txt Add it to the index: git add new2.txt Check the status: git status The file is now in the staging area but not yet committed.   Git Reset Modes The git reset command supports three modes: soft, mixed, and hard. Soft Mode The soft mode undoes the last commit but keeps the changes in the index. This means that you can modify and recommit them. Create a new file: touch new3.txt Add it to the index: git add new3.txt Commit the file: git commit -m "Added new3.txt" If we run git log now, that's what we'll see: To undo the last commit: git reset --soft HEAD~1 The commit is undone, but the file remains in the index. Mixed Mode The mixed mode is the default for git reset. It undoes the commit and resets the index, but leaves the working directory untouched. Create three new files: touch new{1..3}.txt Add and commit them: git add new1.txt new2.txt new3.txtgit commit -m "Added three files" Now undo the commit: git reset HEAD~1 The files remain, but the last commit is removed. Hard Mode The hard mode deletes the commit, resets the index, and removes the files from the working directory. This is the most destructive option. Create and commit a file: touch readme.mdgit add readme.mdgit commit -m "Added readme.md" To remove the commit and the file: git reset --hard HEAD~1 The file and the commit are permanently deleted. Resetting to an Earlier Commit You can also reset to a specific commit using its hash: git reset --hard <commit-hash> This will reset the repository to that specific commit. Conclusion In this tutorial, we explored the git reset command and its modes: soft, mixed, and hard. While git reset is a powerful tool for undoing local changes, it's essential to understand each mode's impact, especially the potential risks of using the hard mode to avoid irreversible data loss.
26 September 2024 · 5 min to read
Git

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

Do you have questions,
comments, or concerns?

Our professionals are available to assist you at any moment,
whether you need help or are just unsure of where to start.
Email us
Hostman's Support