This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Introduction

Branch in Git is like a parallel universe of your project.
It lets you work on new features without disturbing the main codebase.


Why Branches?

Imagine your project has a main branch.
You want to add a new feature. Instead of risking changes on main, you:

  1. Create a new branch.
  2. Work on it separately.
  3. Merge it back when ready.

Git provides the ability to create branches so developers can work on new features or bug fixes without disturbing the main codebase.
Changes you make to a branch are stored separately from other branches. You can switch between branches at any time. When your changes are ready and working, you can merge the branch back into your main branch.

1 - Working with Branches

Listing Branches

To see your local branches, use:

git branch

Expected output:

* main



To list all branches (including remote branches), use:

git branch -a

Expected output:

* main
  remotes/origin/main



To list Remote branches (including remote branches), use:

git branch -r

Expected output:

* remotes/origin/main


  • The branch marked with an asterisk (*) is your currently active branch.
  • Any changes or commits you make are added to this branch.

Tip:
Use branches to keep your work organized and your main branch stable!

2 - Creating Branches

Creating a Branch

Command 1: Create only

git branch <branch-name>
  • Creates a new branch.
  • Does not switch to it.
  • You’re still on the same branch as before.

Command 2: Create + switch

git checkout -b <branch-name>
  • Shortcut: creates the branch and switches to it.
  • Older syntax.

Command 3: Modern way

git switch -c <branch-name>
  • Recommended modern syntax.
  • Does the same thing as checkout -b.

3 - Understanding HEAD

What is HEAD in Git?

When working with Git, you’ll often hear about something called HEAD.
Think of HEAD as a special pointer that tells Git, “This is where you are right now.”


HEAD: Your Current Position

  • HEAD always points to your current branch.
  • That branch, in turn, points to a specific commit (a snapshot of your project).

Visualization:

HEAD
branch (e.g. main)
commit (e.g. a1b2c3d)
  • When you make a new commit, the branch moves forward, and so does HEAD.

How HEAD Moves

  • If you switch branches, HEAD moves to point to the new branch.
  • If you make a commit, HEAD (via the branch) points to the new commit.

Detached HEAD State

Sometimes, HEAD doesn’t point to a branch, but directly to a commit.
This is called a detached HEAD.

When does this happen?

  • When you checkout a specific commit, not a branch:
    git checkout <commit-hash>
    
  • Now, HEAD points straight to that commit, not to any branch.

Visualization:

HEAD
commit (not attached to any branch)

Why is this important?

  • If you make new commits in this state, they’re not attached to any branch.
  • If you switch branches, those commits can be lost (unless you create a new branch from here).

Recovering from Detached HEAD

If you accidentally end up in a detached HEAD and want to save your work:

  1. Create a new branch from here:
    git switch -c my-temporary-branch
    
  2. Now your work is safe on a branch!

Summary

  • HEAD is your current position in the repo.
  • Normally, HEAD points to a branch, which points to a commit.
  • Detached HEAD means HEAD points directly to a commit.
  • Always create a branch if you want to keep work done in detached HEAD!

Tip:
You can always check where HEAD is pointing by running:

git status

4 - Merging Branches

What is Merging?

Merging in Git is the process of taking the changes from one branch and combining them into another branch.
This is how you bring together work from different branches—like adding a new feature you built on a separate branch back into your main project.


Why Merge?

  • You might have a main branch that holds your stable code.
  • You create a feature branch to work on something new, so your main code isn’t disturbed.
  • When your feature is ready, you want to add it back to mainwithout losing any work from either branch.
  • Merging lets you combine the work from both branches, keeping all the commit history and changes.

How Does Merging Work?

When you merge, Git tries to automatically combine the changes from both branches.
If the changes don’t overlap, Git merges them automatically.
If the same lines were changed in both branches, you’ll get a merge conflict (see the Conflicts topic).


Types of Merges in Git

1. Fast-Forward Merge

What is a Fast-Forward Merge?

A fast-forward merge is the simplest type of merge.
It happens when the branch you’re merging into (like main) has not changed since you created your feature branch.
In this case, Git doesn’t need to do any real “combining”—it just moves the branch pointer forward.

Think of it like this:

  • Imagine your project is a line of commits.
  • You branch off main to make a feature branch.
  • You make some commits on feature.
  • Nobody else has made any new commits on main since you branched off.
  • When you merge feature back into main, Git just moves the main pointer forward to where feature is.

Visual Example

Before merge:

A---B---C   (main)
         \
          D---E   (feature)

Here, main is at commit C, and feature has two extra commits (D and E).

If main hasn’t changed since you branched off, a fast-forward merge just moves main forward:

After merge:

A---B---C---D---E   (main, feature)

Now, both main and feature point to the same commit (E).
No new “merge commit” is created—Git just advances the pointer.

Command

git checkout main
git merge feature

If you see a message like “Fast-forward”, you just did a fast-forward merge!


2. Three-Way Merge

What is a Three-Way Merge?

A three-way merge happens when both branches have new commits since they split.
This means both main and feature have moved forward independently.

Why is it called “three-way”?

Because Git looks at three commits:

  • The common ancestor (where the branches split)
  • The tip of the current branch (e.g., main)
  • The tip of the branch being merged (e.g., feature)

Git compares all three to figure out how to combine the changes.

Visual Example

Before merge:

A---B---C---F   (main)
         \
          D---E   (feature)
  • C is where you branched off.
  • main has new commits (F).
  • feature has new commits (D, E).

When you merge, Git creates a new merge commit that combines the changes from both branches:

After merge:

A---B---C---F---M   (main)
         \     /
          D---E   (feature)
  • M is the new merge commit.
  • Both main and feature now point to M.

Command

git checkout main
git merge feature

If you see a message about a “merge commit”, you just did a three-way merge!


3. No-Fast-Forward Merge (--no-ff)

What is a No-Fast-Forward Merge?

Sometimes, even if a fast-forward merge is possible, you want to force Git to create a merge commit.
This is called a no-fast-forward merge.

Why would you want this?

  • It makes your history clearer.
  • You can always see exactly when a feature branch was merged in, even if there were no changes on main.

Visual Example

Before merge (same as fast-forward):

A---B---C   (main)
         \
          D---E   (feature)

After a no-fast-forward merge:

A---B---C-------M   (main)
         \     /
          D---E   (feature)
  • M is the merge commit, even though it wasn’t strictly needed.

Command

git checkout main
git merge --no-ff feature

What Happens During a Merge?

  1. Switch to the branch you want to merge into (usually main):
    git checkout main
    
  2. Run the merge command:
    git merge feature
    
  3. Git tries to combine the changes:
    • If there are no conflicting changes, the merge is automatic.
    • If there are conflicts, Git will pause and ask you to resolve them.

Tip:
Always make sure you’re on the branch you want to merge into before running the merge command!

You can use git log --graph --oneline to visualize your branch and merge history.

5 - Handling Merge Conflicts

What is a Merge Conflict?

A merge conflict happens when Git cannot automatically combine changes from two branches.
This usually occurs when two branches have changed the same part of a file, or when one branch deletes a file that the other branch has modified.


Why Do Conflicts Happen?

  • Two people (or two branches) edit the same line in a file differently.
  • One branch deletes a file that the other branch edits.
  • Changes are made to the same section of a file in both branches.

Git tries to merge changes automatically, but if it’s unsure which change to keep, it stops and asks you to decide.


How Do You Know There’s a Conflict?

  • When you run git merge (or git rebase), Git will pause and show a message like:
    Auto-merging file.txt
    CONFLICT (content): Merge conflict in file.txt
    Automatic merge failed; fix conflicts and then commit the result.
    
  • git status will show files with conflicts as “Unmerged paths”.

What Does a Conflict Look Like?

Git marks the conflicting area in your file like this:

<<<<<<< HEAD
This is your change from the current branch.
=======
This is the change from the branch you are merging in.
>>>>>>> feature-branch
  • Everything between <<<<<<< HEAD and ======= is your branch’s version.
  • Everything between ======= and >>>>>>> branch-name is the other branch’s version.

How to Resolve a Conflict

  1. Open the conflicted file(s).
  2. Look for the conflict markers (<<<<<<<, =======, >>>>>>>).
  3. Decide what the final content should be.
    • Keep your changes, the other branch’s changes, or combine them.
  4. Remove the conflict markers and edit the file so it looks exactly how you want.
  5. Mark the conflict as resolved by adding the file:
    git add <filename>
    
  6. Complete the merge by committing:
    git commit
    
    • If you were in the middle of a merge, Git may auto-generate a commit message.

Example: Resolving a Conflict

Suppose both branches changed the same line in hello.txt:

After merging, hello.txt looks like:

Hello world!
<<<<<<< HEAD
This is my change.
=======
This is their change.
>>>>>>> feature-branch
Goodbye!

To resolve:

  • Decide which line (or both) you want to keep.
  • Edit the file, for example:
    Hello world!
    This is my change and their change.
    Goodbye!
    
  • Save the file.
  • Run:
    git add hello.txt
    git commit
    

Tips for Avoiding and Handling Conflicts

  • Pull often to keep your branch up to date.
  • Communicate with your team about what files you’re working on.
  • Keep commits small and focused to make conflicts easier to resolve.
  • Use tools like VS Code, GitKraken, or SourceTree for visual conflict resolution.

Tip:
You can always see which files have conflicts with git status.
Use git log --merge to see commits that are causing the

6 - Deleting Branches

Why Delete Branches?

Branches are great for working on features, bug fixes, or experiments.
But once a branch has served its purpose (for example, a feature is merged into main), it’s a good idea to delete it.
This keeps your repository clean and avoids confusion.


Deleting Local Branches

A local branch exists only on your computer.

Delete a Local Branch (Safe)

git branch -d <branch-name>
  • Deletes the branch only if it has been fully merged into your current branch.
  • Prevents you from accidentally deleting work that hasn’t been saved elsewhere.

Example:

git branch -d feature/login

Force Delete a Local Branch

git branch -D <branch-name>
  • Force deletes the branch, even if it hasn’t been merged.
  • Use with caution! You could lose work.

Example:

git branch -D old-experiment

Deleting Remote Branches

A remote branch is a branch stored on a remote server (like GitHub or GitLab), usually shared with others.

Delete a Remote Branch

git push origin --delete <branch-name>
  • Tells the remote server to remove the branch.
  • Other collaborators will no longer see this branch after they fetch or pull.

Example:

git push origin --delete feature/login

What Happens After Deletion?

  • Local deletion only affects your copy of the repository.
  • Remote deletion removes the branch for everyone, but others may still have a local copy until they delete it themselves.
  • Deleting a branch does not delete the commits—they remain in the repository history unless they are unreachable from any branch or tag.

Common Scenarios

  • After merging a feature branch:
    Delete the feature branch to keep your branch list tidy.
  • Abandoned or stale branches:
    Remove branches that are no longer needed to avoid confusion.

Safety Tips

  • Always make sure a branch is merged (or you no longer need its changes) before deleting.
  • Use git branch to list local branches and git branch -a to see all branches.
  • If you accidentally delete a branch, you can often recover it using git reflog (as long as the commits haven’t been garbage collected).

Tip:
Regularly clean up old branches to keep your repository organized and easy to

7 - Understanding Remote Branches

What is a Remote Branch?

A remote branch is a branch that exists on a remote repository (like GitHub, GitLab, or Bitbucket), not just on your local computer.
Remote branches let you collaborate with others by sharing your work and keeping your local repository in sync with the remote server.


How Do Remote Branches Work?

  • When you clone a repository, Git creates local copies of all the remote branches.
  • Remote branches are read-only references to the state of branches on the remote server.
  • They are named like origin/main, origin/feature/login, etc.
    • origin is the default name for the remote you cloned from.

Common Remote Branch Commands

1. Listing Remote Branches

  • List only remote branches:

    git branch -r
    

    Example output:

    origin/main
    origin/feature/login
    
  • List all branches (local and remote):

    git branch -a
    

    Example output:

    main
    feature/login
    remotes/origin/main
    remotes/origin/feature/login
    

2. Fetching Updates from Remote

  • Update your local copy of remote branches:
    git fetch
    
    • This downloads new commits and branch updates from the remote, but does not change your local branches.

3. Creating a Local Branch from a Remote Branch

If you see a remote branch (like origin/feature/login) and want to work on it locally:

git checkout -b my-local-branch origin/feature/login
  • This creates a new local branch called my-local-branch that starts from the remote branch.

4. Pushing Local Branches to Remote

  • Push a new local branch to the remote:
    git push origin <branch-name>
    
    • This creates a new branch on the remote server.

5. Tracking Remote Branches

When you create a local branch from a remote branch, Git can set up tracking so your local branch knows which remote branch it’s connected to.

  • Set upstream (tracking) branch:
    git branch -u origin/main
    
    • Now, you can use git pull and git push without specifying the branch name.

How Do Remote Branches Get Updated?

  • When you run git fetch, Git updates your local copies of the remote branches.
  • When you run git pull, Git fetches and then merges changes from the remote branch into your current branch.

Deleting Remote Branches

  • Delete a branch from the remote server:
    git push origin --delete <branch-name>
    
    • This removes the branch from the remote for everyone.

Why Are Remote Branches Important?

  • They allow teams to collaborate by sharing code.
  • They help keep everyone’s work in sync.
  • You can always see what branches exist on the remote and what their latest commits are.

Tip:
Remote branches are read-only. To make changes, create a local branch from the remote branch, work on it, then push your

8 - Understanding Upstream Branches

What is an Upstream Branch?

An upstream branch in Git is the remote branch that your local branch is “tracking.”
This means your local branch knows which remote branch it should compare itself to when you run commands like git pull or git push—so you don’t have to specify the remote branch every time.


Why Use Upstream Branches?

  • Convenience:
    You can simply run git pull or git push without extra arguments, and Git knows which remote branch to use.
  • Collaboration:
    Keeps your local branch in sync with the team’s shared branch on the remote server.
  • Safety:
    Helps prevent mistakes, like pushing to the wrong branch.

How Do You Set an Upstream Branch?

When Creating a Local Branch from a Remote Branch

If you create a local branch from a remote branch, Git usually sets the upstream automatically:

git checkout -b my-feature origin/my-feature
  • Now, my-feature (local) tracks origin/my-feature (remote).

Setting or Changing Upstream Manually

You can set or change the upstream branch for your current branch with:

git branch -u origin/main
  • This tells Git: “My current branch should track origin/main.”

Or, if you want to set it while pushing for the first time:

git push -u origin my-feature
  • The -u (or --set-upstream) flag tells Git to set the upstream branch.

How Do You Know What Your Upstream Branch Is?

To see what upstream branch your current branch is tracking:

git status
  • You’ll see something like:
    Your branch is up to date with 'origin/main'.

Or, for more detail:

git branch -vv
  • This shows all local branches and their upstream branches.

What Happens When You Pull or Push?

  • git pull
    Fetches changes from the upstream branch and merges them into your local branch.
  • git push
    Sends your local commits to the upstream branch on the remote.

If you don’t have an upstream branch set, Git will ask you to specify where to push or pull.


Changing or Removing Upstream Branch

  • Change upstream:
    git branch -u origin/another-branch
    
  • Remove upstream:
    git branch --unset-upstream
    

Tip:
Always check your upstream branch before pushing or pulling, especially if you’re working on multiple features or with

9 - Renaming Branches

Why Rename a Branch?

Sometimes you create a branch with a quick name, but later want something more descriptive or consistent.
Renaming branches helps keep your repository organized and clear for everyone working on it.


Renaming a Local Branch

Rename the Current Branch

If you are on the branch you want to rename:

git branch -m new-branch-name
  • -m stands for “move” (rename).
  • This changes the branch name in your local repository.

Example:

git branch -m feature/login feature/authentication

Or, if you’re already on feature/login:

git branch -m feature/authentication

Rename a Different Local Branch

If you want to rename a branch you’re not currently on:

git branch -m old-branch-name new-branch-name

Renaming a Remote Branch

Renaming a branch on the remote (like GitHub) is a two-step process:

1. Push the Renamed Branch to Remote

git push origin new-branch-name
  • This creates a new branch on the remote with the new name.

2. Delete the Old Branch from Remote

git push origin --delete old-branch-name
  • This removes the old branch name from the remote.

If your local branch was tracking the old remote branch, update it to track the new one:

git branch -u origin/new-branch-name

What About Other Collaborators?

  • After you rename and push, others will still have the old branch name locally.
  • They should fetch the latest changes and delete the old branch locally:
    git fetch origin --prune
    git branch -d old-branch-name
    

Tip:
Renaming branches helps keep your project tidy and your workflow clear—don’t be afraid to update names as your project evolves!

  • Make sure no one else is actively working on the branch you’re renaming, or coordinate with your team.
  • Always double-check branch names before deleting anything on the remote.

10 - Rebasing Branches

What is Rebasing?

Rebasing is a way to move or combine a sequence of commits to a new base commit.
In simple terms, rebasing lets you “replay” your work from one branch onto another branch, creating a cleaner, linear history.


Why Use Rebase?

  • Cleaner History:
    Rebasing creates a straight line of commits, making the project history easier to read.
  • Avoids Merge Commits:
    Unlike merging, rebasing doesn’t create extra merge commits unless there are conflicts.
  • Keeps Features Up-to-Date:
    You can update your feature branch with the latest changes from main before merging.

How Does Rebasing Work?

Imagine you have this history:

A---B---C main
         \
          D---E feature

If you run git rebase main while on feature, Git will:

  1. Temporarily remove commits D and E from feature.
  2. Move feature to the tip of main (commit C).
  3. Replay D and E on top of C.

Result:

A---B---C---D'---E' feature
  • D' and E' are new commits (copies of D and E).

Basic Rebase Command

git checkout feature
git rebase main
  • This moves your feature branch to start from the latest commit on main.

Interactive Rebase

Interactive rebase lets you edit, reorder, squash, or drop commits.

git rebase -i HEAD~n
  • Replace n with the number of commits you want to edit.
  • You’ll see a list of commits in your editor, where you can:
    • pick (keep as is)
    • reword (edit commit message)
    • squash (combine commits)
    • drop (remove a commit)

Rebasing vs. Merging

ActionResultHistory Shape
MergeCombines branches, creates merge commitsBranched
RebaseMoves commits, creates linear historyStraight line
  • Merge preserves the full branch structure.
  • Rebase rewrites history for a cleaner look.

Handling Conflicts During Rebase

  • If there’s a conflict, Git will pause and let you resolve it.
  • After fixing the conflict, run:
    git add <file>
    git rebase --continue
    
  • If you want to abort the rebase:
    git rebase --abort
    

When (Not) to Rebase

  • Do rebase:
    • On your own feature branches before merging, to clean up history.
  • Do NOT rebase:
    • On public branches that others are using. Rebasing rewrites commit history, which can confuse collaborators.

Visualizing Rebase

Use this command to see your commit history as a graph:

git log --oneline --graph --all

Tip:
Rebasing is powerful for keeping your history tidy, but always be

10.1 - Interactive Rebase in Git

What is Interactive Rebase?

Interactive rebase is a powerful Git feature that lets you rewrite, edit, reorder, squash, or even delete commits in your branch’s history.
It’s called “interactive” because Git opens an editor and lets you choose exactly what happens to each commit.


Why Use Interactive Rebase?

  • Clean up messy commit history before sharing your work.
  • Combine (squash) multiple commits into one.
  • Edit commit messages for clarity or consistency.
  • Reorder commits to make history logical.
  • Remove unwanted commits (like mistakes or debug code).
  • Split a commit into smaller, more focused commits.

How to Start an Interactive Rebase

Decide how many commits you want to work with (counting backwards from your current commit):

git rebase -i HEAD~n
  • Replace n with the number of commits you want to edit.
  • Example: To edit the last 4 commits:
    git rebase -i HEAD~4
    

The Rebase Todo List

Git opens your default text editor with a list like:

pick a1b2c3d Add login form
pick b2c3d4e Fix typo
pick c3d4e5f Add logout button
pick d4e5f6g Update styles
  • Each line is a commit, from oldest (top) to newest (bottom).
  • The word pick means “keep this commit as is.”

What Can You Do in Interactive Rebase?

Replace pick with one of these commands:

  • pick: Use the commit as is.
  • reword: Edit the commit message.
  • edit: Pause to change the commit’s content (amend).
  • squash: Combine this commit into the previous one (good for cleanup).
  • fixup: Like squash, but discard this commit’s message.
  • drop: Remove the commit entirely.
  • exec: Run a shell command.

Example: Squash and Reword

pick a1b2c3d Add login form
squash b2c3d4e Fix typo
reword c3d4e5f Add logout button
pick d4e5f6g Update styles

Step-by-Step Example: Squashing and Editing

Suppose you want to:

  • Combine “Fix typo” into “Add login form”
  • Edit the message for “Add logout button”
  1. Start the rebase:
    git rebase -i HEAD~4
    
  2. In the editor, change:
    pick a1b2c3d Add login form
    squash b2c3d4e Fix typo
    reword c3d4e5f Add logout button
    pick d4e5f6g Update styles
    
  3. Save and close the editor.
  4. Git will prompt you to combine commit messages for the squashed commits.
  5. Git will prompt you to edit the commit message for the “reword” commit.
  6. If there are conflicts, Git will pause and ask you to resolve them:
    • Fix the files.
    • Run git add <file>.
    • Continue with git rebase --continue.

Aborting or Continuing

  • Abort the rebase and return to original state:
    git rebase --abort
    
  • Continue after resolving conflicts:
    git rebase --continue
    

When to Use Interactive Rebase

  • Before merging a feature branch, to tidy up your commits.
  • When you want to split, combine, or reorder commits for clarity.
  • When you need to edit a commit message or remove a mistake.

Caution

  • Interactive rebase rewrites history.
    Only use it on branches that haven’t been pushed/shared with others, or coordinate with your team.
  • Never rebase public/shared branches unless everyone agrees.

Tip:
Interactive rebase is one of the most powerful tools in Git for crafting a clean, professional commit history.
Practice on a test branch to get comfortable before using it on important

10.2 - Squash Commits with Interactive Rebase

What is Squashing?

Squashing means combining multiple commits into a single commit.
This is useful for cleaning up your commit history before merging a feature branch—so instead of a long list of tiny or “work in progress” commits, you have one clear, meaningful commit.


Why Squash Commits?

  • Cleaner History:
    Makes your project history easier to read and understand.
  • Atomic Changes:
    Groups related changes together, making it easier to review and revert if needed.
  • Professionalism:
    Shows a tidy, intentional history when collaborating or submitting pull requests.

How to Squash Commits Using Interactive Rebase

Suppose your branch has several commits you want to squash into one.

1. Start an Interactive Rebase

Decide how many commits you want to squash.
For example, to squash the last 3 commits:

git rebase -i HEAD~3

2. The Rebase Editor

Git will open your default text editor with a list like:

pick a1b2c3d First commit
pick b2c3d4e Second commit
pick c3d4e5f Third commit
  • The commits are listed from oldest (top) to newest (bottom).

3. Mark Commits to Squash

  • Leave pick on the first commit.
  • Change pick to squash (or just s) on the commits you want to combine into the first.

Example:

pick a1b2c3d First commit
squash b2c3d4e Second commit
squash c3d4e5f Third commit
  • This will squash the second and third commits into the first.

4. Write the Commit Message

  • After saving and closing the editor, Git will open another editor window to combine the commit messages.
  • Edit the message to describe the combined changes clearly.
  • Save and close the editor.

5. Finish the Rebase

  • If there are no conflicts, you’re done!
  • If there are conflicts, Git will pause and ask you to resolve them. After fixing, run:
    git add <file>
    git rebase --continue
    

Example: Before and After Squash

Before:

* c3d4e5f - Third commit
* b2c3d4e - Second commit
* a1b2c3d - First commit

After squashing:

* z9y8x7w - Combined commit (contains all changes)

Squash While Merging (Alternative)

You can also squash all commits from a feature branch into one when merging to main:

git checkout main
git merge --squash feature-branch
git commit
  • This prepares a single combined commit from the feature branch.

When to Squash

  • Before merging a feature branch into main or develop.
  • When you want to clean up “work in progress” commits.
  • When submitting a pull request and the project prefers a tidy history.

Tip:
Squashing is a great way to make your project history clean and professional.
But remember: **never squash commits on a branch that others are working

10.3 - Reordering Commits with Interactive Rebase

Why Reorder Commits?

Sometimes, you make commits in an order that doesn’t make sense for the project’s history.
Maybe you fixed a typo before adding the main feature, or you want related changes grouped together.
Reordering commits helps you organize your history so it’s logical, readable, and easy to review.


How to Reorder Commits in Git

You use interactive rebase to reorder commits.
This lets you pick up any commit and move it before or after others.


Step-by-Step: Reordering Commits

Suppose your history looks like this (from newest to oldest):

* d4e5f6g - Update styles
* c3d4e5f - Add logout button
* b2c3d4e - Fix typo
* a1b2c3d - Add login form

But you want “Fix typo” to come after “Add login form” and before “Add logout button”.


1. Start an Interactive Rebase

Decide how many commits you want to reorder (counting backwards from HEAD):

git rebase -i HEAD~4

2. The Rebase Todo List

Git opens your editor with:

pick a1b2c3d Add login form
pick b2c3d4e Fix typo
pick c3d4e5f Add logout button
pick d4e5f6g Update styles
  • Commits are listed from oldest (top) to newest (bottom).
  • The order here is the order they will appear in history after the rebase.

3. Change the Order

To reorder, simply move the lines up or down.

Example:
To put “Fix typo” after “Add logout button”:

pick a1b2c3d Add login form
pick c3d4e5f Add logout button
pick b2c3d4e Fix typo
pick d4e5f6g Update styles
  • Save and close the editor.

4. Finish the Rebase

  • Git will replay the commits in the new order.
  • If there are conflicts, Git will pause and let you resolve them:
    • Fix the files.
    • Run git add <file>.
    • Continue with git rebase --continue.

Tips for Reordering Commits

  • Test after reordering:
    Changing commit order can sometimes cause conflicts or break your code if commits depend on each other.
  • Keep related changes together:
    Grouping related commits makes your history easier to understand.
  • Use meaningful commit messages:
    After reordering, you can also use reword to update messages for clarity.

When to Reorder Commits

  • Before merging a feature branch, to make history logical.
  • When you want to group bug fixes or features together.
  • When preparing a pull request for review.

Caution

  • Reordering rewrites history.
    Only reorder commits on branches that haven’t been pushed/shared, or coordinate with your team.
  • Never reorder commits on public/shared branches unless everyone agrees.

Tip:
Reordering commits is a great way to make your project history logical and professional.
Practice on a test branch to get comfortable before using it on important

11 - Reordering Commits

Why Reorder Commits?

Sometimes, you make commits in an order that doesn’t make sense for the project’s history.
Maybe you fixed a typo before adding the main feature, or you want related changes grouped together.
Reordering commits helps you organize your history so it’s logical, readable, and easy to review.


How to Reorder Commits in Git

You use interactive rebase to reorder commits.
This lets you pick up any commit and move it before or after others.


Step-by-Step: Reordering Commits

Suppose your history looks like this (from newest to oldest):

* d4e5f6g - Update styles
* c3d4e5f - Add logout button
* b2c3d4e - Fix typo
* a1b2c3d - Add login form

But you want “Fix typo” to come after “Add login form” and before “Add logout button”.


1. Start an Interactive Rebase

Decide how many commits you want to reorder (counting backwards from HEAD):

git rebase -i HEAD~4

2. The Rebase Todo List

Git opens your editor with:

pick a1b2c3d Add login form
pick b2c3d4e Fix typo
pick c3d4e5f Add logout button
pick d4e5f6g Update styles
  • Commits are listed from oldest (top) to newest (bottom).
  • The order here is the order they will appear in history after the rebase.

3. Change the Order

To reorder, simply move the lines up or down.

Example:
To put “Fix typo” after “Add logout button”:

pick a1b2c3d Add login form
pick c3d4e5f Add logout button
pick b2c3d4e Fix typo
pick d4e5f6g Update styles
  • Save and close the editor.

4. Finish the Rebase

  • Git will replay the commits in the new order.
  • If there are conflicts, Git will pause and let you resolve them:
    • Fix the files.
    • Run git add <file>.
    • Continue with git rebase --continue.

Tips for Reordering Commits

  • Test after reordering:
    Changing commit order can sometimes cause conflicts or break your code if commits depend on each other.
  • Keep related changes together:
    Grouping related commits makes your history easier to understand.
  • Use meaningful commit messages:
    After reordering, you can also use reword to update messages for clarity.

When to Reorder Commits

  • Before merging a feature branch, to make history logical.
  • When you want to group bug fixes or features together.
  • When preparing a pull request for review.

Caution

  • Reordering rewrites history.
    Only reorder commits on branches that haven’t been pushed/shared, or coordinate with your team.
  • Never reorder commits on public/shared branches unless everyone agrees.

Tip:
Reordering commits is a great way to make your project history logical and professional.
Practice on a test branch to get comfortable before using it on important work!

12 - Branching Workflows

What is a Branching Workflow?

A branching workflow is a strategy for how you and your team use branches to organize work in a Git repository.
It defines how features, fixes, releases, and collaboration happen—making teamwork smoother and your project history clearer.


Why Use a Workflow?

  • Organization:
    Keeps features, fixes, and releases separate.
  • Collaboration:
    Allows multiple people to work independently.
  • Safety:
    Protects stable code from unfinished or experimental changes.
  • Traceability:
    Makes it easy to see what was done, when, and by whom.

Common Branching Workflows

1. Feature Branch Workflow

  • Each new feature or fix gets its own branch.
  • Branches are created from the main branch (often called main or master).
  • When finished, the feature branch is merged back into main.

Example:

git checkout -b feature/login
  • Work on your feature, then merge:
git checkout main
git merge feature/login

Benefits:
Keeps features isolated, makes code review easier.


2. Gitflow Workflow

A popular workflow for larger projects.

  • Main branches:
    • main (or master): Always stable, production-ready code.
    • develop: Integration branch for new features.
  • Supporting branches:
    • feature/*: For new features.
    • release/*: For preparing releases.
    • hotfix/*: For urgent fixes to production.

Example:

git checkout -b feature/payment develop
  • Merge features into develop, then create a release branch when ready.

Benefits:
Organized, clear process for releases and hotfixes.


3. Trunk-Based Development

  • Everyone works on a single branch (usually main).
  • Feature branches are short-lived—merged quickly.
  • Encourages frequent integration and small changes.

Example:

git checkout -b quick-fix
# Make changes
git checkout main
git merge quick-fix

Benefits:
Fast-paced, reduces merge conflicts, ideal for continuous integration.


4. Fork + Pull Request Workflow

Common on platforms like GitHub and GitLab.

  • Each contributor forks the repository (creates their own copy).
  • Work happens in branches on the fork.
  • Changes are proposed back to the original repo via a pull request (PR).

Example:

  • Fork the repo.
  • Create a branch on your fork:
    git checkout -b feature/docs
    
  • Push and open a PR to the main repo.

Benefits:
Safe for open source, allows code review before merging.


Choosing a Workflow

  • Small teams/projects:
    Feature branch or trunk-based is often enough.
  • Large teams/multiple releases:
    Gitflow or fork + PR workflows add structure.
  • Open source:
    Fork + PR is standard.

Workflow Tips

  • Name branches clearly:
    Use descriptive names like feature/login, bugfix/header, or hotfix/payment-crash.
  • Keep branches focused:
    One feature or fix per branch.
  • Merge often:
    Avoid long-lived branches to reduce conflicts.
  • Protect important branches:
    Use branch protection rules on main or develop to prevent accidental changes.

Tip:
Pick a workflow that matches your team size, release process, and collaboration style.
Document your workflow so everyone knows how to

13 - Branch Protection

What is Branch Protection?

Branch protection is a set of rules that prevent unwanted changes to important branches in your repository—like main, master, or develop.
It’s a safety feature, usually managed on platforms like GitHub, GitLab, or Bitbucket, to keep your codebase stable and secure.


Why Use Branch Protection?

  • Prevent mistakes:
    Stops accidental force-pushes, deletions, or direct commits to critical branches.
  • Enforce code review:
    Require pull requests and approvals before merging changes.
  • Maintain stability:
    Ensure only tested, reviewed code reaches production branches.
  • Automate checks:
    Require passing tests, successful builds, or other checks before merging.

Common Branch Protection Rules

1. Require Pull Requests

  • Disallow direct pushes to protected branches.
  • All changes must go through a pull request (PR) for review.

2. Require Reviews and Approvals

  • Specify that one or more team members must approve a PR before it can be merged.

3. Require Status Checks

  • Only allow merging if automated tests, builds, or other checks pass.

4. Restrict Who Can Push

  • Limit who can push, merge, or delete the branch (e.g., only admins or maintainers).

5. Prevent Force-Pushes and Deletion

  • Block force-pushes (git push --force) to avoid rewriting history.
  • Prevent accidental or malicious branch deletion.

How to Set Up Branch Protection

On GitHub

  1. Go to your repository on GitHub.
  2. Click Settings > Branches.
  3. Add a branch protection rule for your chosen branch (e.g., main).
  4. Select the rules you want (require PRs, reviews, status checks, etc.).
  5. Save the rule.

On GitLab

  1. Go to your repository.
  2. Click Settings > Repository > Protected Branches.
  3. Choose the branch and set permissions for who can push, merge, or delete.

On Bitbucket

  1. Go to your repository.
  2. Click Repository Settings > Branch permissions.
  3. Add rules for your branches.

Example: GitHub Branch Protection

  • Require pull request before merging.
  • Require at least one approval.
  • Require status checks to pass.
  • Restrict who can push.
  • Block force-pushes and deletion.

When to Protect a Branch

  • Production branches:
    Always protect main, master, or any branch deployed to users.
  • Release branches:
    Protect branches used for releases or hotfixes.
  • Develop/integration branches:
    Protect if multiple people merge features into them.

Tips for Branch Protection

  • Document your rules:
    Make sure everyone on your team knows which branches are protected and why.
  • Review regularly:
    Update protection rules as your team or workflow changes.
  • Combine with workflows:
    Use protection rules alongside branching workflows for maximum safety.

Tip:
Set it up early—don’t wait for a mistake to happen!

14 - Bonus Concepts

This section covers advanced and useful Git concepts related to branching, history, and project management.
Even if you’re new to Git, these tools and ideas will help you understand and recover your project in tricky situations!


Tags vs Branches

What is a Tag?

  • A tag is a fixed pointer to a specific commit.
  • Used to mark important points in history, like releases (v1.0.0, v2.1.3).
  • Tags do not move—they always point to the same commit.

Create a tag:

git tag v1.0.0

List tags:

git tag

Push tags to remote:

git push origin v1.0.0

How is a Tag Different from a Branch?

TagBranch
Fixed pointer to a commitMovable pointer to latest commit
Used for releases/versionsUsed for ongoing development
Does not changeMoves as new commits are added

Detached HEAD & Temporary Branches

What is Detached HEAD?

  • HEAD is Git’s pointer to your current branch.
  • Detached HEAD means HEAD points directly to a commit, not a branch.
  • Happens when you checkout a specific commit:
    git checkout a1b2c3d
    
  • You’re “not on a branch”—any new commits are not attached to a branch.

Why Use Detached HEAD?

  • Experiment with changes without affecting any branch.
  • Review or test old versions of your code.

How to Save Work from Detached HEAD

If you want to keep your work:

git switch -c temp-branch
  • This creates a new branch from your current state, saving your changes.

Orphan Branches

What is an Orphan Branch?

  • An orphan branch starts with no history—it’s a completely fresh branch.
  • Useful for creating documentation, gh-pages, or starting a new project in the same repo.

Create an orphan branch:

git checkout --orphan docs
  • Your working directory stays the same, but the branch has no commits.
  • Add and commit files as usual.

Reflog: Recover Lost Commits and Branches

What is Reflog?

  • Reflog records every change to HEAD (branch checkouts, commits, resets, etc.).
  • Lets you recover lost commits, branches, or undo mistakes—even after deletion.

View reflog:

git reflog
  • Shows a list of recent HEAD positions.

Recover a deleted branch:

  1. Find the commit hash in git reflog.
  2. Create a branch from it:
    git branch recovered-branch <commit-hash>
    

Bisecting: Find the Buggy Commit

What is Bisecting?

  • git bisect helps you find which commit introduced a bug by binary search.
  • You mark a “good” commit and a “bad” commit, and Git checks commits in between.

Start bisect:

git bisect start
git bisect bad                # Mark current commit as bad
git bisect good a1b2c3d       # Mark known good commit
  • Git checks out a commit in the middle. Test it, then mark as good or bad.
  • Repeat until you find the exact commit that introduced the bug.

Finish bisect:

git bisect reset

Visualization Tools

Command-Line Visualization

  • Show commit history as a graph:
    git log --oneline --graph --all
    
  • Show branches and merges visually.

GUI Tools

  • Gitk:
    Run gitk for a graphical history viewer.
  • SourceTree, GitKraken:
    Free tools for visualizing branches, merges, and history.
  • VS Code GitLens:
    Powerful extension for viewing history and branch structure inside VS Code.

Tip:
These advanced tools and concepts help you manage, recover, and understand your project’s history.
Practice using them—they can save your