As part of my journey into Cloud Computing and DevOps, I have been spending more time learning and practicing Git and GitHub.
In this practical exercise, I created several files locally using Git Bash, initialized a Git repository, committed the files, connected the local repository to GitHub, and pushed my work to a GitHub repository.
However, the process did not go completely smoothly. I encountered a non-fast-forward / fetch-first error when I tried to push my local repository to GitHub.
This article documents the complete process, including the error, why it happened, and how I resolved it.
What I Intend to Achieve
Objectives:
Create files in a local project folder.
Initialize Git.
Track the files with Git.
Commit the files.
Create a connection between the local repository and GitHub.
Push the local repository to GitHub.
Make additional changes.
Commit and push the changes again.
My working directory was:
C:/Users/HP/Documents/hagital-demo
My GitHub repository was:
https://github.com/Makansgreat/hagital-demo
Step 1: Navigate to the Project Directory
I started from my Git Bash terminal inside my project directory.
pwd
The result showed:
This confirmed that I was working inside the correct project folder.
Step 2: Create the Files
I created several files using the touch command:
touch devop.txt aps.pdf notes.txt name.docx admin.pdf mccons.py secrets.txt

This created the following files:
devop.txt
aps.pdf
notes.txt
name.docx
admin.pdf
mccons.py
secrets.txt
Step 3: Initialize the Git Repository
I ran:
git init
Git responded:
Initialized empty Git repository in C:/Users/HP/Documents/hagital-demo/.git/
Git created a hidden .git directory inside my project folder.
I then checked the status again:
git status
This time Git reported:
On branch master
No commits yet
Untracked files:
admin.pdf
aps.pdf
devop.txt
mccons.py
name.docx
notes.txt
secrets.txt
This meant Git could now see the files, but they were still untracked.
Step 4: Add the Files to the Staging Area
I used:
git add .
The . means "add all changes in the current directory."
I then checked the status:
git status
Git showed:
Changes to be committed:
new file: admin.pdf
new file: aps.pdf
new file: devop.txt
new file: mccons.py
new file: name.docx
new file: notes.txt
new file: secrets.txt
The files had now moved from untracked to staged.
Step 5: Create the First Commit
I committed the staged files with:
git commit -m "added couple of files"
Git returned:
[master (root-commit) 02e2324] added couple of files
7 files changed, 2 insertions(+)
I had now created my first local Git commit.
Step 6: Rename the Branch from master to main
Modern GitHub repositories commonly use main as the default branch.
My local branch was still called master, so I renamed it:
git branch -m main
My terminal prompt now showed:
(main)

This confirmed that I was working on the main branch.
Step 7: Connect the Local Repository to GitHub
I connected my local repository to my GitHub repository using:
git remote add origin https://github.com/Makansgreat/hagital-demo.git
I then verified the remote connection:
git remote -v
Git returned:
origin https://github.com/Makansgreat/hagital-demo.git (fetch)
origin https://github.com/Makansgreat/hagital-demo.git (push)
This confirmed that origin was pointing to my GitHub repository.
Step 8: Attempt to Push to GitHub
I tried to push my local main branch:
git push -u origin main
But Git rejected the push.
I received:
! [rejected] main -> main (fetch first)
error: failed to push some refs to
'https://github.com/Makansgreat/hagital-demo.git'
hint: Updates were rejected because the remote contains work
that I d0 not have locally.
The Problem
At first, this error was confusing.
I had already created my local commit, so why couldn't Git push it?
The reason was that the GitHub repository already contained a commit that my local repository did not have.
In my case, the GitHub repository already had an Initial commit.
Therefore, the histories looked roughly like this:
GitHub:
Initial commit
|
main
While my local repository had:
Local:
added couple of files
|
main
The two repositories had different histories.
Git did not want me to simply overwrite the history that already existed on GitHub.
Step 9: Pull the GitHub Changes Using Rebase
I then used:
git pull origin main --rebase
Git successfully downloaded the remote changes:
From https://github.com/Makansgreat/hagital-demo
- branch main -> FETCH_HEAD
Successfully rebased and updated refs/heads/main.
What did --rebase do?
The --rebase option allowed Git to take my local commit and place it on top of the existing GitHub history.
Conceptually, the history changed from something like:
GitHub:
Initial commit
Local:
added couple of files
to:
Initial commit
|
added couple of files
Now the histories were properly connected.
Step 10: Push Again
After the successful rebase, I ran:
git push -u origin main
This time the push was successful:
To https://github.com/Makansgreat/hagital-demo.git
900f6a7..0ce22f5 main -> main
branch 'main' set up to track 'origin/main'.
The important part was:
branch 'main' set up to track 'origin/main'
This meant my local main branch was now tracking the GitHub main branch.
Step 13: Verify the Repository Status
I ran:
git status
Git responded:
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree clean
This was exactly what I wanted to see.
It meant:
My local branch was main.
My local branch was synchronized with GitHub.
There were no uncommitted changes.
The working tree was clean.
Step 11: Check the Commit History
I also checked the commit history:
git log --oneline --all --decorate -5
The result was:
0ce22f5 (HEAD -> main, origin/main, origin/HEAD) added couple of files
900f6a7 Initial commit
This confirmed that both commits were now part of the repository history.
The first commit was:
900f6a7 Initial commit
and my second commit was:
0ce22f5 added couple of files
Step 12: Make Additional Changes
After successfully pushing the files to GitHub, I made additional changes to some of the files.
Git then showed:
Changes not staged for commit:
modified: devop.txt
modified: mccons.py
modified: name.docx
modified: notes.txt
This meant Git detected that four previously tracked files had been modified.
Step 13: Stage the New Changes
I added the modified files to the staging area:
git add .
Then I checked the status:
git status
Git showed:
Changes to be committed:
modified: devop.txt
modified: mccons.py
modified: name.docx
modified: notes.txt
The changes were now staged and ready to commit.
Step 14: Commit the New Changes
I created another commit:
git commit -m "I added to some files"
Git responded:
[main afa7b13] I added to some files
4 files changed, 5 insertions(+), 2 deletions(-)
This created another commit in my Git history.
Step 15: Push the New Commit to GitHub
Finally, I pushed the new commit:
git push origin main
Git successfully uploaded the changes:
Enumerating objects: 9, done.
Counting objects: 100% (8/8), done.
Delta compression using up to 4 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (5/5), 536 bytes | 12.00 KiB/s, done.
Total 5 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
To https://github.com/Makansgreat/hagital-demo.git
0ce22f5..afa7b13 main -> main
The push was successful.
The Complete Git Workflow
Looking back, the complete workflow I followed was:
1. Create files
touch devop.txt aps.pdf notes.txt name.docx admin.pdf mccons.py secrets.txt
2. Initialize Git
git init
3. Check status
git status
4. Stage files
git add .
5. Commit
git commit -m "added couple of files"
6. Rename branch
git branch -m main
7. Add GitHub remote
git remote add origin https://github.com/Makansgreat/hagital-demo.git
8. Verify remote
git remote -v
9. Push
git push -u origin main
10. Because the remote already had a commit,
pull and rebase
git pull origin main --rebase
11. Push again
git push -u origin main
12. Verify
git status
13. View commit history
git log --oneline --all --decorate -5
14. After making more changes
git add .
15. Commit the changes
git commit -m "I added to some files"
16. Push the changes
git push origin main
What I Learned From This Exercise
This exercise taught me several important Git concepts.
- git init
Initializes a normal folder as a Git repository.
git init
- git status
Shows the current state of the working directory.
git status
It helps identify:
Untracked files
Modified files
Staged changes
Uncommitted changes
- git add
Moves changes into the staging area.
git add .
- git commit
Creates a snapshot of the staged changes.
git commit -m "commit message"
- git remote
Connects the local repository to a remote repository such as GitHub.
git remote add origin
- git push
Uploads local commits to the remote repository.
git push origin main
- git pull
Downloads changes from the remote repository and integrates them into the local repository.
git pull origin main
- git pull --rebase
In my case, this was the command that solved the problem:
git pull origin main --rebase
It allowed me to integrate the existing GitHub history with my local commit history without creating an unnecessary merge commit.
The Important Lesson From My Error
The most important lesson I learned was that a GitHub repository and a local Git repository can have different histories.
When I received:
! [rejected] main -> main (fetch first)
I initially thought there was something wrong with my Git configuration.
There wasn't.
Git was protecting the existing history on GitHub.
The solution was to first retrieve the remote history:
git pull origin main --rebase
and then push my changes:
git push -u origin main
After that, future pushes became much simpler because my local main branch was tracking origin/main.
A Better Workflow for Future Projects
Going forward, when I create a local project and want to push it to GitHub, I need to be aware of whether the GitHub repository is completely empty.
If the GitHub repository already contains files such as a README or an initial commit, I should synchronize the repositories before pushing.
Final Thought
This may look like a small Git exercise, but it was valuable because I didn't just learn how to push files to GitHub—I encountered and resolved an actual Git version-control problem.
As I continue my Cloud and DevOps engineering journey, understanding Git is essential because Git is used extensively for source-code management, collaboration, CI/CD pipelines, infrastructure-as-code projects, and DevOps automation.
One error message such as:
rejected (non-fast-forward)
can initially be frustrating, but understanding why Git rejected the push makes the solution much easier to understand.
My key takeaway is:
Before pushing, make sure your local repository and remote repository are synchronized.
And when the remote has commits that the local repository doesn't have, one possible solution is:
git pull origin main --rebase
git push origin main
This exercise is another step forward in my journey from learning Git to applying Git as part of my Cloud and DevOps workflow.














Top comments (0)