From Theory to Practice: A Git Workshop for Beginners
Developing Git Proficiency Through Practical Examples with Hugo: Hands-On Learning for Effective Version Control.
Master Git from Start to Finish: A 4-Part Series
Welcome to my 4-part series on Git: Master Git from Start to Finish. Whether you're just starting out or ready to take your Git skills to an advanced level, this series has something for everyone! Each part is designed to build your expertise and streamline your workflow:
Mastering Git: Advanced Techniques for Streamlined Development (Part 1)
Mastering Git: Advanced Techniques for Streamlined Development (Part 2)
Introduction
In my previous article, Getting Started with Git I covered the fundamentals of Git, a powerful distributed version control system that enables developers to manage and track changes in their code. In this workshop, we’ll also explore Hugo, a fast and flexible static site generator that simplifies website creation. By integrating Git with Hugo, you’ll learn how to effectively manage your Hugo projects, including creating repositories, making commits, and how you can collaborating with others.
Goals for This Workshop
In this workshop, you will gain a practical understanding of using Git alongside Hugo. You’ll create a Git repository, make commits to track changes, manage branches, clone repositories, view commit history, resolve merge conflicts, and push your project to a remote repository. By the end, you’ll be ready to integrate Git confidently into your own projects.
Setting Up Your Environment
Before we start working with Git and Hugo together, it’s important to have everything set up correctly. Since you already have Git installed (as covered in my previous article), we’ll focus on getting Hugo up and running.
Installing Hugo
Hugo is a fast, open-source static site generator that simplifies creating websites. It transforms your content into a full-fledged website without needing a server-side language or database. Let’s get it installed so we can move forward.
On macOS:
If you’re using macOS, you can install Hugo via Homebrew. Simply open your terminal and run the following command:
brew install hugo
For more information please see the official documentation.
On Linux:
For Linux, you can install Hugo using Snap or download it directly from the Hugo releases page.
Using Snap (Ubuntu and other distros with Snap support):
sudo snap install hugo
The Hugo snap package is strictly confined. Strictly confined snaps run in complete isolation, up to a minimal access level that’s deemed always safe. The sites you create and build must be located within your home directory, or on removable media. — source: https://gohugo.io/installation/linux/
For more information please see the official documentation.
Manual Installation (other distros):
Go to the Hugo releases page and download the latest version.
Extract the downloaded file and move the Hugo executable to
/usr/local/bin
or a location in your PATH.
On Windows:
Windows users can install Hugo using Chocolatey or Scoop.
Chocolatey is a free and open-source package manager for Windows.
Scoop is a free and open-source package manager for Windows.
With Chocolatey:
choco install hugo-extended
With Scoop:
scoop install hugo-extended
You can confirm the installation by running:
hugo version
This should display the Hugo version you have installed, indicating that the setup was successful.
Creating a New Hugo Site
Now that Hugo is installed, let’s create a new Hugo project that we’ll manage with Git.
Navigate to the directory where you want to create your new Hugo site. For example:
cd ~/projects
Run the following command to create a new Hugo site:
hugo new site <your-site-name>
Replace
<your-site-name>
with your desired project name.
Hugo will generate the necessary directory structure and default files for your site. At this point, your site will have basic folders such as content
, layouts
, and static
that will hold your files and resources.
Hugo default structure:
archetypes: Templates for creating new content types.
assets: Stores raw files like CSS or JavaScript for processing.
content: Main content (posts, pages) of your site.
data: Structured data (YAML, JSON, etc.) used in templates.
hugo.toml: Hugo site configuration file.
i18n: Language translations for multilingual support.
layouts: HTML templates for rendering content.
static: Static files (images, PDFs) served as-is.
themes: Themes to control the look and feel of the site.
Installing a Theme for Your Hugo Site
Hugo doesn’t come with a built-in default theme, so to style and display your content properly, you’ll need to install a theme. Themes provide the layout and design of your site, allowing you to focus on content creation without worrying about the presentation.
To install a theme:
Choose a theme from Hugo Themes that suits your project. A popular choice for beginners is the Ananke theme.
Install the theme by adding it as a Git submodule in your
themes
directory:
git submodule add https://github.com/theNewDynamic/gohugo-theme-ananke.git themes/ananke
Configure Hugo to use the theme by editing the hugo.toml
configuration file. Add the following line:
theme = "ananke"
Now, your site will be styled according to the theme.
Hugo’s flexibility allows you to change your site’s theme without altering your content. This separation between content and presentation is what makes Hugo a powerful static site generator. By selecting the right theme and customizing it, you can quickly create a professional-looking website while focusing on the content itself.
Initializing a Git Repository
Once your Hugo site is set up, it’s time to bring Git into the mix. We’ll create a Git repository to start version-controlling the files in your Hugo project.
Navigate into your Hugo project folder:
cd <your-site-name>
Initialize a Git repository in this directory by running:
git init
This creates an empty Git repository that tracks the changes in your Hugo project. You now have the environment set up to start working with Git and Hugo together!
Making Your First Commit
Now that you have initialized a Git repository in your Hugo project, it’s time to make your first commit. Committing is the process of saving your changes to the local repository, allowing you to track the history of your project.
Creating a Simple Content File
To get started, let’s create a new content file for your Hugo site. This file will serve as a blog post or page on your website.
Use the following command to create a new post:
hugo new posts/my-first-post.md
This command generates a new Markdown file located in the content/posts
directory.
Open the newly created file in your preferred text editor:
nano content/posts/my-first-post.md
# or use any other editor like vim, etc.
You will see some pre-filled front matter in the file, which looks like this:
Front matter refers to metadata that is included at the beginning of a content file. This metadata is enclosed within a pair of triple-plus signs (
+++
) for TOML format, or triple-dashed lines (---
) for YAML format, and typically contains information that Hugo uses to generate the website.
+++
title = 'My First Post'
date = 2033-10-10T05:16:22+00:00
draft = true
+++
You can modify the title
and date
as needed. For now, you can add some content below the front matter. For example:
# Welcome to My First Post!
This is my very first post using Hugo and Git. Exciting times ahead!
Save and close the file. (In nano
, you can do this by pressing CTRL + O
, then Enter
, and then CTRL + X
to exit.)
Staging Your Changes
Before you can commit the new file, you need to stage it. Staging prepares your changes for the commit, allowing you to specify which files you want to include.
Stage all changes in your project directory by running:
git add .
This command adds all new and modified files to the staging area. You can also stage individual files if preferred, like so:
git add content/posts/my-first-post.md
Note on Git and Empty Folders
Git does not track empty folders by default. If you want to include an empty folder in your repository, you need to add a file to that folder. A common practice is to create a placeholder file, such as.gitkeep
, which serves no functional purpose but allows Git to recognize the directory.
Making Your First Commit
Now that your changes are staged, it’s time to create your first commit.
Run the following command to commit your changes:
git commit -m "Add first post: My First Post"
The -m
option allows you to provide a commit message inline. It’s essential to write clear and descriptive commit messages to understand your project's history.
Viewing Your Changes with Hugo
After making changes and committing them to Git, you can see how they affect your Hugo site.
Start the Hugo server to view the changes locally:
hugo server -D
When you start a Hugo server using the command
hugo server -D
, the-D
flag stands for Draft. This option allows Hugo to include content that is marked as drafts in your site.By default, Hugo only generates and serves content that is published (i.e., where
draft: false
in the front matter). Using the-D
flag enables you to preview and test draft content before it goes live, making it easier to work on posts or pages that are still in progress.
Open your web browser and go to http://localhost:1313
to see the live version of your site with the recent changes reflected. Hugo automatically refreshes the page whenever you make updates to the content or files, so you can see the changes in real-time!
Viewing Your Commit History
To confirm that your commit was successful and view the commit history, you can use:
git log
This command displays a list of commits made in the repository, showing details like the commit hash, author, date, and commit message.
Summary
Congrats, you have successfully created your first content file in your Hugo project, staged your changes, and committed them to your Git repository. This process is fundamental to working with Git, as it allows you to track changes and maintain a history of your project's development.
Ignoring Unnecessary Files with .gitignore
Before we continue with our Git/Hugo journey, it's important to recognize that not all files need to be tracked by Git. Certain files, such as logs, build artifacts, and environment configurations, are better kept out of version control to maintain a clean repository. This is where the .gitignore
file becomes essential..
What is a .gitignore
file?
The .gitignore
file is a special file that tells Git which files or directories it should ignore when tracking changes. This helps keep your repository clean and prevents unnecessary or sensitive files from being committed.
Creating a .gitignore
File
Inside the root of your project directory, create a file named .gitignore
:
touch .gitignore
In this file, you can specify file patterns that Git should ignore. For example:
# Ignore log files
*.log
# Ignore Hugo build directory
public/
# Ignore temporary editor files
*.swp
This tells Git to ignore all .log
files, the Hugo public/
directory (which is where Hugo generates your site), and temporary editor files with the .swp
extension.
Common .gitignore
Patterns
Here are some common patterns to include in a .gitignore
file:
Compiled files:
.o
,.exe
,.class
Operating system files:
.DS_Store
(macOS),Thumbs.db
(Windows)Editor/IDE settings:
.vscode/
,.idea/
Hugo directories:
public/
(build output)Environment files:
.env
(to keep sensitive API keys and secrets out of version control)
Viewing and Modifying .gitignore
Once you’ve created your .gitignore
, you can always modify it as your project evolves. Keep in mind that .gitignore
only applies to untracked files. If you've already committed a file, Git will continue to track it even if you later add it to .gitignore
. To stop tracking a file that’s already been committed, you need to manually remove it from the repository:
git rm --cached <file>
Managing Changes and Viewing History
Now that you've made your first commit, let’s explore how to manage changes and keep track of your project’s evolution. Git allows you to easily edit files, stage those changes, commit them, and view the history of everything you've done.
Modifying Existing Files
Let’s make a change to the post you created earlier. Suppose you want to update the content of the my-first-post.md
file.
Open the file again for editing:
nano content/posts/my-first-post.md
Add or modify some text. For example:
# Welcome to My First Post!
This is my very first post using Hugo and Git. Exciting times ahead!
## Why I Chose Hugo
Hugo is fast, flexible, and allows me to focus on writing rather than managing complex tools. It's perfect for static sites!
Save and close the file as before.
Checking the Status of Your Changes
Before committing these changes, it's a good habit to check the status of your Git repository. This tells you which files have been modified or added and whether they are staged for commit.
Run the following command:
git status
You should see output similar to this:
On branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: content/posts/my-first-post.md
This output shows that the file my-first-post.md
has been modified but is not yet staged for the next commit.
Staging and Committing Changes
Just like with the initial commit, you need to stage the changes before committing them.
Stage the modified file:
git add content/posts/my-first-post.md
Now, commit the changes with an appropriate message:
git commit -m "Update content of the first post"
This commit will now be saved in the repository, tracking the changes you just made.
Viewing the Commit History
Git provides a powerful way to look at the history of your changes through the git log
command. This helps you keep track of every commit made in your project.
Run the following command to view the commit history:
git log
The output will look something like this:
commit 12f3d4b8d9a2c1b7e7e7df31ac72e91e7f125cc3 (HEAD -> main)
Author: Your Name <you@example.com>
Date: Wed Oct 16 12:34:56 2024 +0000
Update content of the first post
commit 2e1d6b7eaf1f4c1234d891ce43ef2468c3e528b1
Author: Your Name <you@example.com>
Date: Wed Oct 16 12:00:00 2024 +0000
Add first post: My First Post
Each commit entry shows the commit hash, author, date, and the commit message. You can see exactly when changes were made and what was committed.
Viewing Specific Commit Details
If you want to see the specific changes made in a particular commit, you can use the git show
command followed by the commit hash. For example:
git show 12f3d4b8d9a2c1b7e7e7df31ac72e91e7f125cc3
This will display the changes made in that commit. Alternatively, you can view a summary of differences between commits using:
git diff <commit-hash-1> <commit-hash-2>
This is useful for comparing the changes between two points in your project.
Viewing Your Changes with Hugo
After making changes and committing them to Git, you can see how they affect your Hugo site.
Start the Hugo server to view the changes locally:
Hugo server -D
Open your web browser and go to http://localhost:1313
to see the live version of your site with the recent changes reflected. Hugo automatically refreshes the page whenever you make updates to the content or files, so you can see the changes in real-time!
Summary
In this section, you’ve learned how to modify existing files, stage those changes, commit them to your repository, and view your commit history. Understanding how to manage changes and track your project's history with Git is crucial for effective version control.
Creating and Managing Branches
In Git, branches are used to work on different features or versions of your project without affecting the main branch. For example, you might create a branch to develop a new feature, test it, and then merge it back into the main branch once it’s ready.
Creating a New Branch
Let’s say you want to add a new feature to your Hugo site. You can create a new branch for this feature so that it doesn’t interfere with the current version of the site.
To create a new branch, run:
git branch feature/new-section
This creates a branch named feature/new-section
. It’s common to use descriptive branch names that reflect the purpose of the branch (in this case, we’re creating a new section for the site).
To switch to the new branch, use:
git checkout feature/new-section
Now, any changes you make will be on this new branch, separate from the main
branch.
Making Changes on the New Branch
Let’s add a new page or section to the Hugo site while on the feature/new-section
branch.
Create a new page using Hugo:
hugo new posts/new-section.md
Open and edit the file to add some content:
nano content/posts/new-section.md
Add the following content:
---
title: "New Section"
date: 2024-10-16T12:45:00Z
draft: false
---
# Welcome to the New Section!
This is a new section added as part of our feature branch.
Save and close the file.
Committing Changes to the Branch
Once you’ve made your changes, you’ll want to commit them to the feature/new-section
branch.
Stage the new file:
git add content/posts/new-section.md
Commit the changes:
git commit -m "Add new section to the site"
You now have committed changes on your feature/new-section
branch, separate from the main
branch.
Switching Back to the Main Branch
You can switch between branches whenever you want. For example, let’s say you want to go back to the main
branch.
To switch back to the main branch:
git checkout main
If you run git log
, you will see that the main
branch still reflects the commits made before the new section was added. The changes you made on feature/new-section
are isolated in the feature/new-section
branch and did not affect the main branch.
Merging Changes from the Feature Branch
Once you’re happy with the changes in the feature/new-section
branch, you can merge them into the main
branch.
First, make sure you’re on the main
branch:
git checkout main
Then, merge the feature branch:
git merge feature/new-section
This command will integrate the changes from the feature/new-section
branch into main
. Now, the main
branch includes the new section!
Deleting the Feature Branch (Optional)
After successfully merging the feature branch into main
, you may want to delete the branch to keep your repository clean.
Delete the feature branch:
git branch -d feature/new-section
This removes the branch from your local repository, though the changes will remain in main
.
Viewing Your Changes with Hugo
After merging the changes and deleted the feature branch, you can run the Hugo server again to see the updated site with the new section.
Start the Hugo server again:
Hugo server -D
Visit http://localhost:1313
in your browser. You should now see the new section added to your site.
Summary
In this section, you’ve learned how to create and switch between branches, make changes on a feature branch, and merge those changes back into the main branch. Branching is a powerful way to manage different parts of your project independently, allowing you to experiment and develop new features without disrupting your main codebase.
Working with Remote Repositories
In this section, you’ll learn how to work with remote Git repositories, which are hosted versions of your local Git repository stored on services like GitHub or GitLab. Remote repositories allow you to back up your work, collaborate with others, and track changes made by multiple contributors.
Setting Up a Remote Repository
Create a Remote Repository:
Log into GitHub (or GitLab, Bitbucket, etc.) and create a new repository.
Give it a name like
hugo-site
, and choose whether it will be public or private.
Link the Remote Repository:
In your local project, you’ll link the remote repository using the
git remote add
command:
git remote add origin https://github.com/your-username/hugo-site.git
Replace the URL with your repository’s actual URL. The origin
keyword is the name of your remote repository, and origin
is commonly used by default.
Pushing Changes to the Remote Repository
Now that you have linked your local repository to a remote repository, you can push your changes so they’re stored remotely.
Push to Remote:
To push your changes to the remote repository, run:
git push origin main
This pushes the commits from your main
branch to the remote origin
.
Authentication:
If this is your first time pushing to GitHub, you may be asked to authenticate. GitHub will prompt you for your credentials, or if you have two-factor authentication (2FA), it will require a token or personal access token.
Pulling Changes from the Remote Repository
If you’re collaborating with others or working on multiple machines, you may need to pull changes from the remote repository.
Fetch and Pull:
To pull down any new changes from the remote repository, use:
git pull origin main
This command will download the latest changes from the main
branch of the remote repository and integrate them into your local main
branch.
Cloning an Existing Remote Repository
If you want to start fresh with a project that’s already hosted remotely, you can clone it to your local machine.
Cloning:
To clone a repository, use:
git clone https://github.com/your-username/hugo-site.git
This will create a local copy of the remote repository on your machine, with all its history, branches, and files.
Collaborating with Others
When working on a team, Git’s branching and merging features help organize contributions. Multiple people can work on different branches, push them to the remote repository, and create pull requests (on GitHub) to merge changes.
Pull Requests: After pushing your branch, you can create a pull request in GitHub to propose merging changes from your feature branch into the
main
branch.Code Reviews: GitHub allows team members to review your code changes before merging them.
Viewing Your Changes with Hugo
As you continue collaborating and pushing changes to the remote repository, you can still view your progress locally:
Run the Hugo server:
Hugo server -D
Navigate to http://localhost:1313 to see how the latest changes are reflected in your Hugo site. If you're working on a feature branch, ensure you're previewing the latest branch before pushing to the remote.
Resolving Merge Conflicts
Merge conflicts occur when Git can’t automatically combine changes from different branches or contributors. This usually happens when two or more people make changes to the same lines of a file. In this section, you’ll learn how to identify and resolve these conflicts.
How to Identify a Merge Conflict
When a conflict happens, Git will notify you of the conflict and mark the problematic file(s) in your project. Git won’t allow the merge to complete until the conflicts are resolved.
Triggering a Conflict:
For example, if you and another contributor have edited the same line in a file on different branches, merging those branches will trigger a conflict.
Check Conflict Status:
Run the following command to check which files have conflicts:
git status
The conflicted files will be listed under the "Unmerged paths" section.
Resolving the Conflict
When Git detects a conflict, it adds markers in the affected file. You’ll need to manually choose which changes to keep.
Open the Conflict File:
Open the file in your text editor or IDE. You’ll see something like this:
<<<<<<< HEAD
Content from the current branch.
=======
Content from the branch being merged.
>>>>>>> branch-to-merge
Choose the Right Changes:
You need to decide which changes to keep:
Keep the current branch’s changes (
HEAD
),Keep the changes from the branch being merged (
branch-to-merge
), orCombine both.
After deciding, delete the conflict markers (<<<<<<<
, =======
, >>>>>>>
) and save the file.
After editing the file, mark it as resolved by staging it:
git add <conflicted-file>
Commit the Merge:
Finally, commit the merge:
git commit
Best Practices for Avoiding Conflicts
While conflicts are inevitable in collaborative projects, you can minimize them with a few practices:
Pull often: Regularly pull changes from the remote repository to keep your local branch up to date.
Communicate: Let team members know what areas of the project you’re working on to avoid overlapping edits.
Work on small, isolated changes: This helps reduce the chances of conflicting with other changes.
Simulating a Merge Conflict in Hugo
In real-world projects, it’s common for multiple people (or even yourself at different times) to work on the same file. This can lead to situations where Git encounters a merge conflict—when it cannot automatically decide how to merge changes from different branches. To help you understand how to resolve such conflicts, we’ll walk through a practical example using our Hugo project.
In this section, we’ll simulate a merge conflict by editing the same Hugo content file in two different branches. By the end, you'll learn how to identify and resolve merge conflicts, making you more confident in managing collaborative coding environments.
Feel free to follow along with the steps below if you want to test out this scenario for yourself.
Step 1: Navigate to Your Hugo Project
Open your terminal and navigate to your Hugo project directory:
cd path/to/your/hugo-project
Step 2: Create a New Branch
Create and switch to a new branch for a feature update:
git checkout -b feature/merge_conflict_test
Step 3: Modify a Content File
Edit one of your Hugo content files (e.g., content/posts/my-first-post.md) by adding a line of text:
## On merge_conflict_test branch
This is a change from the merge_conflict_test branch.
Stage and commit the changes:
git add content/posts/my-first-post.md
git commit -m "Update post in merge_conflict_test branch"
Step 4: Switch Back to the Main Branch
Switch back to the main branch:
git checkout main
Step 5: Modify the Same Content File in the Main Branch
Edit the same content file (content/posts/my-first-post.md) again, but this time in the main branch:
# On main branch
This is a change from the main branch.
Stage and commit the changes:
git add content/posts/my-first-post.md
git commit -m "Update post in main branch"
Step 6: Attempt to Merge the Feature Branch
Now, try to merge the feature branch into the main branch:
git merge feature/merge_conflict_test
At this point, Git will detect a conflict since both branches made changes to the same file, and you’ll receive a message like:
Auto-merging content/posts/my-first-post.md
CONFLICT (content): Merge conflict in content/posts/my-first-post.md
Automatic merge failed; fix conflicts and then commit the result.
Step 7: Resolve the Merge Conflict
Open the conflicting file (content/posts/my-first-post.md). You’ll see Git has inserted conflict markers like this:
<<<<<<< HEAD
## On main branch
This is a change from the main branch.
=======
## On merge_conflict_test branch
This is a change from the merge_conflict_test branch.
>>>>>>> feature/merge_conflict_test
Step 8: Resolve the Conflict
Edit the file to resolve the conflict. You can decide how to merge the changes. In this case, we’ll keep both changes:
# Welcome to My First Post!
This is my very first post using Hugo and Git. Exciting times ahead!
## Why I Chose Hugo
Hugo is fast, flexible, and allows me to focus on writing rather than managing complex tools. It's perfect for static sites!
## Combined Content
This is a change from the main branch.
This is a change from the merge_conflict_test branch.
Save the file.
Step 9: Mark the Conflict as Resolved
Stage the resolved file:
git add content/posts/my-first-post.md
Complete the merge by committing:
git commit -m "Resolved merge conflict between main and merge_conflict_test"
Step 10: Verify
You can now check your Git history to ensure the merge was successful:
git log --oneline --graph
Viewing Changes in Hugo
If you’re curious about the changes made in Hugo, you can run the Hugo development server to preview the combined content:
Hugo server -D
Navigate to the post (http://localhost:1313/
) and verify that the changes from both branches are now visible.
Summary
By following this exercise, you’ve learned how to simulate and resolve a merge conflict in Git within the context of your Hugo project. Merge conflicts are a natural part of collaborative development, and with practice, resolving them will become second nature. Don’t forget to frequently commit and communicate changes to minimize conflicts!
Conclusion: Mastering Git for a Smooth Development Workflow
As we've progressed through this workshop, you've gained practical experience with the essential features of Git, from initializing a repository to managing branches, working with remote repositories, and resolving conflicts. By learning to navigate common tasks like creating commits, branching, and handling merge conflicts, you've built a solid foundation for using Git in your own projects.
Remember, version control is not just about tracking changes—it's about collaboration, maintaining a clean project history, and efficiently managing your development workflow. As you continue to work with Git, you'll discover even more advanced features and techniques to streamline your processes.
With the knowledge you’ve acquired, you’re now equipped to confidently integrate Git into your future projects, ensuring that you and your team can work more effectively, no matter the project’s complexity.
Feeling confident with the basics? Take it up a notch in the next article: Mastering Git: Advanced Techniques for Streamlined Development (Part 1).