# Learn Git Branching Complete Guide

## Introduction Segment:

A well-paced introduction to most of the Git commands

1. Git Commit Introduction
    
2. Git Branching
    
3. Git Merging
    
4. Introduction to Git Rebase
    

## Getting Up to Speed

The following dose of pure Git awesomeness. Hope you have an appetite.

1. Detaching Your HEAD
    
2. Relative References (^)
    
3. Relative References #2 (~)
    
4. Reversing Changes in Git
    

## Shuffling Work Around

Getting cozy with modifying the source tree using Git

1. Introduction to Cherry-picking
    
2. Introduction to Interactive Rebase
    

## A Variety Pack

A collection of diverse Git techniques, hacks, and suggestions

1. Selectively Grabbing a Single Commit
    
2. Manipulating Commits
    
3. Manipulating Commits #2
    
4. Git Tags
    
5. Git Describe
    

## Advanced Subjects

For the exceptionally daring!

1. Rebasing over 9000 times
    
2. Multiple Parents
    
3. Branch Spaghetti
    

## Introduction Segment:

1. ### Git Commit Introduction
    
    A commit in a git repository captures a snapshot of all the tracked files in your directory. It functions like a significant copy and paste operation, but with even more advantages!
    
    However, Git aims to keep commits as lightweight as possible. Therefore, instead of blindly duplicating the entire directory every time you commit, Git can compress a commit as a set of changes, known as a "delta," representing the transition from one version of the repository to the next.
    
    In addition, Git maintains a chronological history of when commits were made. This is why most commits have preceding ancestor commits, visually depicted with arrows in our visualization. Preserving this history is highly beneficial for all project collaborators.
    
    It may seem overwhelming, but for now, you can perceive commits as snapshots of the project. Commits possess remarkable efficiency, and transitioning between them is incredibly swift!
    
    Let's examine this concept in action. On the bottom side, we have a visual representation of a (small) git repository. Currently, there are two commits
    
    present. The first commit, labeled as C0, represents the initial commit, while the second commit, labeled as C1, possibly contains some significant modifications.
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1685334616298/5cc2e591-9b17-435b-a486-beced719b033.png align="center")
    
    Made a new commit to the repository with `git commit -m "C2"`
    
    Great! That's fantastic progress. We have successfully made changes to the repository and saved them as a commit. The commit we just created, let's call it "C2," now has a parent commit, C1, which indicates the commit it was built upon or derived from. This parent-child relationship helps maintain a clear lineage of commits within the repository's history.
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1685335073924/55a77368-f248-4154-9590-8c8e4b5ebf65.png align="center")
    
2. ### Git Branching
    
    Branches in Git are extremely lightweight. They act as pointers to specific commits and nothing more. That's why Git enthusiasts often emphasize the mantra:
    
    "Branch early, and branch often."
    
    Since creating branches doesn't consume extra storage or memory, it's easier to logically organize your work into smaller, manageable units rather than having large, cumbersome branches.
    
    As we delve into working with branches and commits together, we'll witness how these two features complement each other. But for now, remember that a branch essentially signifies, "I want to include the changes from this commit and all its preceding commits."
    
    Let's see what branches look like in practice.
    
    `$ git branch newImage`
    
    Here we will create a new branch named `newImage`.
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1685336302172/09d6cefa-5ee5-4669-a1e8-7a7a04e82c63.png align="center")
    
    And just like that, branching is as simple as that! The branch named "newImage" now points to commit C1.
    
    Let's attempt to make some progress on this new branch.
    
    `git commit -m "C2"`
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1685336602053/4fb77c90-c3de-40c9-adc7-4d0166f29d47.png align="center")
    
    Uh-oh! The main branch has moved forward, but the `newImage` branch hasn't! This happened because we weren't "on" the new branch, which is why the asterisk (\*) indicated the main branch as the active one.
    
    To switch to the desired branch before committing our changes, we can use the command:
    
    `$ git checkout <branch_name>`
    
    By executing this command, we inform Git that we want to switch to the specified branch (in this case, the "`newImage`" branch) and make it our current working branch. This ensures that our subsequent commits will be associated with the correct branch.
    
    `$ git checkout newImage`
    
    `$ git commit -m "C2"`
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1685337013721/f99dc5c5-db60-415e-befc-bc95285cdaaf.png align="center")
    
    Perfect! By checking out the new branch before committing our changes, we have successfully recorded our modifications on the new branch. Well done!
    
    1. ### Branches and Merging
        
    
    Fantastic! We have now learned how to commit and branch in Git. The next important concept we need to explore is combining the work from different branches. This capability allows us to branch off, develop new features independently, and later merge them back together.
    
    The first method we will discuss for combining work is called "git merge." In Git, merging involves creating a special commit that has two distinct parents. This type of commit signifies that we want to incorporate all the changes from one parent branch and another parent branch, including their respective ancestry.
    
    To better understand this concept visually, let's proceed to the next view where we'll examine a practical example. This will help elucidate the merging process for beginners.
    
    In the current scenario, we have two branches, each containing a unique commit. This implies that neither branch incorporates the complete set of changes made in the repository so far. To address this, we can utilize the merge operation.
    
    Our goal is to merge the "bugfix" branch into the "main" branch. This merging process will combine the changes from the "bugfix" branch into the "main" branch, ensuring that the final result includes all the modifications made in both branches.
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1685337817223/f6ca5f84-5be0-4688-9980-7d47d563cd2c.png align="center")
    
    `$ git merge bugFix`
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1685338038686/7e1b580d-e9d9-4b08-8c30-af42e17fc4dd.png align="center")
    
    Indeed, there are some remarkable observations here! Firstly, the "main" branch now points to a commit that has two parents. If you follow the arrows upwards from the "main" branch along the commit tree, you will traverse through every commit leading back to the root. This indicates that the "main" branch now encompasses all the work in the repository.
    
    Additionally, you may notice that the colors of the commits have changed. As a helpful visual aid for learning, each branch has a unique color assigned to it. Furthermore, each commit takes on a color that represents a blend of all the branches that contain that particular commit.
    
    In the current scenario, we can see that the "main" branch color is blended into all the commits, while the "bugFix" color is not. To rectify this and include the "bugFix" color into the commits, let's proceed with the necessary steps.
    
    Let's merge the changes from the "main" branch into the "bugFix" branch.
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1685338292080/9cfb860f-e864-4f30-98b3-3a98078d0313.png align="center")
    
    `$ git checkout bugFix`
    
    `$ git merge main`
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1685338391646/1b44c321-624f-42a2-8e94-0427e1cdf2fd.png align="center")
    
    Since the "bugFix" branch was already an ancestor of the "main" branch, Git didn't have to perform any actual merging work. It simply moved the "bugFix" branch pointer to the same commit where the "main" branch was pointing.
    
    As a result, all the commits now share the same color, indicating that each branch now incorporates the entirety of the work in the repository. This is definitely a cause for celebration! Well done!
    

### 4\. Introduction to Git Rebase

Another method of combining work between branches is through rebasing. Rebasing involves taking a series of commits, essentially "copying" them, and placing them in a different location.

Although this may initially seem perplexing, the benefit of rebasing is that it can create a clean and linear sequence of commits. By exclusively using rebasing, the commit log and history of the repository can be maintained in a more organized manner.

Now, let's witness the process in action to gain a better understanding.

In the current scenario, we once again have two branches, with the "bugFix" branch currently selected (indicated by the asterisk).

Our objective is to incorporate the work from the "bugFix" branch onto the work from the "main" branch. This way, it would create an illusion that these two features were developed sequentially, even though they were actually developed in parallel.

To achieve this, we can use the `git rebase` command. This command will help us reapply the commits from the "bugFix" branch onto the latest commit of the "main" branch. By doing so, we can create a more linear and cohesive sequence of commits.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1685338955688/128933c6-5c40-4656-92ed-86b9caac9d79.png align="center")

`$ git rebase main`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1685339003882/3cd9bb74-fde2-4d30-a63a-3b29bda219c6.png align="center")

That's great to hear! Now the work from our "bugFix" branch is seamlessly integrated on top of the "main" branch, resulting in a clean and linear sequence of commits.

Please note that the original commit C3 still exists in the repository (although it appears faded in the commit tree), and C3' represents the "copy" of the commit that we rebased onto the "main" branch.

However, you rightly pointed out that the "main" branch hasn't been updated to reflect the latest changes. To address this, let's update the "main" branch accordingly.

Now we are checked out on the main branch. Let's go ahead and rebase onto bugfix...

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1685339151638/4f9c6473-2a46-4f24-8a71-f2d0ef41e53c.png align="center")

`$ git rebase bugFix`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1685339224571/e9ce68f8-89ef-43da-aae6-292e0e4cf9f6.png align="center")

Great! Since the "main" branch was an ancestor of the "bugFix" branch, Git simply moved the reference of the "main" branch forward in the commit history. This action effectively incorporates the commits from the "bugFix" branch onto the updated "main" branch, resulting in a more streamlined commit history.

## Moving around in Git:

Before delving into the more advanced features of Git, it's crucial to grasp the various methods available for navigating through the commit tree that represents your project.

Becoming proficient in moving around the commit tree will greatly enhance your capabilities with other Git commands and operations. It enables you to efficiently explore different branches, commits, and versions of your project, facilitating seamless collaboration and development.

### HEAD

HEAD is a critical concept in Git that represents the currently checked out commit in your repository. It serves as a symbolic name for the commit you are actively working on.

In most cases, HEAD points to a branch name, such as "bugFix." This means that the branch you have checked out is the one referenced by HEAD. When you make a commit, the status of the branch (e.g., "bugFix") is updated, and this change is reflected through HEAD.

As you continue to make changes to the working tree, HEAD will be updated accordingly to reflect the most recent commit. It essentially acts as a pointer to the tip of the current branch, providing a convenient way to keep track of your progress and navigate through different stages of your project's development.

Let's see this in action. Here we will reveal HEAD before and after a commit.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1685340858161/fbd4b9aa-08b7-45f9-af81-1b7a12dc0971.png align="center")

`$ git checkout C1`

`$ git checkout main`

`$ git commit -m "C2"`

`$ git checkout C2`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1685341023946/b8afb457-3e9d-43ac-af8b-c8bcec010e4d.png align="center")

Indeed! In the visualization, we can observe that HEAD was metaphorically hiding underneath the "main" branch. This implies that the currently checked out commit, represented by HEAD, was aligned with the latest commit on the "main" branch.

By revealing HEAD, we can clearly see its position within the commit tree. It serves as a reference point for the commit you're currently working on, allowing you to easily track your progress and navigate through different branches and commits.

Understanding the role of HEAD and its relationship with the active branch is crucial for effectively managing your project and maintaining a clear understanding of your current working state.

1. ### Detaching HEAD
    

Detaching HEAD refers to the act of attaching it directly to a specific commit instead of a branch. Let's examine the state before detaching HEAD:

Before detaching, HEAD is typically pointing to a branch, such as "main," which in turn points to a specific commit, denoted as C1. This configuration allows for easy navigation and management of the project's history through the branch.

However, when we detach HEAD, it will be directly attached to a commit, breaking the link to any branch. This state of detached HEAD has its own implications and usage, which we will explore further.

HEAD -&gt; main -&gt; C1

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1685341213213/0c398235-bcb1-409c-90cc-2be17ec17f64.png align="center")

`$ git checkout C1`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1685341276286/75782613-2962-49c7-9873-7f845702b40c.png align="center")

And now it's

HEAD -&gt; C1

1. ### Relative Refs
    
    When navigating through the commit history in Git, specifying commit hashes can become tedious, especially when working without a visual representation of the commit tree. In real-world scenarios, commit hashes tend to be longer and less memorable.
    
    However, Git offers a convenient solution known as "Relative Refs" to simplify moving around without relying solely on commit hashes. Relative Refs allow you to reference commits using relative positions and relationships instead of their complete hashes.
    
    For example, instead of typing the entire lengthy hash like xea3da64c0efc6293610ded892f82a58e8cbc5d8, you can use a shorter, unique identifier such as "fed2" to identify the commit. Git is intelligent enough to recognize the unique identifier based on the available commits in the repository.
    
    Using Relative Refs reduces the burden of remembering and typing lengthy commit hashes, providing a more intuitive and efficient way to navigate the commit history, even in real-world Git scenarios where the commit tree visualization may not be readily available.
    

Git's relative refs, indeed, offer a more convenient way to specify commits without relying on their complete hash. They enable you to start from a memorable reference point, such as a branch like "bugFix" or the HEAD, and navigate from there.

Two commonly used relative refs provide flexibility in moving through commit history:

1. Moving upwards one commit at a time with `^`: By appending the caret symbol `^` to a reference, you can access the parent commit. For example, `bugFix^` refers to the parent commit of the "bugFix" branch.
    
2. Moving upwards a specific number of times with `~<num>`: Using the tilde symbol `~` followed by a number allows you to move up the commit history by the specified number of times. For instance, `HEAD~3` represents the commit three steps above the current HEAD.
    

By leveraging these relative refs, you can navigate through the commit tree in a more intuitive manner, making it easier to track changes, compare versions, and perform various Git operations.

Let's examine the commit history and access the commit above the "main" branch using the caret (^) operator. This operator allows us to reference the parent commit of a specified commit.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1685342281378/d68f2ce2-489d-430b-9783-86e2951d12f2.png align="center")

To check out the commit above "main," you can use the following command:

`$ git checkout main^`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1685342298193/4e7165ee-68d7-4c1e-91af-fb38387c6b21.png align="center")

Great job! That was much simpler than having to type the entire commit hash. Using the caret (^) operator allows you to effortlessly navigate to the parent commit of a given reference.

You can also reference `HEAD` as a relative ref. Let's use that a couple of times to move upwards in the commit tree.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1685342795245/8ac2ff68-b940-421f-90fd-8a99bc43d12c.png align="center")

`$ git checkout C3`

`$ git checkout HEAD^   $ git checkout HEAD^`

`$ git checkout HEAD^`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1685342917153/c90aaf7a-36bc-4950-ae04-ddd649febf46.png align="center")

Easy! We can travel backwards in time with `HEAD^`

1. ### The "~" operator
    

Say you want to move a lot of levels up in the commit tree. It might be tedious to type ^ several times, so Git also has the tilde (~) operator.

The tilde operator (optionally) takes in a trailing number that specifies the number of parents you would like to ascend. Let's see it in action.

To use the tilde operator, simply append it with a number indicating the desired level of ascent. Let's see an example:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1685343461690/486ac0d4-f0ec-4462-9850-803060a5e951.png align="center")

`$ git checkout HEAD~4`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1685343507173/6c15c4a5-6d59-4a26-a9c1-d2d00dcbc230.png align="center")

Boom! So concise -- relative refs are great.

### Branch forcing

Now that you're familiar with relative refs, let's put them to use in a practical scenario. One common application is to move branches around using the `-f` option.

To move a branch directly to a specific commit, you can use the `-f` option followed by the branch name and the desired relative ref. For example, the command:

`$ git branch -f main HEAD~3`

will forcefully move the `main` branch to three parents behind the current commit pointed to by `HEAD`.

This operation can be useful when you need to adjust branch references, align them with specific commits, or reorganize the commit history. However, please exercise caution when using branch forcing, as it can overwrite and discard existing commits if not used carefully.

Let's see that previous command in action.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1685343665951/ded0d49b-88a1-4c5e-969d-827fc8fe5662.png align="center")

`$ git branch -f main HEAD~3`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1685343696151/d4fb023e-6292-4448-bd61-da2942b7b813.png align="center")

Perfect! With the help of relative references, we now have a succinct method of referencing C1, and by utilizing branch forcing (-f), we have efficiently relocated a branch to that specific position.

1. ### Reversing Changes in Git
    
    There are many ways to reverse changes in Git. And just like committing, reversing changes in Git has both a low-level component (staging individual files or chunks) and a high-level component (how the changes are actually reversed). Our application will focus on the latter.
    
    There are two primary ways to undo changes in Git -- one is using `git reset` and the other is using `git revert`. We will look at each of these in the next dialog
    

**Git Reset**

When it comes to reversing changes in Git, the `git reset` command is a powerful tool. It allows you to move a branch reference backward in time to an earlier commit, effectively undoing the changes made in subsequent commits. It can be thought of as "rewriting history" since it alters the commit timeline as if the specified commit never existed.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1685345750869/a94de7e1-b01f-46b2-bc0f-7b93e8a191e1.png align="center")

Let's take a closer look at how `git reset` works:

`$ git reset HEAD~1`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1685345801630/33b54e98-c40b-4898-82d4-62a578764ce6.png align="center")

Nice! Git moved the main branch reference back to `C1`; now our local repository is in a state as if `C2` had never happened.

Git Revert

When it comes to reverting changes in Git, the `git revert` command provides a safe and collaborative approach. Unlike `git reset`, which modifies the commit history and can cause issues with shared branches, `git revert` creates a new commit that undoes the changes made in a previous commit. This allows you to reverse changes and share those reversed changes with others.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1685345750869/a94de7e1-b01f-46b2-bc0f-7b93e8a191e1.png align="center")

Here's how `git revert` works:

`$ git revert HEAD`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1685345912167/3c011ae8-0476-4829-b9e2-4239ff392dd3.png align="center")

Weird, a new commit plopped down below the commit we wanted to reverse. That's because this new commit `C2'` introduces *changes* -- it just happens to introduce changes that exactly reverse the commit of `C2`.

With reverting, you can push out your changes to share with others.

**Part -II will be Coming Soon...**

**Like and Subscribe for more blogs**
