<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
      <title>Sarah Deaton - talks</title>
      <link>https://sdeaton.com</link>
      <description>On AI, developer tools, technical writing, and whatever else catches my interest</description>
      <generator>Zola</generator>
      <language>en</language>
      <atom:link href="https://sdeaton.com/tags/talks/rss.xml" rel="self" type="application/rss+xml"/>
      <lastBuildDate>Fri, 15 May 2026 00:00:00 +0000</lastBuildDate>
      <item>
          <title>The git commands I avoided for nine years</title>
          <pubDate>Fri, 15 May 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://sdeaton.com/blog/git-talk-recap/</link>
          <guid>https://sdeaton.com/blog/git-talk-recap/</guid>
          <description xml:base="https://sdeaton.com/blog/git-talk-recap/">&lt;!--
TODO before publishing:
- copy videos from ~&#x2F;src&#x2F;git-talk-slides&#x2F;videos&#x2F; into this directory:
  with-worktrees.mp4, with-reflog.mp4, update-refs.mp4
--&gt;
&lt;p&gt;I recently gave a &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=FiGT3XYICSE&quot;&gt;talk&lt;&#x2F;a&gt; at &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.writethedocs.org&#x2F;&quot;&gt;Write the Docs&lt;&#x2F;a&gt; about three git commands I’d been avoiding for most of my career, or in two cases didn’t know existed at all. For the demo environment behind the talk, I built &lt;a href=&quot;..&#x2F;making-acaas&#x2F;&quot;&gt;All Caps as a Service&lt;&#x2F;a&gt;, a fake API and docs site that turned into its own side quest.&lt;&#x2F;p&gt;
&lt;p&gt;To properly set expectations up front: I’m not a git expert. I’ve used git almost daily for a decade with a workflow I’ll describe in a minute, and recently discovered some parts of git that changed my thinking on it. I gave the talk and wrote this post because you don’t need to be a git expert to get a long way with the tool, or to look into the parts you’ve been avoiding.&lt;&#x2F;p&gt;
&lt;iframe
  width=&quot;100%&quot;
  height=&quot;415&quot;
  src=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;embed&#x2F;FiGT3XYICSE&quot;
  title=&quot;The git commands I avoided for nine years&quot;
  frameborder=&quot;0&quot;
  allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot;
  allowfullscreen
&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;git-talk-recap&#x2F;git-talk-sketchnote.jpg&quot; alt=&quot;Sketchnote of the talk by Dennis Dawson. A timeline of sticky notes across the top reads “The Git Commands I Avoided for Nine Years (and why I wish I hadn’t).” Below, three commands are illustrated: git worktree, described as creating a second checkout of the repo; git reflog, described as an undo button for recovering lost commits, deleted branches, and bad rebases; and git rebase –update-refs, described as automagically force-updating all local branches that point to commits being rebased. Jujutsu (jj) appears at the bottom, described as reimagining what a commit is with no staging area and conflicts as first-class objects. On the right, “commit often” is written vertically in large pink letters.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Sketchnote by &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;dennissdawson.wixsite.com&#x2F;mr--dawson&#x2F;portfolio&quot;&gt;Dennis Dawson&lt;&#x2F;a&gt;, via the &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.flickr.com&#x2F;photos&#x2F;writethedocs&#x2F;55266348341&#x2F;in&#x2F;album-72177720333614185&quot;&gt;Write the Docs Flickr&lt;&#x2F;a&gt;. I love the reflog drawing.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-i-got-here&quot;&gt;How I got here&lt;&#x2F;h2&gt;
&lt;p&gt;For the majority of my time in tech, I used about six git commands: &lt;code&gt;add&lt;&#x2F;code&gt;, &lt;code&gt;commit -m &quot;wip&quot;&lt;&#x2F;code&gt;, &lt;code&gt;rebase -i&lt;&#x2F;code&gt;, &lt;code&gt;commit --amend&lt;&#x2F;code&gt;, &lt;code&gt;cherry-pick&lt;&#x2F;code&gt;, &lt;code&gt;push&lt;&#x2F;code&gt;. Add, save, clean up the history, polish, and push. It worked, and it produced nice clean PRs.&lt;&#x2F;p&gt;
&lt;p&gt;When I first learned git in a software engineering bootcamp, the framing was unambiguous. Git was esssential but could be dangerous. You could lose work, and you should stay inside a small set of safe commands. Above all, don’t touch rebase while you’re learning. If you got into a mess with it, you were on your own.&lt;&#x2F;p&gt;
&lt;p&gt;I eventually went to work on teams with other engineers, learned about collaborating on projects using git, added &lt;code&gt;git rebase -i&lt;&#x2F;code&gt; to my toolkit, and felt pretty accomplished. My daily git commands worked for the most part… except for the few times when I’d do a bad rebase, and think I lost hours of work, despair and start over. Or get into a really tangled merge conflict and just end up deleting the branch and pulling down again from the remote. I thought that was the way it was; git was powerful with some rough edges.&lt;&#x2F;p&gt;
&lt;p&gt;What changed this for me had nothing to do with git initially. Last year, I wanted to run two Claude Code sessions on the same repo on different branches at once, purely for parallelism. I didn’t know how to do this, because as far as I knew, you could only have one branch checked out for a repo at a given time on your computer.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;code.claude.com&#x2F;docs&#x2F;en&#x2F;worktrees&quot;&gt;The fix turned out to be &lt;code&gt;git worktree&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, a git feature rather than anything to do with Claude Code. It also addressed a paper cut I’d been living with for years that I didn’t even realized I’d had: the stash-checkout-checkout-back-stash-pop dance every time I wanted to spin up a quick branch for a typo fix when I was in the middle of other work. Fixing a problem I hadn’t let rise to consciousness made me wonder what else was there and what I should incorporate into my toolkit.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-three-commands&quot;&gt;The three commands&lt;&#x2F;h2&gt;
&lt;p&gt;I picked these three commands because they were the most directly useful ones that I found, and, in the case of git reflog, changed my view of git entirely.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;git-worktree&quot;&gt;&lt;code&gt;git worktree&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;You’re mid-edit on a quickstart change, and someone asks for a one-line typo fix on a different branch. The dance is &lt;code&gt;git stash&lt;&#x2F;code&gt;, &lt;code&gt;git checkout main&lt;&#x2F;code&gt;, &lt;code&gt;git checkout -b typo-fix&lt;&#x2F;code&gt;, fix it, &lt;code&gt;git checkout quickstart&lt;&#x2F;code&gt;, &lt;code&gt;git stash pop&lt;&#x2F;code&gt;. So much context switching and heaven forbid you get pulled away in the middle of it and have to remember where you were in the dance when you left off.&lt;&#x2F;p&gt;
&lt;p&gt;Worktrees solve this context switching problem. The command &lt;code&gt;git worktree add ..&#x2F;typo-fix main -b typo-fix&lt;&#x2F;code&gt; makes a second working directory on your machine, pointing at the same repo, on a new branch. Then you can cd into that directory (&lt;code&gt;..&#x2F;typo-fix&lt;&#x2F;code&gt;) and have two branches open at the same time, without needing to wind down&#x2F;stash your work from one task to switch to the other.&lt;&#x2F;p&gt;
&lt;p&gt;The worktrees share the same &lt;code&gt;.git&lt;&#x2F;code&gt; history, but all the files are duplicated and won’t clobber each other as you make changes in each branch. The quickstart edits stay sitting in the original directory, untouched. Open the new folder in a second editor window, fix the typo, push, and the original is exactly where you left it.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;When you’d reach for it:&lt;&#x2F;strong&gt; any time you’d otherwise stash or do the checkout dance. Especially worth it if you context-switch between branches a lot.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;When it shipped:&lt;&#x2F;strong&gt; 2015, a year before I started learning git.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;git-reflog&quot;&gt;&lt;code&gt;git reflog&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;A pronunciation note: it’s &lt;em&gt;ref-log&lt;&#x2F;em&gt;, short for “reference log.” I’d always pronounced it &lt;code&gt;git re-flog&lt;&#x2F;code&gt;, which made it sound made up and was a primary reason I didn’t investigate it any further. Based on the people who came up to me after the talk, I’m not the only one who did this. This is also the one command that made me rethink my whole vision of git and realize it’s so much safer than I’d been taught.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;git reflog&lt;&#x2F;code&gt; shows everywhere &lt;code&gt;HEAD&lt;&#x2F;code&gt; has been recently (every commit, reset, checkout, rebase, all of it). If you ever accidentally &lt;code&gt;git reset --hard&lt;&#x2F;code&gt; too far or drop a commit during a rebase, or get a tangled git history through a merge conflict, this is the command that saves you. The “lost” commit, or the good place you used to be but now aren’t, is sitting right there in the reference log. You can always go back to it. Just run &lt;code&gt;git reflog&lt;&#x2F;code&gt;, find the SHA for the commit&#x2F;place in time you want to return to, and &lt;code&gt;git reset&lt;&#x2F;code&gt; back to that place.&lt;&#x2F;p&gt;
&lt;p&gt;The piece that finally clicked for me, which I’d been missing when I was taught: HEAD in your repository is just a &lt;strong&gt;pointer&lt;&#x2F;strong&gt;. Doing something like &lt;code&gt;reset&lt;&#x2F;code&gt; or changes branches moves the pointer, but the commits themselves don’t go anywhere. &lt;code&gt;reflog&lt;&#x2F;code&gt; is the trail the pointer left behind, kept for 90 days by default.&lt;&#x2F;p&gt;
&lt;p&gt;The one catch is that it only saves &lt;em&gt;committed&lt;&#x2F;em&gt; work, and uncommitted changes blown away by &lt;code&gt;reset --hard&lt;&#x2F;code&gt; are gone. Commit early and often!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;When you’d reach for it:&lt;&#x2F;strong&gt; any time you think you’ve lost work or are in an otherwise irreparable state.&lt;&#x2F;p&gt;
&lt;p&gt;Neat trick: I also learned recently that you can even do things like &lt;code&gt;main@{one.week.ago}&lt;&#x2F;code&gt; to go back to where your main branch was a week ago. You don’t even need to find exact commits but can say things like “I knew this was working two days ago, let’s go back there.”&lt;&#x2F;p&gt;
&lt;h3 id=&quot;git-rebase-update-refs&quot;&gt;&lt;code&gt;git rebase --update-refs&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;You have a stacked PR (branch A off main, branch B off A). Someone lands a change on main that conflicts with A. The naive flow is to rebase A onto main, resolve the conflict, then rebase B onto the new A, where the same conflict is waiting for you to resolve a second time.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;git rebase --update-refs main&lt;&#x2F;code&gt;, run from the top of the stack, rebases the whole stack in one pass and moves the branch pointers along the way. You only need to resolve the conflict once, and then all the downstream branches get updated with the changes from main as well.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;When you’d reach for it:&lt;&#x2F;strong&gt; stacked PRs that should all get updates from a branch at the same time.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;When it shipped:&lt;&#x2F;strong&gt; 2022. I’d originally planned to talk about &lt;code&gt;git rerere&lt;&#x2F;code&gt; (it remembers a conflict resolution and replays it next time the same one shows up), and found &lt;code&gt;--update-refs&lt;&#x2F;code&gt; while writing the talk.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-one-i-think-actually-matters&quot;&gt;The one I think actually matters&lt;&#x2F;h2&gt;
&lt;p&gt;If you take one of these home, take reflog.&lt;&#x2F;p&gt;
&lt;p&gt;Worktrees and &lt;code&gt;--update-refs&lt;&#x2F;code&gt; are about ergonomics and make day-to-day tasks easier. Reflog is different; it changes the belief that screwing up means losing work, and that belief was what kept me out of the parts of git where the real power lives.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;for-the-record&quot;&gt;For the record&lt;&#x2F;h2&gt;
&lt;p&gt;A few QA questions where my live answer was either hand-wavy or wrong, because again, I’m not a git expert at all. Here are my corrected answers (and thanks for the opportunity to learn this with you all!)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;git-worktree-vs-multiple-clones&quot;&gt;&lt;code&gt;git worktree&lt;&#x2F;code&gt; vs. multiple clones&lt;&#x2F;h3&gt;
&lt;p&gt;Someone asked what the actual difference was between doing &lt;code&gt;git worktree&lt;&#x2F;code&gt; vs. just having multiple cloned versions of your repo. A clearer answer than whatever I gave:&lt;&#x2F;p&gt;
&lt;p&gt;Multiple clones give you fully independent copies of the repo, each with its own &lt;code&gt;.git&lt;&#x2F;code&gt; directory. Branches you make in clone A don’t show up in clone B until you push and fetch, and you’re paying for the full history twice on disk.&lt;&#x2F;p&gt;
&lt;p&gt;Worktrees share the underlying &lt;code&gt;.git&lt;&#x2F;code&gt; (the secondary worktree has a tiny &lt;code&gt;.git&lt;&#x2F;code&gt; &lt;em&gt;file&lt;&#x2F;em&gt; pointing back to the main one). Branches and commits show up in every worktree immediately, with no fetching and no duplicate storage. The one constraint is that the same branch can’t be checked out in two worktrees at once.&lt;&#x2F;p&gt;
&lt;p&gt;If you want two separate copies of the project that can drift apart, that’s &lt;code&gt;git clone&lt;&#x2F;code&gt;. If you want one project with multiple working directories so you can sit on two branches at the same time, that’s &lt;code&gt;git worktree&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;does-reflog-work-with-reset-soft-and-hard&quot;&gt;Does reflog work with &lt;code&gt;reset --soft&lt;&#x2F;code&gt; and &lt;code&gt;--hard&lt;&#x2F;code&gt;?&lt;&#x2F;h3&gt;
&lt;p&gt;Yes for both. I also want to walk back something I said on stage, because I had it backwards. Every form of &lt;code&gt;git reset&lt;&#x2F;code&gt; moves HEAD, which is the whole point of the command. The flags change what happens to the index and working tree on top of that:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--soft&lt;&#x2F;code&gt;: move HEAD. Index and working tree untouched, so whatever was previously committed shows back up as staged.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;--mixed&lt;&#x2F;code&gt; (the default): move HEAD, reset the index to match it. Working tree untouched, so changes show up as unstaged.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;--hard&lt;&#x2F;code&gt;: move HEAD, reset the index, and overwrite the working tree to match. Uncommitted changes (staged or not) are gone.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Reflog records every move of HEAD, so all three are recoverable in the sense that you can always put HEAD back where it was. The thing reflog &lt;em&gt;can’t&lt;&#x2F;em&gt; save you from is the working-tree damage &lt;code&gt;--hard&lt;&#x2F;code&gt; does. Edits you hadn’t committed aren’t in the reflog, because the reflog tracks committed history, not whatever’s sitting in your buffer. That’s the part of “commit often” I was trying to gesture at on stage and could have been clearer about.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;a-note-on-jujutsu&quot;&gt;A note on Jujutsu&lt;&#x2F;h3&gt;
&lt;p&gt;At the end of the talk I mentioned &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jj-vcs&#x2F;jj&quot;&gt;Jujutsu (or &lt;code&gt;jj&lt;&#x2F;code&gt;)&lt;&#x2F;a&gt;. I didn’t even realize for a while that there were other version control systems being developed; I kind of thought git was the one we’d settled on. But of course there were VCS systems before and people are making new ones too, revisiting these foundational concepts and&#x2F;or layering on top.&lt;&#x2F;p&gt;
&lt;p&gt;I find &lt;code&gt;jj&lt;&#x2F;code&gt; to be really interesting because it’s a more recent VCS that’s gaining popularity. It can live alongside a git repo, sharing objects and remotes, but rethinks the model. &lt;code&gt;jj&lt;&#x2F;code&gt; has no staging area, and you carry conflicts with you across operations instead of resolving them on the spot. The command surface is also much smaller. I’ve just heard of it and haven’t used it for real work, so I don’t have an opinion on whether it’s worth switching. But it’s on the list, and if you’ve been bouncing off git for years and it still doesn’t fit, that might be the direction to look.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;coda&quot;&gt;Coda&lt;&#x2F;h2&gt;
&lt;p&gt;I keep coming back to one specific story. After learning software engineering at a bootcamp, I taught there for a year. One day a student ran a rebase even though we’d warned her against it. She lost a day of work and asked for help. The response from the instructors, mine included, was “oops, oh well, we told you so,” and I still feel bad about it. Reflog would have walked her right back to where she was, but none of us knew about it, so none of us could tell her. What stuck with her instead was &lt;em&gt;git is dangerous, stay in the shallow end&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I gave the talk as a non-expert and that was sort of the point. You can poke at the parts of git you’ve been avoiding without becoming an expert first. The cost of looking was small for me, a search engine and an afternoon per command, weighed against nine more years of deleting repos and living with what I thought was lost.&lt;&#x2F;p&gt;
</description>
      </item>
    </channel>
</rss>
