Simplifying Git: A Practical Guide for Everyday Use.

Hie! Welcome to another post where I’m going to use my poor writing skills to simplify Git for you, XD.

The Problem

If you’re reading this, I expect you to be already familiar with the basic Git workflow. Git is often portrayed as overly complex, which it is but only if you consider all the features it has to offer.

If you only consider the features that you need for everyday software development, it becomes much simpler to use in practice.

Having worked at various companies and multiple personal projects over the years, one thing that has been constant is Git. Your tech stack might change every year or so, but Git remains the same. Learn it once, and you can use it everywhere.

While I’m not an expert, I’ll walk you through the most basic Git workflow I use—one that aligns with how most software is written. We’ll explore one of the most popular Git workflows, known as Git Flow.

What is Git Flow?

Git Flow is a popular Git branching strategy aimed at simplifying release management, introduced by software developer Vincent Driessen in 2010. Fundamentally, Git Flow involves isolating your work into different types of Git branches - Source.

Git Flow defines specific branch types for different purposes:

  • main/master: Production-ready code.
  • development: Staging/development environments.
  • feature branches: Individual feature development.
  • release branches: Preparing new releases.
  • hotfix branches: Quick fixes to production.

For most projects, you’ll primarily work with main (production), development (staging), and feature branches. The others are useful for larger teams with complex release cycles.

📌 Important Note

Before moving forward, I’d like to stop here and define some aliases that make working with Git much easier and fun. Paired with a Zsh framework such as Oh My Zsh, I don’t think there is a better way to use Git from a CLI. These aliases are straight from my .zshrc config:

Git CLI

# Navigation & Branch Management

alias gb='git branch'           # List all local branches
alias gc='git checkout'         # Switch to an existing branch
alias gcb='git checkout -b'     # Create and switch to a new branch
alias gbd='git branch -d'       # Delete a local branch

# Push & Pull

alias gp='git push'                     # Push current changes to remote branch
alias gpl='git pull'                    # Pull latest changes from remote branch
alias gpo='git pull origin --no-edit'   # Pull without opening an editor for merge messages

# Commit & Add

alias gcm='git commit -m'             # Commit with message
alias gacp='git add . && git commit -m "uwu" && git push'  # Quick add/commit/push with default message. Works best when working on personal projects.

# Stash Management

alias gs='git stash'                  # Stash current changes
alias gsp='git stash pop'             # Apply and remove last stash
alias gsl='git stash list'            # Show all stashed changes

GitHub CLI

The GitHub CLI (gh) is GitHub-specific and provides easy integration with GitHub’s features like pull requests, issues, and repositories. I use it for both personal projects and professional work.

But the Git CLI should be the same regardless of the code hosting provider you use.

alias gpr='gh pr create --base' # Create a PR by specifying a base branch ex: gpr main
alias gpm='gh pr merge' # Merge the current PR (run from the PR branch).

Now that we have these aliases set up, I’ll use them throughout the rest of this article to demonstrate Git workflows. They’ll make our examples much cleaner and more practical for real-world usage.

Working with Branches

If you ask me, branches are one of the coolest things about Git. They let you work on features independently without affecting the main codebase.

You can make multiple copies of the same code in multiple branches and test your changes in isolation. Here’s how I typically work with branches using the aliases we set above:

Creating, Switching, & Deleting Branches

Always pull the changes from the remote before you create a branch. It will save you a lot of headaches with conflicts later.

# Create and switch to a new feature branch
gcb feat/user-authentication

# Switch to an existing branch
gc feat/user-chat

# Delete a local branch after it's been merged, not necessary unless you wanna create a fresh branch with same name.
gbd feat/user-authentication

# Force delete a local branch (even if not merged - use with caution)
gb -D feat/broken-feature

# Delete a remote branch
gp origin --delete feat/user-authentication

Adding and Committing

I personally use my editor to add and remove files because it’s much easier and gives me a beautiful diff of the changes that I made so that I can be sure that I’m not pushing unintended changes.

The diffs also act as self code review because you can see if you have over-engineered something so you can go back and simplify it. This is far more relevant these days because people are straight up pushing LLM written slop. Please don’t be that person.

If you think using the CLI to add files makes you look cool, good luck when you push something to prod without diffing it first.

Nonetheless, here are some examples of adding & removing files using the CLI:

# Add individual files
git add src/components/chat.ts
git add README.md

# Add multiple files
git add src/utils/auth.ts src/types/user.ts package.json

# Add all files in a directory
git add src/components/

# Add all TypeScript files
git add *.ts *.tsx

# Add everything (use when you're sure you have only changed the required files & make sure to run "git status" to see which files have been modified)
git add .

# Remove files from staging (if you added by mistake)
git reset src/components/chat.ts

And here’s how I typically commit:

# Quick commit for personal projects
gacp    # Adds everything, commits with "uwu", and pushes

# Proper commit with custom message
gcm "Add http fallback to websockets"

Pulling and Pushing

These are the most basic operations to sync your local work with the remote repository. Always pull before you start working to avoid conflicts later.

Note: Git might ask you to configure how to handle divergent branches on your first pull. Just run git config pull.rebase false to use merge (the default). Rebase vs merge is a whole different topic—I just use merge and call it a day.

If you really wanna know how each one differs, you can read it here.

# Pull latest changes from remote
gpl

# Pull changes from a remote branch
gpo main

# Push current local changes
gp

Stash Operations

Stash is like a temporary clipboard for your uncommitted changes. Super useful when you need to quickly switch branches but aren’t ready to commit yet.

# Save current changes temporarily
gs

# Restore last stashed changes
gsp

# See all stashed changes
gsl

Pull Requests (PRs)

PRs are basically how you say “Hey, I made some changes, can you review them?” They’re super important for code review and collaboration, especially when working with other people.

These can also be used to keep track of the things that you have shipped.

Creating a PR

After you’ve pushed your feature branch, creating a PR is dead simple with the GitHub CLI:

gpr main  # Creates a PR against the main branch

The GitHub CLI will ask you for a title and description. Don’t just write “fix stuff”—actually explain what you did and why. If not a description, at least make the title clear.

PR Best Practices

I’ve seen some terrible PRs, so here’s what actually matters:

  • Write clear titles: “Add user authentication” not “fix stuff” or “updates.”
  • Explain (optional): Don’t just say what you changed, explain why you changed it. Not always, but when the changes touch a bunch of other files.
  • Keep PRs Simple: Nobody wants to review 50 files. Break big features into smaller PRs.
  • Test & lint your stuff: Please test & lint your changes ffs before you ask for a review.

Wrapping Up

Alright, that’s pretty much everything you need to know about Git for day-to-day development. Honestly, Git can do a lot more than this, but trust me—you as an average developer will barely need that. Here are the takeaways:

  • Git Flow is nice but don’t over-complicate it. For small & personal projects, just use main and feature branches.
  • Use aliases. Your fingers will thank you after typing gcb instead of git checkout -b for the 100th time.
  • Branch often. Create branches for everything, even tiny changes.
  • PRs are your friends. They catch bugs and make you and your code better. But only if you take criticism constructively.
  • Commit messages matter when you work with teams. Imagine trying to find the change that broke prod among 100 commits that all say “fixes.”
  • When in doubt, stash the changes. It’s much better than losing work.

That’s about it for this post. See you in the next one! Happy Coding & have a good day!