Skip to main content

Command Palette

Search for a command to run...

Git & GitHub from a DevOps Perspective: What I Learned Beyond the Commands

Updated
22 min readView as Markdown
Git & GitHub from a DevOps Perspective: What I Learned Beyond the Commands
N

Tech learner with Core Java experience, revising DSA & DBMS while diving into Cloud, DevOps, and modern tools like Docker, Kubernetes & Azure

Introduction

I thought I already knew Git and GitHub.

I had worked with GitHub before, mainly through GitHub Desktop, where many operations were just a few clicks away.

Commit. Push. Pull.

Simple.

But while revisiting Git and GitHub from a DevOps perspective, I decided to work more through the command line.

And that changed my understanding completely.

Because this time, I wasn't just clicking buttons.

I started seeing what was happening underneath:

What is Git tracking? Where are my changes? Which branch am I on? What is happening locally? What is happening on the remote repository? Why is Git rejecting my command?

And then Git started giving me some very practical lessons.

I encountered errors like:

Authentication failed
! [rejected] main -> main (fetch first)
refusing to merge unrelated histories
Please commit or stash your changes

And instead of treating these errors as problems to get rid of, I started treating them as explanations of what Git was trying to tell me.

This blog documents what I learned.


1. Git vs GitHub

Before going into commands, I wanted to understand the difference clearly.

Git

Git is a version control system.

It helps us track changes in our code and maintain its history.

GitHub

GitHub is a platform where Git repositories can be hosted remotely and where teams can collaborate around the code.

A simple way to visualize it:

Developer's Machine
        |
       Git
        |
        ↓
Local Repository
        |
      Push
        ↓
GitHub Repository

Git manages the version history.

GitHub provides the remote collaboration platform.


2. The Git Workflow

One of the most important concepts I learned was that a file does not simply go directly from "edited" to "committed."

There are different stages.

Working Directory
        ↓
     git add
        ↓
Staging Area
        ↓
   git commit
        ↓
Local Repository
        ↓
    git push
        ↓
Remote Repository

Working Directory

This is where I actually modify my files.

For example:

git status

can show:

modified: file.txt

This means the file has changed, but the change has not yet been staged.


3. git status

One command I learned to use frequently is:

git status

It tells me the current state of my working directory and staging area.

For example:

modified: file.txt

means:

The file has changed, but the change isn't staged yet.

After:

git add file.txt

the status can show:

Changes to be committed:
    modified: file.txt

Now Git knows that this change is intended for the next commit.


4. Staging Area

The staging area was an important concept for me.

Think of it as a preparation area.

Working Directory
"These files changed."

        ↓ git add

Staging Area
"These changes should go into the next commit."

        ↓ git commit

Repository
"These changes are now part of history."

For example:

git add calculator.sh

or:

git add .

Then:

git commit -m "Update calculator"

5. git diff

Another useful command is:

git diff

It helps inspect what actually changed in files.

This becomes especially useful when working in a team because sometimes you know that something changed, but you need to see exactly what changed.


6. git log

Git also maintains history.

git log

can be used to inspect previous commits.

This becomes very useful when you want to understand:

  • What changed?

  • When did it change?

  • Which commit introduced the change?

  • What was the project like before?

In a team environment, Git history can help investigate when a change was introduced.


7. Git Tracks Changes

One interesting concept I learned was how Git tracks files.

If a tracked file is modified or deleted, Git can identify that change.

For example:

git status

can show that a file was modified.

And:

git diff

can help inspect the changes.

This made me understand that Git isn't simply "uploading files."

It is maintaining a history of changes.


8. Initializing a Repository

One way to start tracking a local project is:

git init

This creates a Git repository in the project.

Then we can connect it to GitHub:

git remote add origin REPOSITORY_URL

Then:

git add .
git commit -m "Initial Commit"
git push origin main

At this point, the local project can be pushed to the GitHub repository.


9. git init vs git clone

I also learned that there are different ways of getting a project locally.

git init

Usually used when you already have a local project and want to start tracking it with Git.

git init

git clone

Used when a repository already exists remotely and you want to create a local copy.

git clone REPOSITORY_URL

This is commonly how developers start working with an existing organizational repository.


10. Remote Repository and origin

When working with GitHub, we often see:

origin

For example:

git push origin main

Here:

  • origin = remote repository reference

  • main = branch

The remote can be inspected/configured using Git's remote commands.

The important idea is:

Local Repository
      |
      | origin
      ↓
GitHub Repository

11. My First Real Git Problem: master vs main

This was one of my first practical lessons.

I ran:

git init

I expected my local branch to be:

main

But my local repository had:

master

Then I tried:

git push -u origin main

But the local main branch didn't exist.

So I renamed the branch:

git branch -M main

After that, I could work with:

git push -u origin main

Lesson

I learned that before running a Git command, I should understand the current repository state.

Don't assume.

Check.


12. Authentication failed

After solving the branch issue, I encountered another problem:

Authentication failed

What made it interesting was that Git wasn't simply asking me for a username and password.

I had to look at the credential/authentication side of the setup.

I checked Windows Credential Manager and removed the old GitHub credentials.

Then I tried pushing again:

git push -u origin main

A Git Credential Manager authentication window appeared.

I used the browser-based sign-in flow.

Lesson

This taught me that Git operations can fail because of authentication configuration, not because of the project itself.


13. git push — What Does It Actually Do?

Once the local commit exists, we can send it to the remote repository:

git push origin main

Conceptually:

Local Commit
     ↓
   Push
     ↓
GitHub

But pushing isn't always successful.

And that led to another real error.


14. fetch first

I tried pushing and got:

! [rejected] main -> main (fetch first)

At first, this looks scary.

But the reason was simple:

The GitHub repository already had a commit that my local repository did not have.

In my case, the remote repository had its own initial content while my local project had its own history.

So Git was essentially saying:

"Your local history doesn't contain everything that exists remotely."

The next concept I had to understand was:

git pull origin main

15. What Does git pull Actually Do?

A useful mental model is:

git pull
   =
git fetch
   +
integration

The official Git documentation describes git pull as fetching from a remote and then integrating the remote changes into the current branch.

So:

git pull origin main

means:

Bring the latest changes from the remote main branch and integrate them into my current local branch.


16. git fetch vs git pull

This distinction became important.

git fetch

git fetch origin

Fetches information/updates from the remote without performing the same integration step as pull.

git pull

git pull origin main

Fetches and then integrates the remote changes.

A simple way to remember:

fetch
↓
"Show/bring me what changed remotely."

pull
↓
"Bring it and integrate it into my current branch."

17. unrelated histories

After trying to pull, I encountered another situation:

refusing to merge unrelated histories

This happened because my local project and remote GitHub repository had separate histories.

For example:

Remote:
README Commit
     ↓
     B


Local:
Laravel Initial Commit
     ↓
     Y

There was no common ancestor between the two histories.

Git therefore refused to merge them automatically.

The command discussed in my notes was:

git pull origin main --allow-unrelated-histories

This tells Git to allow the integration of those separate histories.


18. Merge Conflict

After allowing the histories to be merged, another practical concept appeared:

CONFLICT (content): Merge conflict in README.md
Automatic merge failed.

Git had found conflicting changes in the same file.

The file could contain conflict markers such as:

<<<<<<< HEAD
Local content
=======
Remote content
>>>>>>> main

I had to decide which content should remain and remove the conflict markers.

Then:

git add README.md

followed by:

git commit -m "Resolve merge conflict"

and finally:

git push -u origin main

Lesson

A merge conflict is not Git "breaking."

It means Git needs a human decision about which changes should be kept.


19. Branches

Then I moved deeper into branching.

A branch can be thought of as a separate line of development.

Instead of every developer working directly on the same branch:

main
 |
 +--- Developer A
 |
 +--- Developer B
 |
 +--- Developer C

we can separate work:

main
 |
 +--- feature/login
 |
 +--- feature/payment
 |
 +--- bug/fix-auth

This allows different work to happen independently.


20. Branching Strategy

In a real project, branching is not just:

"Create random branches."

There is usually a strategy.

For example:

Feature Branch
      ↓
Development
      ↓
Testing / Review
      ↓
Develop
      ↓
Release Branch
      ↓
Production

The exact strategy depends on the organization, but the important idea is separation of development, integration, and release work.


21. One Ticket → One Branch

One of the most important workflow rules from my notes was:

One ticket → one branch

For example:

Jira Ticket
     ↓
Create Feature Branch
     ↓
Work on Ticket
     ↓
Finish Work
     ↓
Pull Request
     ↓
Code Review
     ↓
Merge

Once the ticket is complete, that work goes through the review/merge process.

When a new task arrives:

New Ticket
    ↓
New Branch

This keeps tasks separated and makes the history easier to understand.


22. Keeping a New Branch Updated

Before starting work on a new task, the branch should be brought up to date with the relevant development code.

Conceptually:

develop
   ↓
latest code
   ↓
your new branch
   ↓
your development

This reduces the chance of developing against an outdated codebase.


23. Feature Branches

Feature branches are used to isolate feature development.

For example:

feature/user-login
feature/payment
feature/dashboard

Developers can work independently and later submit their work for review.


24. Bug Fix Branches

The same idea can be applied to bugs.

For example:

bug/login-error
bug/payment-validation
bug/api-response

This keeps bug-fixing work separate from unrelated development.


25. Release Branches

Another concept I learned was the release branch.

Why not just release directly from the active development branch?

Because the development branch may still be changing.

A release branch gives a more controlled place for:

  • testing

  • stabilization

  • final fixes

  • release preparation

Conceptually:

Feature Development
       ↓
Main / Develop
       ↓
Release Branch
       ↓
Testing
       ↓
Release
       ↓
Users

The idea is to keep the code being tested stable while active development continues elsewhere.


26. Feature Branch Cleanup

After a feature has been completed and merged, the feature branch can be deleted according to the team's workflow.

For example:

feature/login
     ↓
Pull Request
     ↓
Review
     ↓
Merge
     ↓
Delete feature branch

27. Fork

Another GitHub concept I learned was forking.

A fork is essentially creating your own copy of another repository under your GitHub account.

For example:

Original Repository
        ↓
       Fork
        ↓
Your GitHub Repository
        ↓
Your Changes
        ↓
Pull Request
        ↓
Original Repository

This is particularly useful when you don't have direct write access to the original repository.

My notes used an open-source project such as Kubernetes as an example of a project with many contributors.


28. SSH vs HTTPS

When cloning a repository, GitHub can provide different connection methods.

One important distinction I learned was:

HTTPS

Repository URL uses HTTPS.

SSH

Repository URL uses SSH.

If using SSH, an SSH key needs to be configured.

This led me to another practical topic.


29. SSH Key Generation

For SSH-based GitHub access, I learned about generating an SSH key.

A command discussed in my notes was:

ssh-keygen -t rsa

The key generation process creates the key files in the user's SSH directory.

The important concept is:

Local Machine
     ↓
SSH Key
     ↓
GitHub Authentication
     ↓
Repository Access

So when an SSH clone failed because the required key wasn't configured, the error wasn't about the repository code.

It was about authentication.


30. Local Integration vs Remote Integration

This was another concept I found useful.

Local Integration

For example:

git checkout develop
git merge feature-branch

The merge happens on the local machine.

feature-branch
       ↓
     merge
       ↓
develop

GitHub doesn't automatically create a Pull Request just because you performed a local merge and pushed the resulting develop branch.

Remote Integration / Pull Request

Instead:

git push origin feature-branch

Then create a Pull Request on GitHub.

Now the team gets a review process.


31. Pull Request

A Pull Request is more than just a way to merge code.

It creates a collaboration and review point.

Typical flow:

Feature Branch
      ↓
     Push
      ↓
Pull Request
      ↓
Code Review
      ↓
Changes / Approval
      ↓
Merge

32. Code Review

The reviewer can inspect changed files and comment on specific parts of the code.

A review can result in:

Comments

Suggestions or observations.

Approve

The reviewer is satisfied with the changes.

Request Changes

The developer needs to make modifications.

If changes are requested:

Reviewer
   ↓
Request Changes
   ↓
Developer fixes code
   ↓
Push again
   ↓
Reviewer checks again

This creates a controlled path toward merging code.


33. Why Pull Requests Matter in DevOps

This was an important shift in my understanding.

A direct local merge is:

Developer
   ↓
Local merge
   ↓
Push

A Pull Request workflow is:

Developer
   ↓
Feature Branch
   ↓
Pull Request
   ↓
Code Review
   ↓
Checks / Testing
   ↓
Approval
   ↓
Merge

The second workflow introduces collaboration, review, discussion and automated checks where configured.

That is why Pull Requests fit naturally into professional development and DevOps workflows.


34. GitHub + Jira

Another important part of the workflow was connecting development work with project/task tracking.

The idea was:

Jira Ticket
     ↓
Development Branch
     ↓
GitHub
     ↓
Pull Request
     ↓
Code Review
     ↓
Jira Subtask / Tracking

The Pull Request link can be added to the relevant Jira subtask.

The review status can then be tracked as part of the team's work.


35. Code Review Assignment

One practical team-management lesson from my notes was that code review should also be planned.

The same person should not necessarily be assigned every review.

A healthy team workflow considers:

  • who has expertise in the area

  • reviewer availability

  • workload distribution

  • avoiding bottlenecks

So code review is not only a technical process.

It is also part of team coordination.


36. Git Stash — One of My Most Useful Lessons

This was one of my favorite practical scenarios.

I had modified some code.

But I hadn't committed it.

Then I needed to switch/create another branch.

Git stopped me:

Please commit or stash your changes

The problem was:

My current work
      +
Unfinished changes
      +
Need to switch branch

I didn't necessarily want to create a commit for unfinished work.

So I learned:

git stash

This temporarily stores the changes and gives me a clean working directory.

I could then switch branches.

Later:

git stash pop

and the changes could be brought back.

The official Git documentation describes stash as a way to temporarily store local modifications so the working directory can be cleaned without committing unfinished work.

Easy way to remember:

git stash
=
"Put my unfinished work somewhere safe temporarily."

git stash pop
=
"Bring that work back."

37. git stash Commands

Some useful commands include:

git stash

Create a stash.

git stash list

See available stashes.

git stash pop

Restore the latest stash and remove it from the stash list.

git stash apply

Apply a stash without removing it.

git stash drop

Remove a stash.


38. Git Reset

Then came one of the more confusing concepts:

git reset

Git reset can move the branch/HEAD to another state and can also affect the staging area and working tree depending on the mode.

The three modes I focused on were:

             Commit   Staging   Files
--soft          ❌        ✅       ✅
--mixed         ❌        ❌       ✅
--hard          ❌        ❌       ❌

39. git reset --soft

Example:

git reset --soft HEAD~1

This removes the latest commit from the branch history while keeping the changes staged.

Useful when:

  • the commit message was wrong

  • you want to combine commits

  • you want to undo the last commit but keep the code

Then you can create another commit:

git commit -m "Correct commit message"

40. git reset --mixed

This is the default reset mode.

It moves the branch back while leaving the file changes in the working directory, but they are no longer staged.

Conceptually:

Commit removed
Staging removed
Code remains

41. git reset --hard

This is much more destructive to the current working state.

For example:

git reset --hard COMMIT_ID

can move the branch back to that commit and discard changes from the index and working tree.

This is where I had one of my most important Git learning moments.

I saw later commits disappear from normal git log output.

And I learned not to panic.


42. git reflog — The Recovery Lesson

After using:

git reset --hard

the later commit was no longer visible in the normal branch history.

But Git keeps a record of HEAD movements.

That's where:

git reflog

became important.

It can show previous HEAD positions, including commits that are no longer reachable from the current branch tip.

Conceptually:

Commit A
   ↓
Commit B
   ↓
Commit C

After reset:

Commit A
   ↓
Commit B

C is no longer the current branch tip

But:

git reflog

can help locate where HEAD previously pointed.

My biggest lesson here:

"Not visible in git log" does not automatically mean "immediately gone forever."


43. git log vs git reflog

This distinction became very useful.

git log

Shows the commit history reachable from the current branch/reference.

git reflog

Shows movements of references such as HEAD over time.

So:

git log
=
"What commits are in my current history?"

git reflog
=
"Where has HEAD/reference been?"

44. git reset vs git revert

These commands can sound similar, but they represent different approaches.

git reset changes the branch's history/state.

git revert creates a new commit that reverses the effect of an earlier commit.

This distinction becomes particularly important when working with shared branches.


45. The GitHub Workflow I Understand Now

After going through these concepts, the workflow makes much more sense to me.

JIRA TICKET
     ↓
CREATE BRANCH
     ↓
DEVELOP
     ↓
git status
     ↓
git diff
     ↓
git add
     ↓
git commit
     ↓
git push
     ↓
PULL REQUEST
     ↓
CODE REVIEW
     ↓
┌───────────────────┐
│ Changes Requested │
└─────────┬─────────┘
          ↓
       FIX CODE
          ↓
       PUSH AGAIN
          ↓
       REVIEW AGAIN
          ↓
       APPROVAL
          ↓
        MERGE
          ↓
       DEVELOP
          ↓
   RELEASE BRANCH
          ↓
       TESTING
          ↓
       RELEASE

This is where Git stopped looking like a collection of commands and started looking like a development workflow.


46. The Biggest Lesson: Errors Are Teachers

Looking back, some of the most useful things I learned came from errors.

Error 1

main branch doesn't exist

Lesson:

Understand your current branch.


Error 2

Authentication failed

Lesson:

Understand Git authentication and credentials.


Error 3

! [rejected] main -> main (fetch first)

Lesson:

Understand local vs remote history.


Error 4

refusing to merge unrelated histories

Lesson:

Understand commit history and common ancestors.


Error 5

CONFLICT

Lesson:

Understand how Git handles conflicting changes.


Error 6

Please commit or stash your changes

Lesson:

Understand when unfinished work should be stashed instead of committed.


Error 7

My commit disappeared after reset --hard

Lesson:

Understand HEAD, reset and reflog.


47. From GitHub Desktop to Command Line

This is probably the biggest personal shift in this learning journey.

GitHub Desktop had already introduced me to GitHub.

But when I started using commands, I started understanding what those GUI actions actually represented.

For example:

GUI Button
    ↓
Underlying Git command
    ↓
Repository state changes

Instead of simply thinking:

"Click Push."

I started thinking:

"What branch am I on? What commit am I pushing? Which remote am I targeting? Is the remote ahead? Is my authentication configured?"

That is a completely different level of understanding.


48. Git Is Not Just About Memorizing Commands

At the beginning, it is tempting to memorize:

git add
git commit
git push
git pull

But practical Git requires something more.

You need to understand:

STATE
  ↓
COMMAND
  ↓
RESULT
  ↓
ERROR
  ↓
REASON
  ↓
SOLUTION

That is the mindset I want to carry forward.


49. My Git Command Cheat Sheet

Repository

git init
git clone <url>
git remote -v
git remote add origin <url>

Status & Changes

git status
git diff
git log

Snapshot

git add .
git add <file>
git commit -m "message"

Remote

git fetch
git pull
git push

Branches

git branch
git branch <branch-name>
git branch -M main
git checkout <branch>
git switch <branch>

Merge

git merge <branch>

Stash

git stash
git stash list
git stash pop
git stash apply
git stash drop

Reset / Recovery

git reset --soft HEAD~1
git reset --mixed HEAD~1
git reset --hard <commit>
git reflog

SSH

ssh-keygen -t rsa

50. Final Takeaway

I started this learning phase thinking I was revising Git and GitHub.

But I ended up understanding something much more valuable.

Git is not just:

add
commit
push
pull

It is about understanding:

state, history, branches, collaboration, integration, review, recovery and automation.

And the errors I encountered were not interruptions to the learning process.

They were the learning process.

The biggest change in my mindset is this:

Before:

"Which command will fix this?"

Now:

"Why did Git do this?"

And I think that question is much more important.

Because once you understand why, the command usually makes much more sense.


Conclusion

From using GitHub Desktop…

to working through the command line…

to dealing with real Git errors…

to understanding branches, remotes, Pull Requests, code reviews, Jira integration, release branches, stash, reset and reflog…

this journey helped me see Git from a much more practical DevOps perspective.

And this is only one part of the bigger picture.

The next step is taking this workflow further into:

CI/CD → Automation → Testing → Deployment → Infrastructure


If you're also learning DevOps...

I documented the Git & GitHub concepts I learned, including the practical workflow and the real errors I encountered.

If you're trying to understand Git beyond just memorizing commands, I hope this helps. 🚀