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


# 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:

```text
Authentication failed
```

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

```text
refusing to merge unrelated histories
```

```text
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:

```text
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.

```text
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:

```bash
git status
```

can show:

```text
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:

```bash
git status
```

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

For example:

```text
modified: file.txt
```

means:

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

After:

```bash
git add file.txt
```

the status can show:

```text
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.

```text
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:

```bash
git add calculator.sh
```

or:

```bash
git add .
```

Then:

```bash
git commit -m "Update calculator"
```

* * *

# 5\. `git diff`

Another useful command is:

```bash
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.

```bash
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:

```bash
git status
```

can show that a file was modified.

And:

```bash
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:

```bash
git init
```

This creates a Git repository in the project.

Then we can connect it to GitHub:

```bash
git remote add origin REPOSITORY_URL
```

Then:

```bash
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.

```bash
git init
```

### `git clone`

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

```bash
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:

```bash
origin
```

For example:

```bash
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:

```text
Local Repository
      |
      | origin
      ↓
GitHub Repository
```

* * *

# 11\. My First Real Git Problem: `master` vs `main`

This was one of my first practical lessons.

I ran:

```bash
git init
```

I expected my local branch to be:

```text
main
```

But my local repository had:

```text
master
```

Then I tried:

```bash
git push -u origin main
```

But the local `main` branch didn't exist.

So I renamed the branch:

```bash
git branch -M main
```

After that, I could work with:

```bash
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:

```text
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:

```bash
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:

```bash
git push origin main
```

Conceptually:

```text
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:

```text
! [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:

```bash
git pull origin main
```

* * *

# 15\. What Does `git pull` Actually Do?

A useful mental model is:

```text
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:

```bash
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`

```bash
git fetch origin
```

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

### `git pull`

```bash
git pull origin main
```

Fetches and then integrates the remote changes.

A simple way to remember:

```text
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:

```text
refusing to merge unrelated histories
```

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

For example:

```text
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:

```bash
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:

```text
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:

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

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

Then:

```bash
git add README.md
```

followed by:

```bash
git commit -m "Resolve merge conflict"
```

and finally:

```bash
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:

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

we can separate work:

```text
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:

```text
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:

```text
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:

```text
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:

```text
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:

```text
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:

```text
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:

```text
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:

```text
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:

```text
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:

```bash
ssh-keygen -t rsa
```

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

The important concept is:

```text
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:

```bash
git checkout develop
git merge feature-branch
```

The merge happens on the local machine.

```text
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:

```bash
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:

```text
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:

```text
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:

```text
Developer
   ↓
Local merge
   ↓
Push
```

A Pull Request workflow is:

```text
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:

```text
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:

```text
Please commit or stash your changes
```

The problem was:

```text
My current work
      +
Unfinished changes
      +
Need to switch branch
```

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

So I learned:

```bash
git stash
```

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

I could then switch branches.

Later:

```bash
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:

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

git stash pop
=
"Bring that work back."
```

* * *

# 37\. `git stash` Commands

Some useful commands include:

```bash
git stash
```

Create a stash.

```bash
git stash list
```

See available stashes.

```bash
git stash pop
```

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

```bash
git stash apply
```

Apply a stash without removing it.

```bash
git stash drop
```

Remove a stash.

* * *

# 38\. Git Reset

Then came one of the more confusing concepts:

```bash
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:

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

* * *

# 39\. `git reset --soft`

Example:

```bash
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:

```bash
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:

```text
Commit removed
Staging removed
Code remains
```

* * *

# 41\. `git reset --hard`

This is much more destructive to the current working state.

For example:

```bash
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:

```bash
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:

```bash
git reflog
```

became important.

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

Conceptually:

```text
Commit A
   ↓
Commit B
   ↓
Commit C
```

After reset:

```text
Commit A
   ↓
Commit B

C is no longer the current branch tip
```

But:

```bash
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:

```text
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.

```text
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

```text
main branch doesn't exist
```

Lesson:

> Understand your current branch.

* * *

### Error 2

```text
Authentication failed
```

Lesson:

> Understand Git authentication and credentials.

* * *

### Error 3

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

Lesson:

> Understand local vs remote history.

* * *

### Error 4

```text
refusing to merge unrelated histories
```

Lesson:

> Understand commit history and common ancestors.

* * *

### Error 5

```text
CONFLICT
```

Lesson:

> Understand how Git handles conflicting changes.

* * *

### Error 6

```text
Please commit or stash your changes
```

Lesson:

> Understand when unfinished work should be stashed instead of committed.

* * *

### Error 7

```text
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:

```text
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:

```text
git add
git commit
git push
git pull
```

But practical Git requires something more.

You need to understand:

```text
STATE
  ↓
COMMAND
  ↓
RESULT
  ↓
ERROR
  ↓
REASON
  ↓
SOLUTION
```

That is the mindset I want to carry forward.

* * *

# 49\. My Git Command Cheat Sheet

### Repository

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

### Status & Changes

```bash
git status
git diff
git log
```

### Snapshot

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

### Remote

```bash
git fetch
git pull
git push
```

### Branches

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

### Merge

```bash
git merge <branch>
```

### Stash

```bash
git stash
git stash list
git stash pop
git stash apply
git stash drop
```

### Reset / Recovery

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

### SSH

```bash
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:

```text
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. 🚀
