Master Git & GitHub Workflows

Build a portfolio site while mastering Git and GitHub CI/CD workflows.

Introduction

30 Second Summary

Every professional development team relies on version control to collaborate without chaos. Your Git history tells future employers how you think, organize, and ship code.

In this project, you will build a personal portfolio site and deploy it using GitHub Actions, while mastering Git workflows from basic commits all the way to advanced history rewriting and branch protection.

What You'll Build

A personal portfolio site deployed to GitHub Pages, backed by a professionally structured repository with feature branches, clean commit history, automated CI/CD, and branch protection via rulesets.

By the end of this project, you'll have:

  • A public GitHub repository with a clean, professional commit history demonstrating branching, merging, and rebasing.
  • A working GitHub Actions CI/CD pipeline that automatically deploys your portfolio site to GitHub Pages on every push to main.
  • Hands-on experience with interactive rebase, stash, cherry-pick, reflog recovery, conflict resolution, rulesets, tags, and releases.
  • Secret Mission: Add a multi-environment deployment workflow with a staging branch and tag-triggered releases.

Are there any prerequisites?

You need a free GitHub account and basic HTML/CSS knowledge. The site content is minimal and provided for you, so the focus stays on Git and GitHub workflows.

Set Up Git and Create Your Repository

Every professional project starts with a solid foundation. Git tracks every change you make to your code, and GitHub gives your project a home online where others (and automated workflows) can access it.

In this step, you will configure Git on your machine, create a GitHub repository, and push your first code. This sets the stage for all the branching, merging, and automation you will do in later steps.

In this step, get ready to:
  • Verify your Git installation and configure your identity.
  • Create a public GitHub repository.
  • Push your initial project files to GitHub.
Verify Git and configure your identity

Before writing any code, you need to confirm Git is installed and tell it who you are. Every commit you make carries your name and email as the author.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Antigravity and press Enter to open the Antigravity IDE.
  • Open the built-in terminal by pressing Cmd+J (macOS) or Ctrl+J (Windows/Linux).

What is the Antigravity IDE terminal?

Google Antigravity IDE has a built-in terminal at the bottom of the window. It works exactly like any other terminal on your system, so all Git commands run identically here.

  • Check that Git is installed by running this command:
git --version

✔️ I see a version number

You should see output like git version 2.x.x. Git is ready to go.

ⓧ Command not found

Git is not installed on your system. Download and install it from the official site.

  • Visit https://git-scm.com/downloads and download the installer for your operating system.
  • Run the installer and accept the default options.
  • Close and reopen Antigravity IDE, then open the terminal again with Ctrl+J (Windows) or Cmd+J (macOS).
  • Run git --version again to confirm it works.

Now configure your name and email. These appear on every commit you make.

  • Set your Git username by running this command:
git config --global user.name [[YOURNAME="your full name"]]
  • Set your Git email by running this command:
git config --global user.email [[YOUREMAIL="your email address"]]

Use your GitHub email

Use the same email address associated with your GitHub account. This ensures your commits link to your GitHub profile.

  • Confirm your configuration by running:
git config --global --list

You should see your user.name and user.email listed in the output.

Don't see your name or email?

Make sure you included the quotes around your name in the git config command. Without quotes, Git only captures the first word.

help me fix my git config

Create a public repository on GitHub

Your code needs a home on GitHub. Creating a remote repository first gives you the URL you will connect your local project to.

  • Open your browser and go to github.com.
  • Sign in to your GitHub account.
  • Click the + icon in the top-right corner of the page.
  • Select New repository.
  • Enter portfolio-site as the repository name.
  • Select Public for the visibility.
  • Leave all other options unchecked. Do NOT initialize with a README, .gitignore, or license.
  • Click Create repository.

Why skip the README?

Initializing with a README creates a commit on GitHub that your local project does not have. This causes conflicts when you try to push. Starting empty keeps the remote clean so you can push your local work directly.

  • On the next page, find the HTTPS URL that looks like https://github.com/your-username/portfolio-site.git.
  • Copy this URL. You will need it shortly.
Initialize your local project and push to GitHub

Now you will create a project folder on your computer, add your starter files, and push everything to the GitHub repository you just created.

  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir portfolio-site
cd portfolio-site
  • Initialize a new Git repository by running:
git init

You should see a message saying Initialized empty Git repository in the output.

What does git init do?

This command creates a hidden .git folder inside portfolio-site. That folder is where Git stores the entire history of your project. Without it, Git has no idea your folder is a repository.

Next, open this folder in the Antigravity IDE editor so you can create your files.

  • In Antigravity IDE, click File in the top menu bar.
  • Click Open Folder.
  • Navigate to your Desktop and select the portfolio-site folder, then click Open.
  • If prompted to trust the authors of this folder, click Yes, I trust the authors.

Now create the first file for your portfolio site.

  • In the Explorer sidebar on the left, click the new file icon (a page with a + symbol).
  • Type index.html and press Enter.
  • Paste the following code into index.html:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Portfolio</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <header>
        <h1>Your Name</h1>
        <p>Software Developer</p>
    </header>

    <footer>
        <p>Built with Git &amp; GitHub Actions</p>
    </footer>
</body>
</html>

What does this HTML do?

This is a minimal portfolio page with a header showing your name and role, plus a footer. The <link> tag connects to a stylesheet you will create next.

You will add more sections (About, Projects, Contact) in later steps as you practice branching and merging.

  • Save index.html with Cmd+S (macOS) or Ctrl+S (Windows).

Now create the stylesheet.

  • In the Explorer sidebar, click the new file icon again.
  • Type style.css and press Enter.
  • Paste the following code into style.css:
/* Reset default browser styles */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

/* Main body layout */
body {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    line-height: 1.6;
    color: #333;
    max-width: 800px;
    margin: 0 auto;
    padding: 2rem;
}

/* Header section styling */
header {
    text-align: center;
    padding: 3rem 0;
    border-bottom: 2px solid #eee;
    margin-bottom: 2rem;
}

header h1 {
    font-size: 2.5rem;
    color: #1a1a1a;
}

header p {
    font-size: 1.2rem;
    color: #666;
    margin-top: 0.5rem;
}

/* Section spacing */
section {
    margin-bottom: 2.5rem;
}

section h2 {
    font-size: 1.8rem;
    margin-bottom: 1rem;
    color: #1a1a1a;
}

/* Project card component */
.project-card {
    background: #f8f9fa;
    border: 1px solid #e9ecef;
    border-radius: 8px;
    padding: 1.5rem;
    margin-bottom: 1rem;
}

.project-card h3 {
    color: #0366d6;
    margin-bottom: 0.5rem;
}

/* List styling */
ul {
    list-style: none;
    padding: 0;
}

ul li {
    padding: 0.3rem 0;
}

/* Footer styling */
footer {
    text-align: center;
    padding: 2rem 0;
    border-top: 2px solid #eee;
    margin-top: 2rem;
    color: #666;
}

Why include styles for sections that don't exist yet?

The CSS includes rules for .project-card, section, and ul elements that are not in the HTML yet. CSS rules for missing elements are simply ignored by the browser.

Having the full stylesheet ready means you can focus on Git workflows when you add those sections later, instead of stopping to write CSS each time.

  • Save style.css with Cmd+S (macOS) or Ctrl+S (Windows).
  • Confirm the files exist by running this in your terminal:
ls

You should see index.html and style.css in the output.

Don't see both files?

Make sure your terminal is in the portfolio-site folder. Run pwd to check your current directory. If you are somewhere else, run cd ~/Desktop/portfolio-site to get back.

help me find my project files

✔️ Awesome, I've got everything!

Great. Double check you have saved both files before moving on.

ⓧ I'd like to double check the full code

Here is what index.html should look like:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Portfolio</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <header>
        <h1>Your Name</h1>
        <p>Software Developer</p>
    </header>

    <footer>
        <p>Built with Git &amp; GitHub Actions</p>
    </footer>
</body>
</html>

And here is what style.css should look like:

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    line-height: 1.6;
    color: #333;
    max-width: 800px;
    margin: 0 auto;
    padding: 2rem;
}

header {
    text-align: center;
    padding: 3rem 0;
    border-bottom: 2px solid #eee;
    margin-bottom: 2rem;
}

header h1 {
    font-size: 2.5rem;
    color: #1a1a1a;
}

header p {
    font-size: 1.2rem;
    color: #666;
    margin-top: 0.5rem;
}

section {
    margin-bottom: 2.5rem;
}

section h2 {
    font-size: 1.8rem;
    margin-bottom: 1rem;
    color: #1a1a1a;
}

.project-card {
    background: #f8f9fa;
    border: 1px solid #e9ecef;
    border-radius: 8px;
    padding: 1.5rem;
    margin-bottom: 1rem;
}

.project-card h3 {
    color: #0366d6;
    margin-bottom: 0.5rem;
}

ul {
    list-style: none;
    padding: 0;
}

ul li {
    padding: 0.3rem 0;
}

footer {
    text-align: center;
    padding: 2rem 0;
    border-top: 2px solid #eee;
    margin-top: 2rem;
    color: #666;
}

Now it is time to make your first commit and push your code to GitHub.

  • Stage all your files for commit by running:
git add .
  • Create your first commit by running:
git commit -m "Initial commit"

What does this commit do?

The git add . command stages every file in the current folder, telling Git you want to include them in the next snapshot. The git commit command saves that snapshot permanently in your project history with the message "Initial commit".

You should see output listing the files that were committed. Now connect your local repository to the one on GitHub.

  • Connect your remote repository by running this command (replace the URL with the one you copied from GitHub):
git remote add origin [[REPO_URL="https://github.com/your-username/portfolio-site.git"]]
  • Push your code to GitHub by running:
git push -u origin main

What does -u origin main mean?

The -u flag sets up tracking between your local main branch and the remote main branch on GitHub. After this, you can simply run git push without specifying the branch name every time.

You should see output indicating your branch was pushed successfully to GitHub.

  • Refresh your GitHub repository page in the browser.
  • You should see index.html and style.css listed in the repository.

Push failed or asked for credentials?

If Git asks for your username and password, GitHub no longer accepts passwords for HTTPS pushes. You need a Personal Access Token. Go to GitHub Settings > Developer settings > Personal access tokens > Tokens (classic), generate one with repo scope, and use it as your password when prompted.

If you see an error about the remote already existing, run git remote -v to check the current URL. You can update it with git remote set-url origin followed by the correct URL.

help me push my code to GitHub

Your foundation is set. You have a local repository connected to GitHub with your first commit in place. Next up, you will learn how to create branches, merge changes, and resolve conflicts like a pro.

Branch, Merge, Resolve Conflicts, and Stash

In the previous step, you created your repository and pushed your first commit. Now it's time to learn the workflows that make Git powerful on real teams.

Branches let multiple people (or one person wearing multiple hats) work on different features without stepping on each other's code. In this step, you'll create branches, merge them together, handle the inevitable conflicts, and learn to stash work-in-progress when you need to context-switch.

In this step, get ready to:
  • Create a feature branch, add an About section, and merge it into main.
  • Simulate a merge conflict between two branches and resolve it manually.
  • Use stashing to save unfinished work, switch contexts, and restore it later.
Create and merge a feature branch

A branch is a parallel line of development. You create one for each feature, make your changes in isolation, and then merge the finished work back into main.

  • Create a new branch called feature/about-section and switch to it by running this command:
git checkout -b feature/about-section

What does this command do?

The -b flag tells Git to create a new branch AND switch to it in one step. Without -b, git checkout only switches to an existing branch.

Now you're on the feature/about-section branch. Any changes you make here won't affect main until you merge.

  • In index.html, find the closing </header> tag. Add the following About section between </header> and <footer>:
    <section id="about">
        <h2>About Me</h2>
        <p>I'm a computer science graduate passionate about building software that makes a difference. I love working with modern technologies and am always eager to learn something new.</p>
    </section>

What does this code do?

This adds an About Me section to your portfolio page. The id="about" attribute gives the section a unique identifier, which is useful for navigation links later.

  • Save index.html.
  • Stage and commit your changes by running these commands:
git add index.html
git commit -m "Add about section"
  • Switch back to main and merge your feature branch by running:
git checkout main
git merge feature/about-section

What just happened?

Git took all the commits from feature/about-section and applied them to main. Since nobody else changed main while you were working, Git performed a "fast-forward" merge. This means it simply moved main's pointer forward to match your feature branch.

  • Delete the merged branch to keep things tidy by running:
git branch -d feature/about-section
  • Confirm the merge by running:
git log --oneline

You should see your "Add about section" commit in the log on main.

Don't see the commit on main?

Make sure you ran git checkout main before the merge. If you merged while still on the feature branch, switch to main and try the merge again.

Help me figure out why my merge didn't work.

✔️ Awesome, I've got everything!

Great. Double check you've saved index.html and that your About section is visible in the file.

ⓧ I'd like to double check the full code

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Portfolio</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <header>
        <h1>Your Name</h1>
        <p>Software Developer</p>
    </header>

    <section id="about">
        <h2>About Me</h2>
        <p>I'm a computer science graduate passionate about building software that makes a difference. I love working with modern technologies and am always eager to learn something new.</p>
    </section>

    <footer>
        <p>Built with Git &amp; GitHub Actions</p>
    </footer>
</body>
</html>
Simulate and resolve a merge conflict

Merge conflicts happen when two branches modify the same line in a file. Git can't decide which version to keep, so it asks you to resolve the conflict manually. This is a normal part of working on a team.

  • Create the first feature branch and modify the <h1> element by running:
git checkout -b feature/header-v1
  • In index.html, find this line:
        <h1>Your Name</h1>
  • Replace it with:
        <h1>Your Name - Portfolio</h1>
  • Save index.html, then stage and commit:
git add index.html
git commit -m "Update header v1"
  • Switch back to main to create the second branch:
git checkout main
  • Create the second branch that will also modify the <h1>:
git checkout -b feature/header-v2
  • In index.html, find this line (still Your Name on main):
        <h1>Your Name</h1>
  • Replace it with a different version:
        <h1>Your Name | Developer</h1>
  • Save index.html, then stage and commit:
git add index.html
git commit -m "Update header v2"

Why does this create a conflict?

Both branches started from the same version of main (where the <h1> says Your Name). Each branch changed the same line to something different. When you merge the first branch, Git accepts it without question. But the second merge finds that the line has already been changed, so Git doesn't know which version you want.

  • Switch back to main and merge the first branch (this will succeed cleanly):
git checkout main
git merge feature/header-v1

You should see a fast-forward merge message. Main now has the Your Name - Portfolio header.

  • Now attempt to merge the second branch (this will cause a conflict):
git merge feature/header-v2

Git reports a conflict. It can't automatically merge because both branches changed the same line.

  • Open index.html in your editor. You'll see conflict markers that look like this:
<<<<<<< HEAD
        <h1>Your Name - Portfolio</h1>
=======
        <h1>Your Name | Developer</h1>
>>>>>>> feature/header-v2

What do these markers mean?

The section between <<<<<<< HEAD and ======= shows what's currently on your branch (main). The section between ======= and >>>>>>> feature/header-v2 shows what the incoming branch wants to change it to.

Your job is to delete the markers and decide what the final content should be. You can keep one version, combine them, or write something entirely new.

  • Resolve the conflict by deleting all the conflict markers and replacing the entire block with the clean original:
        <h1>Your Name</h1>

After experimenting with both versions, you decide the clean placeholder works best for now. This demonstrates that you have full control during conflict resolution.

  • Save index.html.
  • Mark the conflict as resolved and complete the merge by running:
git add index.html
git commit -m "Merge header-v2, resolve conflict"

Why git add before committing?

Running git add on a conflicted file tells Git that you've resolved the conflict. Without it, Git still considers the file unresolved and won't let you commit.

  • Clean up both feature branches by running:
git branch -d feature/header-v1
git branch -d feature/header-v2
  • Confirm the merge history by running:
git log --oneline

You should see both merge commits in the log. The <h1> in your file is back to Your Name.

Still seeing conflict markers in your file?

If git commit complains about unresolved conflicts, open index.html and make sure you deleted all lines containing <<<<<<<, =======, and >>>>>>>. Only the resolved content should remain.

Help me fix my merge conflict resolution.

Practice stashing to save work-in-progress

Sometimes you're halfway through a feature when something urgent comes up. Stashing lets you save your uncommitted changes, switch to a clean state, and come back to your work later.

  • Start adding a projects section to index.html. Find the closing </section> tag of the About section and add this below it (before <footer>):
    <section id="projects">
        <h2>Projects</h2>
        <p>Coming soon...</p>
    </section>
  • Save index.html. Do NOT commit yet. This simulates being mid-task when you need to switch contexts.
  • Stash your uncommitted changes by running:
git stash push -m "wip: projects section"

What does git stash do?

Stash takes your uncommitted modifications and stores them on a temporary stack. Your working directory reverts to the last commit, giving you a clean slate to switch branches or handle something else.

The -m flag adds a message so you can remember what the stash contains later.

  • Verify your working directory is clean by running:
git status

You should see "nothing to commit, working tree clean". Your projects section changes are safely stored in the stash.

  • Simulate switching context by creating and moving to a temporary branch:
git checkout -b feature/temp
  • Switch back to main (your stash is waiting there):
git checkout main
  • View your stash list to see what's saved:
git stash list

You should see stash@{0}: On main: wip: projects section in the output.

  • Restore your stashed changes by running:
git stash pop

What is the difference between pop and apply?

Both git stash pop and git stash apply restore your changes. The difference is that pop also removes the stash entry from the stack. Use apply if you want to keep the stash around as a backup.

  • Confirm your projects section is back by opening index.html. You should see the <section id="projects"> block you added earlier.

The stash exercise is complete. Since the projects section will be built properly in a later step, discard these placeholder changes for now.

  • Discard the uncommitted changes by running:
git restore index.html
  • Delete the temporary branch by running:
git branch -d feature/temp

Seeing 'error: branch not fully merged'?

If Git refuses to delete feature/temp, it means the branch has commits that aren't on main. Since this was just a temporary context-switch demo with no real commits, you can force-delete it with git branch -D feature/temp (capital D).

Help me understand the difference between -d and -D for branch deletion.

You've now mastered the core collaboration workflows: branching, merging, conflict resolution, and stashing. Next up, you'll learn to rewrite history with interactive rebase, amend commits, and recover from mistakes with reflog.

Rewrite History with Rebase, Amend, and Cherry-Pick

Your repository now has a clean merge history. In real projects, though, your commits are rarely clean on the first try.

Messy commit messages, accidental typos, and too-granular commits clutter your history. Git gives you powerful tools to rewrite history before sharing your work. In this step, you will use interactive rebase to squash and reword commits, amend a recent message, cherry-pick a commit across branches, and recover from a simulated disaster with reflog.

In this step, get ready to:
  • Rewrite your commit history using interactive rebase.
  • Fix a commit message with amend and cherry-pick a commit from another branch.
  • Recover lost commits using reflog.
Clean up your commit history with interactive rebase

When you work on a feature, you often make lots of small, messy commits as you figure things out. Interactive rebase lets you combine, rename, and reorder those commits before pushing them to your team.

  • Create a new feature branch by running this command:
git checkout -b feature/projects

You are now on the feature/projects branch. You will build a projects section for your portfolio in several small commits, deliberately using poor commit messages.

  • Open index.html in the editor. Find the closing </section> tag of the about section (the line after the about paragraph).
  • Add the following code between the about section and the footer:
    <section id="projects">
        <h2>Projects</h2>
    </section>
  • Save index.html and commit with a basic message by running these commands:
git add index.html
git commit -m "Add projects section"
  • Now add the first project card inside the <section id="projects"> element, below the <h2>Projects</h2> line:
        <div class="project-card">
            <h3>Project One</h3>
            <p>A brief description of your first project and the technologies used.</p>
        </div>
  • Save and commit with a deliberately bad message:
git add index.html
git commit -m "stuff"
  • Add the second project card directly below the first </div> closing tag:
        <div class="project-card">
            <h3>Project Two</h3>
            <p>A brief description of your second project and the technologies used.</p>
        </div>
  • Save and commit with another poor message:
git add index.html
git commit -m "more changes"
  • Make one more trivial commit. Add a blank line between the two project card </div> and <div tags if one is missing, or simply re-save the file and commit:
git add index.html
git commit -m "fix"
  • View your messy commit history by running:
git log --oneline

You should see four commits with messages like Add projects section, stuff, more changes, and fix. Nobody wants to read these in a shared repository.

What is interactive rebase?

Interactive rebase lets you rewrite history by choosing what to do with each commit. You can squash (combine) multiple commits into one, reword (rename) commit messages, reorder commits, or drop them entirely.

The command git rebase -i HEAD~4 opens your last 4 commits in an editor. Each line starts with the word pick followed by the commit hash and message. You change pick to another command like squash or reword to tell Git what to do.

  • Start the interactive rebase by running:
git rebase -i HEAD~4

What happens when you run this command?

An editor opens with your 4 commits listed from oldest (top) to newest (bottom). Each line looks like:

pick abc1234 Add projects section
pick def5678 stuff
pick ghi9012 more changes
pick jkl3456 fix

The hash values will be different on your machine. The important part is the command word at the start of each line and the commit message at the end.

  • Change the first line from pick to reword (this lets you rename that commit message).
  • Change the third line (more changes) from pick to squash (this combines it with the commit above it, stuff).
  • Leave the second and fourth lines as pick.

Your editor should now look like this:

reword abc1234 Add projects section
pick def5678 stuff
squash ghi9012 more changes
pick jkl3456 fix

Editing in your terminal editor

If Vim opens (you see colored text and cannot type normally), press i to enter insert mode. Make your changes, then press Esc and type :wq then Enter to save and exit.

If you prefer a graphical editor, you can configure Git to use your Antigravity IDE. Run git config --global core.editor "code --wait" before retrying the rebase. The --wait flag tells Git to pause until you close the editor tab.

  • Save and close the editor. Git will immediately open a new editor window for the reword step.
  • Change the commit message to Add projects section structure and save.
  • Git then opens another editor for the squash. Replace all the text with Add project cards and save.
  • Verify the cleaned-up history by running:
git log --oneline

You should now see 3 commits instead of 4. The messages are cleaner: something like Add projects section structure, Add project cards, and fix.

Rebase went wrong?

If you see conflict markers or something unexpected, abort the rebase and start fresh by running git rebase --abort. This restores your branch to its state before the rebase. Then try again from the git rebase -i HEAD~4 command.

Help me fix my interactive rebase

Fix a commit message and cherry-pick from another branch

The fix commit at the top of your log is still vague. You will fix it with git commit --amend, which rewrites only the most recent commit.

  • Amend the most recent commit message by running:
git commit --amend -m "Fix formatting in projects section"

What does amend do?

The --amend flag replaces your most recent commit with a new one. It changes the commit's SHA hash, so only use it on commits you have not pushed yet. Here you are using -m to set the message directly without opening an editor.

  • Verify the message changed by running:
git log --oneline -3

Your latest commit should now say Fix formatting in projects section instead of fix.

Next, you will practice cherry-pick. This command grabs a specific commit from one branch and applies it to another. It is useful when you need a bug fix from a feature branch on main immediately, without merging the entire feature.

  • Note the commit hash of your Add project cards commit from the log output. Copy the short hash (the 7-character code at the start of the line).
  • Switch to main by running:
git checkout main
  • Cherry-pick that commit onto main by running (replace the hash with your actual commit hash):
git cherry-pick [[CHERRY_PICK_HASH="your commit hash from git log, e.g. a1b2c3d"]]

What does cherry-pick do?

The git cherry-pick command takes the changes introduced by a specific commit and applies them as a brand new commit on your current branch. The original commit stays on its source branch untouched.

This is different from merging. A merge brings ALL commits from a branch. Cherry-pick lets you grab just one.

  • Verify the cherry-pick worked by running:
git log --oneline -3

You should see your cherry-picked commit at the top of main's history.

  • Switch back to your feature branch by running:
git checkout feature/projects

Cherry-pick shows a conflict?

If Git reports a conflict during cherry-pick, it means the same lines were changed differently on both branches. Open the conflicting file, resolve it (remove the conflict markers), run git add ., then run git cherry-pick --continue.

Help me resolve my cherry-pick conflict

Recover lost commits with reflog

What if you accidentally delete your work? Most Git "disasters" are recoverable because Git keeps a hidden log of every place your HEAD has been. This log is called the reflog.

  • Simulate losing your last 3 commits by running:
git reset --hard HEAD~3

Seeing this page? Don't freak out

This is intentional. You just told Git to move your branch pointer back 3 commits and discard all changes after that point. Your index.html projects section is gone. The commits look deleted.

  • Check that your commits appear gone by running:
git log --oneline

Your projects section commits are missing from the log. But they are not truly gone.

  • Use reflog to find the lost commits by running:
git reflog

What is reflog?

The reflog records every time HEAD moves. Even after a hard reset, the old commits still exist in Git's object database. The reflog shows you their hashes so you can jump back.

Look for the entry that says something like commit (amend): Fix formatting in projects section or rebase (finish). The hash on that line is where your branch was before the reset.

  • Recover your lost commits by resetting to the hash you found (replace the hash with the one from your reflog):
git reset --hard [[REFLOG_HASH="the commit hash from your reflog output"]]
  • Verify everything is back by running:
git log --oneline

Your 3 commits should be back. The projects section is restored in index.html.

Can't find the right hash in reflog?

The reflog can look overwhelming at first. Look for the line with the commit message you recognize. The hash is the short code at the very start of that line (before HEAD@{N}). Copy it carefully.

Help me find the right hash in my reflog

  • Push your cleaned-up feature branch to GitHub by running:
git push -u origin feature/projects
  • Finally, merge the feature branch into main by running:
git checkout main
git merge feature/projects

Your main branch now has the complete projects section.

✔️ Awesome, I've got everything!

Great. Double check you have saved index.html and that your branch is merged to main.

ⓧ I'd like to double check the full code

Here is what your index.html should look like at the end of this step:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Portfolio</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <header>
        <h1>Your Name</h1>
        <p>Software Developer</p>
    </header>

    <section id="about">
        <h2>About Me</h2>
        <p>I'm a computer science graduate passionate about building software that makes a difference. I love working with modern technologies and am always eager to learn something new.</p>
    </section>

    <section id="projects">
        <h2>Projects</h2>
        <div class="project-card">
            <h3>Project One</h3>
            <p>A brief description of your first project and the technologies used.</p>
        </div>
        <div class="project-card">
            <h3>Project Two</h3>
            <p>A brief description of your second project and the technologies used.</p>
        </div>
    </section>

    <footer>
        <p>Built with Git &amp; GitHub Actions</p>
    </footer>
</body>
</html>

You just went from messy commits to a clean, professional history. Next up, you will set up a GitHub Actions pipeline that automatically deploys your portfolio site to the web every time you push to main.

Set Up GitHub Actions CI/CD and Deploy to GitHub Pages

In the last step, you rewrote commit history, cherry-picked individual commits, and recovered from a simulated disaster using reflog. Your local repository is now in great shape.

Now it's time to automate deployment so your portfolio goes live on the internet every time you push to main. You'll use GitHub Actions to build a CI/CD pipeline that deploys your site to GitHub Pages automatically.

In this step, get ready to:
  • Enable GitHub Pages in your repository settings.
  • Create a GitHub Actions workflow file that deploys your site.
  • Push the workflow and see your live site at a public URL.
Enable GitHub Pages in your repository settings

Before GitHub can deploy your site, you need to tell it where the site content comes from. You'll configure GitHub Pages to use a GitHub Actions workflow as its publishing source.

  • In your browser, navigate to your portfolio-site repository on GitHub.
  • Click Settings in the top navigation bar of your repository.
  • In the left sidebar under "Code and automation", click Pages.
  • Under "Build and deployment", find the Source dropdown.
  • Select GitHub Actions from the dropdown.

What is GitHub Pages?

GitHub Pages is a free hosting service that serves static websites directly from a GitHub repository. Your site will be accessible at a public URL based on your username and repository name.

By selecting "GitHub Actions" as the source, you're telling GitHub to deploy your site using a workflow file you define. This gives you full control over the deployment process.

Why GitHub Actions instead of deploying from a branch?

GitHub offers two ways to publish Pages: from a branch (static files served from a specific branch) or from a GitHub Actions workflow. The Actions approach lets you add build steps, run tests, or transform files before deployment.

Even though your site is simple HTML today, using Actions gives you a professional CI/CD pipeline that scales as your project grows.

Don't see the Settings tab?

If you can't see Settings in the repository navigation, click the overflow menu (the three dots) and select Settings from the dropdown.

Create the GitHub Actions workflow file

GitHub Actions workflows are defined in YAML files stored in a special directory in your repository. You'll create a workflow that automatically deploys your site every time you push to main.

  • Switch back to your Antigravity IDE terminal.
  • Make sure you're on the main branch by running:
git checkout main
  • Create the required directory structure for GitHub Actions workflows by running:
mkdir -p .github/workflows

What does this directory do?

GitHub Actions looks specifically in the .github/workflows directory for workflow definition files. Any .yml or .yaml file in this folder is treated as a workflow.

  • Create a new file called deploy.yml inside the .github/workflows directory.
  • Paste the following workflow content into deploy.yml:
name: Deploy to GitHub Pages

on:
  push:
    branches:
      - main

permissions:
  contents: read
  pages: write
  id-token: write

concurrency:
  group: "pages"
  cancel-in-progress: false

jobs:
  deploy:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Pages
        uses: actions/configure-pages@v5

      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: '.'

      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4

What does this workflow do?

  • on: push: branches: - main triggers the workflow every time you push to the main branch.
  • permissions grants the workflow the ability to read your code, write to Pages, and use an identity token for deployment.
  • concurrency ensures only one deployment runs at a time, preventing conflicts.
  • actions/checkout@v4 pulls your repository code into the workflow runner.
  • actions/configure-pages@v5 prepares the Pages deployment metadata.
  • actions/upload-pages-artifact@v3 packages your entire repo (path: '.') as a deployable artifact.
  • actions/deploy-pages@v4 deploys that artifact to your live GitHub Pages site.
  • Save deploy.yml.
  • Confirm the file exists by running:
ls .github/workflows/

You should see deploy.yml in the output.

Don't see the file?

Make sure you created the directory with mkdir -p .github/workflows (note the dot before github). The .github directory is a hidden folder. If you accidentally created github without the dot, delete it and recreate it with the dot prefix.

help me troubleshoot why my workflow file isn't showing up

✔️ Awesome, I've got everything!

Great. Make sure you've saved deploy.yml before moving on.

ⓧ I'd like to double check the full code

Here's the complete deploy.yml file for comparison:

name: Deploy to GitHub Pages

on:
  push:
    branches:
      - main

permissions:
  contents: read
  pages: write
  id-token: write

concurrency:
  group: "pages"
  cancel-in-progress: false

jobs:
  deploy:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Pages
        uses: actions/configure-pages@v5

      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: '.'

      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4
Commit and push the workflow to trigger deployment

Now that your workflow file is ready, pushing it to main will automatically trigger the deployment. GitHub Actions picks up the workflow and runs it immediately.

  • Stage and commit the new workflow file by running:
git add .github/workflows/deploy.yml
git commit -m "Add GitHub Pages deployment workflow"
  • Push the commit to GitHub by running:
git push origin main

What happens when you push?

The moment your push reaches GitHub, the Actions runner detects the new workflow file. Because the workflow is triggered on: push to main, it starts running immediately. The runner checks out your code, packages it, and deploys it to GitHub Pages.

  • In your browser, go to your portfolio-site repository on GitHub.
  • Click the Actions tab at the top of your repository.
  • You should see a workflow run in progress or completed called "Deploy to GitHub Pages".
  • Click on the workflow run to see its details and confirm all steps passed with green checkmarks.

What should I see?

The Actions tab shows a list of workflow runs. Your "Deploy to GitHub Pages" run should appear with a green checkmark once it completes successfully. This typically takes 30 to 60 seconds.

  • Once the workflow completes, visit your live site. Your URL follows this pattern:
https://[[GITHUB_USERNAME="your GitHub username"]].github.io/portfolio-site

You should see your portfolio page with your header, about section, and projects section displayed.

Changes can take up to 10 minutes

GitHub's documentation notes that changes may take up to 10 minutes to publish after pushing. If you don't see your site immediately after the workflow completes, wait a few minutes and refresh.

Workflow failed or site not loading?

If the workflow failed, click on the failed run in the Actions tab to see the error logs. Common issues include:

  • The workflow file has a YAML indentation error. YAML is strict about spaces. Make sure you used spaces (not tabs) and the indentation matches the file exactly.
  • GitHub Pages is not enabled. Go back to Settings > Pages and confirm the source is set to GitHub Actions.
  • The file is in the wrong location. It must be at .github/workflows/deploy.yml (not github/workflows/deploy.yml).

help me debug why my GitHub Actions deployment workflow is failing

Your portfolio is now live on the internet with automated deployments. Every future push to main will automatically redeploy your site. Next up, you'll learn how to protect your main branch with rulesets and use Pull Requests for a professional collaboration workflow.

Open Pull Requests, Add Rulesets, and Create a Release

Your CI/CD pipeline is live and your portfolio site deploys automatically on every push to main.

On a real team, you would never push directly to main without safeguards. In this step, you will protect your main branch with a ruleset, use a Pull Request to propose and merge a new feature, and tag your first official release.

In this step, get ready to:
  • Create a branch ruleset that prevents accidental deletions and force pushes to main.
  • Open and merge a Pull Request using the squash-and-merge strategy.
  • Tag and publish your first GitHub Release.
Create a branch ruleset to protect main

A ruleset is a set of rules that controls how people interact with specific branches. You will create one that prevents anyone from deleting the main branch or force-pushing over its history.

  • Switch to your browser where you are signed into GitHub.
  • Navigate to your portfolio-site repository page.
  • Click Settings in the top navigation bar of your repository.
  • In the left sidebar, under "Code and automation," click Rules.
  • Click Rulesets.
  • Click New ruleset, then select New branch ruleset.

You are now on the ruleset creation page.

  • In the Ruleset Name field, enter Protect main.
  • Set Enforcement status to Active.
  • Under Target branches, click Add target and select Include default branch.

Now scroll down to the rules section.

  • Confirm that Restrict deletions is checked (it is selected by default).
  • Check the box next to Block force pushes (this is also enabled by default, but verify it is checked).
  • Click Create at the bottom of the page.

What do these rules do?

Restrict deletions prevents anyone from deleting the main branch entirely. Block force pushes prevents anyone from rewriting the commit history on main, which could erase other people's work.

Together, these two rules are the minimum safety net for any shared branch.

Why rulesets instead of branch protection rules?

GitHub now recommends rulesets as the modern alternative to branch protection rules. Rulesets support rule layering (multiple rulesets can apply simultaneously), have active/inactive statuses, and are viewable by anyone with read access. They are available on the GitHub Free plan for public repositories.

  • Verify that you see your new Protect main ruleset listed on the Rulesets page with status Active.

Don't see your ruleset listed?

Make sure you clicked Create at the bottom of the page. If you navigated away before saving, the ruleset was not created.

help me create a branch ruleset on GitHub

Open a Pull Request with the contact section

Instead of pushing directly to main, you will create a feature branch, add a contact section, and propose the change through a Pull Request. This is the standard workflow on professional teams.

  • Switch back to your Antigravity IDE terminal.
  • Create and switch to a new feature branch by running this command:
git checkout -b feature/contact
  • Open index.html in your editor.
  • Find the closing </section> tag of the projects section (the one right before <footer>).
  • Add the following contact section between the closing projects </section> and the <footer> tag:
    <section id="contact">
        <h2>Contact</h2>
        <p>Feel free to reach out!</p>
        <ul>
            <li>Email: your.email@example.com</li>
            <li>GitHub: github.com/yourusername</li>
        </ul>
    </section>

What does this code do?

This adds a Contact section to your portfolio page with placeholder email and GitHub links. The <ul> list uses the same styling from your style.css that removes default bullet points.

  • Save index.html.
  • Stage and commit the change by running these commands:
git add index.html
git commit -m "Add contact section"
  • Push the feature branch to GitHub by running:
git push -u origin feature/contact

You should see output confirming that a new remote branch was created.

  • Switch to your browser and navigate to your portfolio-site repository on GitHub.
  • You should see a yellow banner saying your branch had recent pushes. Click Compare & pull request.
  • In the Pull Request title field, enter Add contact section.
  • In the description box, write a brief summary such as: "Adds a contact section with email and GitHub links."
  • Click Create pull request.

Your Pull Request is now open. You can review the diff by scrolling down to the Files changed tab.

  • Click the Files changed tab to review your code changes.
  • Return to the Conversation tab.
  • Click the Merge pull request dropdown arrow and select Squash and merge.
  • Click Squash and merge, then click Confirm squash and merge.

Why squash and merge?

Squash and merge combines all the commits from your feature branch into a single commit on main. This keeps your main branch history clean and easy to read, even when feature branches contain many small commits.

  • Back in your Antigravity IDE terminal, switch to main and pull the merged changes by running:
git checkout main
git pull

Your local main branch now includes the contact section.

Pull request button not appearing?

Make sure you pushed the feature/contact branch. Run git branch -r to verify the remote branch exists. If you do not see the yellow banner, click the Pull requests tab and then New pull request to create it manually.

help me open a pull request on GitHub

Tag and publish your first release

Git tags mark a specific point in your repository's history. Combined with GitHub Releases, they give you a way to package and announce stable versions of your project.

  • Create an annotated tag for version 1.0.0 by running this command:
git tag -a v1.0.0 -m "First release"

What does this command do?

The -a flag creates an annotated tag, which stores the tagger's name, date, and a message. The -m flag provides the tag message inline. Annotated tags are the recommended type for releases because they contain metadata about who created them and when.

  • Push the tag to GitHub by running:
git push origin v1.0.0

You should see output confirming that a new tag was pushed.

  • Switch to your browser and navigate to your portfolio-site repository on GitHub.
  • On the right sidebar, click Releases (or click the Tags link and then Releases).
  • Click Create a new release.
  • In the Choose a tag dropdown, select v1.0.0.
  • In the Release title field, enter v1.0.0.
  • In the description box, write a brief note such as: "First production release of the portfolio site."
  • Click Publish release.

You should now see your release page with the v1.0.0 tag and your release description.

Tag not showing up in the dropdown?

Make sure you ran git push origin v1.0.0 and not just git push. Regular pushes do not include tags. You must push tags explicitly.

help me push and find my Git tag on GitHub

✔️ Awesome, I've got everything!

Great. Double check you have saved index.html and that your local main branch is up to date with the remote.

ⓧ I'd like to double check the full code

Here is the complete index.html after merging the contact section:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Portfolio</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <header>
        <h1>Your Name</h1>
        <p>Software Developer</p>
    </header>

    <section id="about">
        <h2>About Me</h2>
        <p>I'm a computer science graduate passionate about building software that makes a difference. I love working with modern technologies and am always eager to learn something new.</p>
    </section>

    <section id="projects">
        <h2>Projects</h2>
        <div class="project-card">
            <h3>Project One</h3>
            <p>A brief description of your first project and the technologies used.</p>
        </div>
        <div class="project-card">
            <h3>Project Two</h3>
            <p>A brief description of your second project and the technologies used.</p>
        </div>
    </section>

    <section id="contact">
        <h2>Contact</h2>
        <p>Feel free to reach out!</p>
        <ul>
            <li>Email: your.email@example.com</li>
            <li>GitHub: github.com/yourusername</li>
        </ul>
    </section>

    <footer>
        <p>Built with Git &amp; GitHub Actions</p>
    </footer>
</body>
</html>

Your repository now has professional safeguards in place, a clean merge history from Pull Requests, and a tagged release marking your first stable version. You have experienced the full lifecycle of a feature from branch to production.

Secret mission

Multi-Environment Deployment

What if you could preview changes on a staging environment before they go live to production? In this secret mission, you'll add a multi-environment deployment workflow with a staging branch, PR-triggered previews, branch protection, and automatic tag-based releases.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely on free services. GitHub Actions minutes are unlimited for public repositories, and GitHub Pages hosting is free. There are no ongoing costs to worry about.

Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

Resources you used:

  • Local portfolio-site project folder on your Desktop.
  • GitHub repository portfolio-site (includes GitHub Pages site, Actions workflows, branch rulesets, tags, and releases).

Keep everything running

No action needed. Choose this if you want your portfolio site to stay live and your repository to remain public.

  • Your GitHub Pages site will remain live at its current URL.
  • GitHub Actions will continue to deploy automatically on every push to main.
  • Your branch rulesets will keep protecting main and staging.
  • None of this costs anything for public repositories on the GitHub Free plan.

Pause - I'll come back to this later

Take your site offline without losing any code or history. Your repository stays intact so you can re-enable everything later.

  • In your GitHub repository, click Settings.
  • In the left sidebar, click Pages.
  • Under Build and deployment, change the Source to None to unpublish your site.
  • Your repository, commit history, branches, rulesets, and releases will all remain intact.
  • To bring the site back later, set the Source back to GitHub Actions and push a new commit to main.

Delete - I don't want to use this again

Remove all project resources completely. This cannot be undone.

  • Delete the GitHub repository. In your repository on GitHub, click Settings.
  • Scroll to the bottom of the General page and find the Danger Zone section.
  • Click Delete this repository and follow the confirmation prompts. This removes the repository, Pages site, Actions history, rulesets, releases, and all branches.

Delete the local project folder. In your terminal, run the command for your operating system:

macOS

cd ~/Desktop
rm -rf portfolio-site

Windows

cd ~/Desktop
rmdir /s /q portfolio-site
  • Confirm the folder is gone by running ls (macOS) or dir (Windows) and checking that portfolio-site no longer appears.

Nice Work!

Nice Work!

Nice work! You've gone from basic commits to professional-grade Git and GitHub workflows, with a live portfolio site to prove it.

You've learned how to:

  • Built a GitHub Actions CI/CD pipeline that automatically deploys your portfolio site to GitHub Pages on every push to main.
  • Mastered branching, merging, and conflict resolution, plus rewrote history with interactive rebase, cherry-pick, and recovered lost commits with reflog.
  • Protected your repository with branch rulesets, used Pull Requests with squash-and-merge for clean history, and created tags and releases for versioning.
  • Added a multi-environment deployment workflow with a staging branch, PR-triggered preview deployments, and automatic releases on tag push.

Ready to quiz yourself?