<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rajat</title>
    <description>The latest articles on DEV Community by Rajat (@saboorajat).</description>
    <link>https://dev.to/saboorajat</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3976165%2F722a3ba3-f849-4641-96d0-266d7301244c.png</url>
      <title>DEV Community: Rajat</title>
      <link>https://dev.to/saboorajat</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/saboorajat"/>
    <language>en</language>
    <item>
      <title>What I got wrong about Git for years</title>
      <dc:creator>Rajat</dc:creator>
      <pubDate>Sat, 27 Jun 2026 08:10:00 +0000</pubDate>
      <link>https://dev.to/saboorajat/what-i-got-wrong-about-git-for-years-2me1</link>
      <guid>https://dev.to/saboorajat/what-i-got-wrong-about-git-for-years-2me1</guid>
      <description>&lt;p&gt;I used Git for years before I actually understood it.&lt;/p&gt;

&lt;p&gt;I could commit, push, pull, and make a branch. I could copy-paste my way out of most messes. But I didn't have a model in my head for what Git was doing. So every time something went sideways, it felt like luck whether I'd get out of it.&lt;/p&gt;

&lt;p&gt;What finally fixed that wasn't learning more commands. It was unlearning a handful of things I believed that were simply wrong. Here are the six that mattered most. If you hold any of them too — and most self-taught Git users do — fixing them will do more for you than memorising another twenty flags.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. I thought a commit was a diff
&lt;/h2&gt;

&lt;p&gt;This is the big one. I thought a commit stored &lt;em&gt;the changes&lt;/em&gt; — the lines I added and removed. A little patch, stacked on the patch before it.&lt;/p&gt;

&lt;p&gt;It isn't. A commit stores a &lt;strong&gt;snapshot of your entire project&lt;/strong&gt; at that moment. Every file, as it was. Git's own documentation is explicit about this: "Git thinks of its data more like a series of snapshots of a miniature filesystem." (&lt;a href="https://git-scm.com/book/en/v2/Getting-Started-What-is-Git%3F" rel="noopener noreferrer"&gt;Pro Git, "What is Git?"&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Git is smart about storage — files that didn't change between commits aren't duplicated, they're referenced. But the &lt;em&gt;model&lt;/em&gt; is snapshots, not diffs. The diffs you see in &lt;code&gt;git show&lt;/code&gt; or &lt;code&gt;git log -p&lt;/code&gt; are computed on the fly by comparing two snapshots. They aren't what's stored.&lt;/p&gt;

&lt;p&gt;Once this clicked, half of Git stopped being mysterious. Why can you check out any old commit and get the whole project back? Because each commit &lt;em&gt;is&lt;/em&gt; the whole project. Why is branching cheap? (More on that below.) Because a branch doesn't copy anything — it points at a snapshot that already exists.&lt;/p&gt;

&lt;p&gt;If you only fix one belief from this list, fix this one.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. I thought rebase was dangerous
&lt;/h2&gt;

&lt;p&gt;I avoided &lt;code&gt;git rebase&lt;/code&gt; for a long time. I'd read scary warnings: "rebase rewrites history", "never rebase", "you'll lose work". So I stuck to merge and felt vaguely guilty about my messy history.&lt;/p&gt;

&lt;p&gt;Here's the truth I wish someone had said plainly: &lt;strong&gt;rebase is completely safe on commits you haven't shared yet.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The danger of rebase is exactly one thing — rewriting history that &lt;em&gt;other people have already pulled&lt;/em&gt;. If you rebase a branch someone else has based work on, you force them into a painful reconciliation. That's the whole warning. (&lt;a href="https://git-scm.com/docs/git-rebase" rel="noopener noreferrer"&gt;git-rebase docs&lt;/a&gt; — "Recovering from upstream rebase".)&lt;/p&gt;

&lt;p&gt;But your own local feature branch, the one nobody else has touched? Rebase it freely. Reorder commits, squash them, clean up messages. You're rewriting history that only exists on your machine. Nobody is affected. And if you mess it up, the reflog has your back (see #6).&lt;/p&gt;

&lt;p&gt;"Don't rebase shared branches" is good advice. "Don't rebase" is not — it just keeps you scared of one of Git's most useful tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. I thought &lt;code&gt;git pull&lt;/code&gt; was always safe
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;git pull&lt;/code&gt; felt like the safe, simple way to get updates. It's not simple, and it's not always safe.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git pull&lt;/code&gt; is two commands in a trench coat: &lt;code&gt;git fetch&lt;/code&gt; followed by &lt;code&gt;git merge&lt;/code&gt; (or &lt;code&gt;git rebase&lt;/code&gt;, if you've configured it). (&lt;a href="https://git-scm.com/docs/git-pull" rel="noopener noreferrer"&gt;git-pull docs&lt;/a&gt; — "git pull runs git fetch ... and then ... git merge".)&lt;/p&gt;

&lt;p&gt;That hidden merge is where the surprises live. If your local branch and the remote have both moved on, &lt;code&gt;pull&lt;/code&gt; creates a merge commit you didn't ask for. If you have uncommitted changes that conflict, it stops half-finished and leaves you confused. The "simple" command did two things, and the second one had opinions.&lt;/p&gt;

&lt;p&gt;What changed for me was separating the steps. &lt;code&gt;git fetch&lt;/code&gt; first — that only downloads, it never touches your working files, so it's genuinely always safe. Then I look at what came in (&lt;code&gt;git log HEAD..@{u}&lt;/code&gt;), and &lt;em&gt;then&lt;/em&gt; I decide: merge or rebase. The decision is mine, made on purpose, instead of baked into a command I ran on autopilot.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. I thought branches were heavy
&lt;/h2&gt;

&lt;p&gt;I came to Git after a brief, unhappy time with older version-control tools where creating a branch was a big deal — slow, space-hungry, something you did a few times a year.&lt;/p&gt;

&lt;p&gt;So I hoarded branches. I'd do too much work on one branch because making a new one felt expensive.&lt;/p&gt;

&lt;p&gt;A Git branch is &lt;strong&gt;a file containing one line: the 40-character hash of a commit.&lt;/strong&gt; That's it. Creating a branch writes 41 bytes. It's instant, and it costs nothing. (&lt;a href="https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell" rel="noopener noreferrer"&gt;Pro Git, "Git Branching in a Nutshell"&lt;/a&gt; — "a branch in Git is simply a lightweight movable pointer".)&lt;/p&gt;

&lt;p&gt;Branches are so cheap that the right habit is the opposite of hoarding: make one for every small thing. Trying an idea? Branch. Reviewing a colleague's work? Branch. Worried a change might not pan out? Branch — if it doesn't work, you just delete the pointer and the snapshots get cleaned up later. There's no cost to being generous with them.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. I thought HEAD meant "the latest commit"
&lt;/h2&gt;

&lt;p&gt;I assumed &lt;code&gt;HEAD&lt;/code&gt; was Git's word for the newest commit — the tip of everything.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;HEAD&lt;/code&gt; means "&lt;strong&gt;where you are right now&lt;/strong&gt;". Almost always, it's a pointer to the branch you're currently on (which in turn points at a commit). It's not "the latest commit in the repo" — it's "the commit your working directory currently reflects". (&lt;a href="https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell" rel="noopener noreferrer"&gt;Pro Git, "Git Branching"&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;This sounds like a small distinction. It isn't. Once I understood HEAD as a cursor — &lt;em&gt;you are here&lt;/em&gt; — a whole family of commands stopped being scary. &lt;code&gt;HEAD~1&lt;/code&gt; is "one commit back from where I am". &lt;code&gt;git reset --hard HEAD~3&lt;/code&gt; is "move my current branch back three steps". "Detached HEAD" stopped being an error message I feared and became a plain fact: I'd pointed HEAD directly at a commit instead of at a branch, so I was standing on a commit with no branch name attached.&lt;/p&gt;

&lt;p&gt;Git tells you where HEAD is all the time. I just hadn't understood what it was telling me.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. I thought a commit could be truly lost
&lt;/h2&gt;

&lt;p&gt;The scariest belief, and the one that made me timid. I thought a bad &lt;code&gt;reset&lt;/code&gt;, a deleted branch, or a botched rebase could &lt;em&gt;destroy&lt;/em&gt; my work for good. So I treated history as fragile and avoided the commands that touch it.&lt;/p&gt;

&lt;p&gt;Almost nothing is truly lost. Git keeps a journal called the &lt;strong&gt;reflog&lt;/strong&gt; that records every time &lt;code&gt;HEAD&lt;/code&gt; moved — every commit, checkout, reset, and rebase. The commits those entries point to survive in the object database for a while even after nothing else references them: roughly &lt;strong&gt;90 days&lt;/strong&gt; for commits still reachable, and &lt;strong&gt;30 days&lt;/strong&gt; for unreachable ones, before garbage collection is even allowed to remove them. (&lt;a href="https://git-scm.com/docs/git-reflog" rel="noopener noreferrer"&gt;git-reflog docs&lt;/a&gt;, &lt;a href="https://git-scm.com/docs/git-config" rel="noopener noreferrer"&gt;gc.reflogExpire config&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;That means a "lost" commit is usually just a &lt;code&gt;git reflog&lt;/code&gt; and a &lt;code&gt;git reset --hard &amp;lt;sha&amp;gt;&lt;/code&gt; away. I wrote a &lt;a href="https://www.gitflow.dev/blog/git-reflog-the-undo-button" rel="noopener noreferrer"&gt;whole post on exactly how to do that recovery&lt;/a&gt; — it's the single highest-ROI thing I learned in Git.&lt;/p&gt;

&lt;p&gt;Knowing the reflog exists changed how I work. I stopped being precious about history. I reorder, squash, and reset freely now, because I know the footprints are still there if I need to walk back.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common myths
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Myth: "You have to understand Git's internals to use it well."&lt;/strong&gt; Mostly false. You don't need to know how packfiles work. You &lt;em&gt;do&lt;/em&gt; need the right mental model for a handful of everyday things — commit, branch, HEAD, reflog. That's six ideas, not a computer-science course.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Myth: "Merge is safe, rebase is dangerous."&lt;/strong&gt; Half true, stated misleadingly. Both are safe on your own unshared work. The only real rule is: don't rewrite history other people have already pulled. That applies to any history-rewriting command, not just rebase.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Myth: "If &lt;code&gt;git status&lt;/code&gt; looks scary, I probably broke something."&lt;/strong&gt; Usually false. Most of the time &lt;code&gt;git status&lt;/code&gt; is telling you exactly what state you're in and what to do next — it even suggests commands. The fear comes from not having the model, not from anything being broken. Read it slowly; it's on your side.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pattern
&lt;/h2&gt;

&lt;p&gt;Looking back, none of my problems were about commands. They were about the model. I'd memorised &lt;em&gt;what to type&lt;/em&gt; without understanding &lt;em&gt;what it did&lt;/em&gt;, so I couldn't reason about anything new.&lt;/p&gt;

&lt;p&gt;That's the whole reason I built &lt;a href="https://www.gitflow.dev/about" rel="noopener noreferrer"&gt;GitFlow&lt;/a&gt;: a place to practise on a real Git engine, with a live commit graph that shows you the snapshots and pointers moving as you type — so the model builds itself in your head instead of staying abstract.&lt;/p&gt;

&lt;p&gt;If you take one thing from this: stop collecting commands. Build the model. Six ideas — snapshots, cheap branches, HEAD-as-cursor, safe-local-rebase, the two-step truth of pull, and the reflog safety net — and Git stops feeling like luck.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What did you get wrong about Git for the longest time? I'm curious which of these six is the most common — or which one I'm still wrong about.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>git</category>
      <category>beginners</category>
      <category>productivity</category>
      <category>devops</category>
    </item>
    <item>
      <title>git reflog: the undo button you didn't know you had</title>
      <dc:creator>Rajat</dc:creator>
      <pubDate>Tue, 09 Jun 2026 14:20:05 +0000</pubDate>
      <link>https://dev.to/saboorajat/git-reflog-the-undo-button-you-didnt-know-you-had-5078</link>
      <guid>https://dev.to/saboorajat/git-reflog-the-undo-button-you-didnt-know-you-had-5078</guid>
      <description>&lt;p&gt;You've just run &lt;code&gt;git reset --hard HEAD~5&lt;/code&gt;. The terminal goes quiet. Your stomach drops.&lt;/p&gt;

&lt;p&gt;Five commits — three hours of work — are gone. You didn't push them. You didn't open a PR. Your editor doesn't have them in its undo buffer anymore. The Git object database has freed the references and is going to garbage-collect them eventually.&lt;/p&gt;

&lt;p&gt;You can get every one of them back, perfectly, in about forty seconds.&lt;/p&gt;

&lt;p&gt;This is the most important command in Git that almost nobody learns until they've already lost work twice. It's called &lt;code&gt;git reflog&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What reflog actually is
&lt;/h2&gt;

&lt;p&gt;A "ref" in Git is just a pointer. &lt;code&gt;HEAD&lt;/code&gt; is a ref. Every branch name is a ref. Every tag is a ref. When you make a commit, Git updates whatever ref you're currently sitting on to point at the new commit's SHA. When you switch branches, the &lt;code&gt;HEAD&lt;/code&gt; ref moves to the tip of the new branch.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;reflog&lt;/strong&gt; is a journal of every ref movement. Every time &lt;code&gt;HEAD&lt;/code&gt; shifts — every commit, every checkout, every merge, every reset, every rebase step — Git writes a line into the reflog with the previous SHA, the new SHA, and a short reason like &lt;code&gt;"commit"&lt;/code&gt; or &lt;code&gt;"reset: moving to HEAD~5"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That journal lives in &lt;code&gt;.git/logs/HEAD&lt;/code&gt; and &lt;code&gt;.git/logs/refs/heads/&amp;lt;branch&amp;gt;&lt;/code&gt;. It's local to your machine. It's not pushed anywhere. It's not part of the repo. Nobody else can see it.&lt;/p&gt;

&lt;p&gt;And, critically — and this is the part that saves your three hours of work — the &lt;strong&gt;commits the reflog references are not deleted until garbage collection runs&lt;/strong&gt;, which by default is &lt;strong&gt;30 days from now&lt;/strong&gt;. Even when those commits are no longer reachable from any branch, no longer in any working tree, no longer mentioned by any tag — they sit in &lt;code&gt;.git/objects/&lt;/code&gt;, untouched, waiting for you to point a branch at them.&lt;/p&gt;

&lt;p&gt;A failed &lt;code&gt;reset --hard&lt;/code&gt; doesn't delete commits. It just unparks them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The commands you actually need
&lt;/h2&gt;

&lt;p&gt;There are only three. Memorise them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git reflog
git reflog show &amp;lt;branch&amp;gt;
git reset &lt;span class="nt"&gt;--hard&lt;/span&gt; &amp;lt;sha-or-ref&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's walk through a recovery from start to finish.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Look at the reflog
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git reflog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll get something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;a1b2c3d HEAD@{0}: reset: moving to HEAD~5
9f4e2c8 HEAD@{1}: commit: feat: wire payment confirmation email
6c1a5e3 HEAD@{2}: commit: feat: add payment confirmation route
3d8f2b1 HEAD@{3}: commit: chore: bump stripe SDK
0e7a9d4 HEAD@{4}: commit: refactor: extract email queue
b4c8e2f HEAD@{5}: commit: feat: add email queue worker
2a1b3c4 HEAD@{6}: checkout: moving from main to feature/payments
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read this top-down as "what HEAD just did, in reverse order". The most recent thing is &lt;code&gt;HEAD@{0}&lt;/code&gt; — the &lt;code&gt;reset --hard&lt;/code&gt; we just regretted. The five commits we lost are right there at &lt;code&gt;HEAD@{1}&lt;/code&gt; through &lt;code&gt;HEAD@{5}&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Move HEAD back
&lt;/h3&gt;

&lt;p&gt;The simplest recovery: put your branch back where it was before the reset. The reflog tells you the SHA of the commit you regret leaving — it's the entry that says &lt;code&gt;commit:&lt;/code&gt; immediately above the bad &lt;code&gt;reset:&lt;/code&gt; line. In the example above, that's &lt;code&gt;9f4e2c8&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git reset &lt;span class="nt"&gt;--hard&lt;/span&gt; 9f4e2c8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Your branch is now at &lt;code&gt;9f4e2c8&lt;/code&gt; again. All five commits are reachable from the tip. &lt;code&gt;git log&lt;/code&gt; shows them. Your working tree matches commit &lt;code&gt;9f4e2c8&lt;/code&gt;. Three hours of work, restored.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four scenarios where reflog saves you
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Hard reset you regret
&lt;/h3&gt;

&lt;p&gt;The scenario above. Either you reset more than you meant to, or you reset and then realised "wait, I needed that branch state for something."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git reflog                           &lt;span class="c"&gt;# find the SHA you want back&lt;/span&gt;
git reset &lt;span class="nt"&gt;--hard&lt;/span&gt; &amp;lt;sha&amp;gt;               &lt;span class="c"&gt;# move HEAD there&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Deleted a branch with unmerged work
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git branch &lt;span class="nt"&gt;-D&lt;/span&gt; feature/payments
&lt;span class="c"&gt;# (panic)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Branches are just refs. Deleting one removes the pointer but &lt;strong&gt;leaves the commits intact&lt;/strong&gt; for the reflog window. Recovery:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git reflog | &lt;span class="nb"&gt;grep &lt;/span&gt;payments           &lt;span class="c"&gt;# find the last SHA the branch was at&lt;/span&gt;
git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; feature/payments &amp;lt;sha&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Rebase ate someone else's commits
&lt;/h3&gt;

&lt;p&gt;You ran &lt;code&gt;git rebase main&lt;/code&gt; and resolved a conflict by picking your side. Now you realise that side dropped Alice's commits that were on the branch before you started.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git reflog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Find the entry that says &lt;code&gt;rebase: start&lt;/code&gt; or &lt;code&gt;rebase (start)&lt;/code&gt;. Whatever HEAD was at &lt;em&gt;just before&lt;/em&gt; the rebase began is your pre-rebase state. Reset to it.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Force-pushed over a teammate's work
&lt;/h3&gt;

&lt;p&gt;Your teammate pushed something. You force-pushed your version over it without pulling first. Their commits are gone from the remote — and from your local, because you &lt;code&gt;git push --force&lt;/code&gt;'d.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# On their machine:&lt;/span&gt;
git reflog | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt;                &lt;span class="c"&gt;# they can still see their commits&lt;/span&gt;
git push &lt;span class="nt"&gt;--force&lt;/span&gt; origin feature      &lt;span class="c"&gt;# re-publish their state&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this exact reason: &lt;strong&gt;never &lt;code&gt;git push --force&lt;/code&gt;. Use &lt;code&gt;git push --force-with-lease&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The thing reflog won't save you from
&lt;/h2&gt;

&lt;p&gt;Reflog tracks ref movement. It does &lt;strong&gt;not&lt;/strong&gt; track changes to files you never committed.&lt;/p&gt;

&lt;p&gt;If you ran &lt;code&gt;git checkout -- &amp;lt;file&amp;gt;&lt;/code&gt; (or its modern equivalent &lt;code&gt;git restore &amp;lt;file&amp;gt;&lt;/code&gt;) on a file you'd been editing but never staged, that work is lost. The file's pre-edit state replaced your edits in the working tree, and nothing was ever written to the object database. The reflog can't help.&lt;/p&gt;

&lt;p&gt;The rule: &lt;strong&gt;anything Git ever assigned a SHA to is recoverable for ~30 days. Anything Git never saw is gone the moment you tell it to discard.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How long do I actually have?
&lt;/h2&gt;

&lt;p&gt;By default, two windows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reachable but unreferenced commits&lt;/strong&gt; (e.g., dangling after a reset): &lt;strong&gt;90 days&lt;/strong&gt; before &lt;code&gt;git gc&lt;/code&gt; is allowed to remove them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unreachable commits&lt;/strong&gt; (orphan branches, dropped stashes): &lt;strong&gt;30 days&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also extend these in &lt;code&gt;~/.gitconfig&lt;/code&gt; if you want longer recovery windows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[gc]&lt;/span&gt;
  &lt;span class="py"&gt;reflogExpire&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;365.days&lt;/span&gt;
  &lt;span class="py"&gt;reflogExpireUnreachable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;365.days&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I run with 365 on personal machines. The disk cost is negligible, and the peace of mind is real.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical habit
&lt;/h2&gt;

&lt;p&gt;I keep this command aliased in my &lt;code&gt;.gitconfig&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[alias]&lt;/span&gt;
  &lt;span class="py"&gt;rl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;reflog --date=relative&lt;/span&gt;
  &lt;span class="py"&gt;oops&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;!git reset --hard HEAD@{1}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first gives me a human-friendly reflog with relative dates. The second is the literal undo button: it moves HEAD back to where it was one operation ago, no matter what that operation was. &lt;code&gt;git oops&lt;/code&gt; is the muscle memory I want when I realise I just did something wrong.&lt;/p&gt;

&lt;p&gt;Try this once today: do a &lt;code&gt;git reset --hard HEAD~1&lt;/code&gt; on a throwaway branch, then recover with &lt;code&gt;git oops&lt;/code&gt;. The first time you do it deliberately, the panic-flavored memory of doing it accidentally evaporates.&lt;/p&gt;

&lt;p&gt;You don't need to learn Git's internals to ship code. But you do need to know the reflog exists. The next three-hour mistake you save yourself from will make it the highest-ROI five minutes of Git education you ever have.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I wrote this as part of &lt;a href="https://www.gitflow.dev" rel="noopener noreferrer"&gt;gitflow.dev&lt;/a&gt; — interactive Git training with a real Git engine in the browser. Free, no sign-up to read. If you found this useful, the &lt;a href="https://www.gitflow.dev/scenarios/lost-commits-reflog" rel="noopener noreferrer"&gt;reflog recovery scenario&lt;/a&gt; lets you try the recovery in a live terminal.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>git</category>
      <category>beginners</category>
      <category>productivity</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
