Skip to content(if available)orjump to list(if available)

Oh Shit, Git?

Oh Shit, Git?

274 comments

·January 16, 2025

pitaj

Some changes I would make:

1. Always use `git switch` instead of `git checkout`

2. Avoid `reset --hard` at all costs. So for the "accidentally committed something to master that should have been on a brand new branch" issue, I would do this instead:

    # create a new branch from the current state of master
    git branch some-new-branch-name
    # switch to the previous commit
    git switch -d HEAD~
    # overwrite master branch to that commit instead
    git switch -C master
    # switch to the work branch you created
    git switch some-new-branch-name
    # your commit lives in this branch now :)
3. I'd apply the same to the `cherry-pick` version of "accidentally committed to the wrong branch":

    git switch name-of-the-correct-branch
    # grab the last commit to master
    git cherry-pick master
    # delete it from master
    git switch -d master~
    git switch -C master
4. And also to the "git-approved" way for "Fuck this noise, I give up.":

    # get the lastest state of origin
    git fetch origin
    # reset tracked files
    git restore -WS .
    # delete untracked files and directories
    git clean -d --force
    # reset master to remote version
    git switch -d origin/master
    git switch -C master
    # repeat for each borked branch

lalaithion

The disconnect between git's beautiful internal model of blobs, a tree of commits, and pointers to commits, and the command line interface is so wild. All of these recipes are unintuitive even if you have a firm grasp of git's model; you also need to know the quirks of the commands! To just look at the first one... wouldn't it be more intuitive for the command line interface to be:

    # this command exists already;
    $ git switch -c some-new-branch-name
    # is there a command that simply moves a branch from one commit to another without changing anything else? It feels like it should be possible given how git works.
    $ git move-branch master HEAD~

Certhas

The real "internal model" of git contains much more data/moving parts.

There isn't one tree of commits, there are typically at least two: local and remote

Branches are not just pointers to commits, but also possibly related to pointers in the other tree via tracking.

Stash and index and the actual contents of the working directory are additional data that live outside the tree of commits. When op says "avoid git reset hard" it's because of how all these interact.

Files can be tracked, untracked and ignored not ignored. All four combinations are possible.

lalaithion

None of these seem to preclude a command to make an arbitrary branch point to an arbitrary commit without changing anything else.

neild

The "move a branch from one commit to another without changing anything" command is "git reset".

"git reset --hard" is "...and also change all the files in the working directory to match the new branch commit".

"git reset --soft" is "...but leave the working directory alone".

rav

Actually, "git reset --soft" moves the current branch to another commit, without moving the index (aka staging area) along with it, whereas "git reset" (aka "git reset --mixed") moves the current branch AND the index to another commit. I really couldn't wrap my head around it before I had gone through "Reset demystified" [1] a couple times - it's not a quick read but I can strongly recommend it.

[1] https://git-scm.com/book/en/v2/Git-Tools-Reset-Demystified

lalaithion

git reset only works if you're on the branch you want to move, which is why every one of these example flows has you create your new branch, then do the reset, then switch to the new branch, instead of just allowing you to move a branch you're not on.

Terr_

> The disconnect between git's beautiful internal model of blobs, a tree of commits, and pointers to commits, and the command line interface is so wild

Something I heard somewhere that stuck with me: git is less less of a Version Control System, and more of a toolkit for assembling your own flavor of one.

JadeNB

> Something I heard somewhere that stuck with me: git is less less of a Version Control System, and more of a toolkit for assembling your own flavor of one.

That's how it is in principle, but it seems to me that there aren't that many different CLI "porcelains" in practice. Kind of like how Knuth figured people would essentially write their DSLs on top of plain TeX, not spend most of their time in giant macro packages like LaTeX.

pitaj

I prefer just using `git switch` because it's easy to remember the flags (and the position of arguments), but you're right, there is a simpler way:

    git switch -c some-new-branch-name
    git branch -f master HEAD~

DangitBobby

You should also be able to do

  git branch -f master origin/master

lalaithion

Good to know! Thanks for the tip.

jimbokun

Are there alternative git command lines that keep the beautiful internals, but implement a more elegant and intuitive set of commands to manage it?

dalia-reds

Check out jujutsu or jj (same thing). It's its own VCS, but it uses git as a backend, so it works with GitHub and other git integrations

maleldil

Another vote for jujutsu. No one else needs to know you're using it. You can think of it as just a different CLI for git (although you shouldn't mix them). I used to use third-party interfaces like lazygit, but I don't need them anymore because jujutsu _just makes sense_.

stouset

Seconded jujutsu. It's 100% git-compatible and one of those rare birds that is both more powerful and simpler to use in practice due to rethinking some of the core ideas.

jonasced

Lazygit has a terminal UI but might otherwise be what you're looking for: https://github.com/jesseduffield/lazygit

lilyball

The "move a branch" command is `git push .`. Yes, you can push to the current repo. I have a script called git-update-branch which just does some preflight checks and then runs `git push --no-verify . +$branch@{upstream}:$branch` to reset a branch back to its upstream version.

zahlman

> The "move a branch" command is `git push .`. Yes, you can push to the current repo.

Wouldn't that copy a branch rather than moving it?

assbuttbuttass

> is there a command that simply moves a branch from one commit to another without changing anything else? It feels like it should be possible given how git works.

git switch -C master HEAD~

rav

For move-branch: Use `git branch -f master HEAD~` if you're currently on another branch, or `git reset --soft HEAD~` if you're currently on master.

mrshu

Not trying to defend the choice of `git checkout` over `git switch` (and `git restore`) but they were introduced in v2.23 of Git [0], which was released about 5 years ago [1]. If you take a look at their help pages, they still include a warning that says

> THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.

Granted, it has been in there for basically as long as the command(s) existed [2] and after 5 years perhaps it might be time to no longer call it experimental.

Still, it does seem like `git checkout` might be a bit more backwards compatible (and also reflective of the time when this website was originally created).

[0] https://github.com/git/git/blob/757161efcca150a9a96b312d9e78...

[1] https://github.com/git/git/releases/tag/v2.23.0

[2] https://github.com/git/git/commit/4e43b7ff1ea4b6f16b93a432b6...

baobun

5. Teaching `git add .` as default to add changes to the staging area is not ideal. Show adding specific files instead has less room for subsequent "oh shit" and better.

zahlman

Learning about the `-p` option for `git add` was one of two things that revolutionized my Git usage. (The other was figuring out how to write effective commit messages.)

wodenokoto

This is the main reason to use a GUI imho.

ajross

True enough, but it does make for good practice with the index and splitting workflows later on when you need to clean it up.

I think there's space for "git add ." as a didactic step. It maps cleanly to the most obvious way to understand a commit, as "here's what I've done". Bootstrapping from that to an understanding of "commits as communication with other developers" will naturally happen over time.

baobun

Is not very compatible with printlog-debugging. I'd rather encourage devs to prod around as they go if it benefits them, which causes grief for either them or reviewers in the end if they've internalized what you just said.

Explicitly adding internalizes a personal review process as inherent part of the push process, instead of something you attempt to force on top later.

It's better with a collaboration workflow that limits the span of time with expected discipline, imo.

jaapz

Could you motivate why you suggest these? Why is `switch` better than `checkout`? And why not use `reset --hard`?

jopicornell

Not comment OP, but checkout has two very different uses merged into one: restoring files and switching branches. To not break compatibility, git has now switch and restore commands that make commands more readable and understandable.

You should avoid reset --hard because it will delete all your uncommited, and you could end up in situations where that's really bad. Using reset --keep will keep uncommited changes, and failing if any uncommited change cannot be kept.

teaearlgraycold

I just do

    git add -A

    git stash

    git reset --hard

    git stash pop

johnisgood

What do you mean avoid "reset --hard"? Why or why is it not enough in practice? I use it quite often, along with "alias git-restore-file='git restore --source=HEAD --'". It seems to work.

CharlieDigital

What's the problem with `reset --hard`?

pitaj

It leaves behind tracked files that were moved or deleted between revisions.

xk3

> 2. Avoid `reset --hard` at all costs

Sounds like you might be looking for `git reset --keep`

null

[deleted]

stouset

Rewriting these for jj users. I'm prefering long option names and full command names for clarity here, but all the commands have shortened aliases and all the option names have single-letter alternatives. `@` means "the current revision", `x+` means "the revision just after `x`", `x-` means "the revision just before `x`".

2. "Accidentally committed something to master that should have been on a brand new branch".

This doesn't really have an analogue. Branches ("bookmarks") only move when you tell them to. If you make a new commit on top of master, it doesn't point master to it, it just lives one past the tip of master. But let's say you accidentally moved master to include the new commit you shouldn't have:

    # set master to the previous commit (and reaffirm that
    # you're okay moving a bookmark backward)
    $ jj bookmark set master --allow-backwards --revision @- 

    # there is no step two, you're still editing the change you already were
3. Move a commit from one branch to another.

    # move the revision one-past-master on to our desired bookmark
    $ jj rebase --revision master+ --destination name-of-the-correct-bookmark

    # there is also no step two; technically we're not updating the bookmark
    # to point to the new commit yet, but this isn't something you'd do as rote
    # habit in jujutsu anyway
4. Fuck this noise, I give up:

    # list all the operations I've performed against the repo
    $ jj op log

    # restore to some previous known-good state
    $ jj op restore {id}
Bonus content, translated from the article:

> Oh shit, I committed and immediately realized I need to make one small change!

    # move the current edits into the previous revision
    $ jj squash
> Oh shit, I need to change the message on my last commit!

    # re-describe the previous revision
    $ jj describe --revision @-
> Oh shit, I tried to run a diff but nothing happened?!

    # there is no staging area, all your changes are part of the repo and there is no
    # staging area pseudo-commit; please understand that this still works elegantly
    # with "patch-add" workflows and does not imply that large change sets can't be
    # easily broken up into small commits
> Oh shit, I need to undo a commit from like 5 commits ago!

    # find the commit
    $ jj log

    # back it out
    $ jj backout {id}
> Oh shit, I need to undo my changes to a file!

    # find the commit
    $ jj log

    # restore the paths provided to their contents in the given revision
    $ jj restore --from {id} [paths...]
And finally there are a few things that are super easy/obvious in jujutsu that are far more annoying in git.

> Oh shit, I committed and many commits later realized I need to make one small change!

    # moves the changes in the current working copy into the revision provided
    $ jj squash --into {id}
> Oh shit, I committed and many commits later realized I need to make extensive changes!

    # sets your working copy to the commit provided; later commits will be
    # auto-rebased on top live as you make modifications
    $ jj edit {id}
> Oh shit, I need to reorder two commits!

    # does what it says on the tin
    $ jj rebase --revision {a} --insert-before {b}
> Oh shit, I haven't committed anything in hours but I need something from an interim change from like thirty minutes ago

    # look in the "obsolete log" for earlier iterations of the current revision
    $ jj obslog

    # restore the contents
    $ jj restore --from {id} [paths...]
> Oh shit, I made a bunch of changes but want them to be in multiple commits (e.g., patch-add workflow)

    # choose the parts to move out; you'll end up with two revisions, one with each half
    $ jj split
> Oh shit, I need to break out a change from my current work into a new branch off master

    # choose the parts to move out; you'll end up with two revisions, one with each half
    $ jj split

    # move the stuff I pulled out onto master
    $ jj rebase --revision @- --destination master

    # optional: name it; most of the time you wouldn't bother
    $ jj bookmark create new-name --revision master+
> Oh shit, I need to make three sequential changes but roll them out one-by-one. I also might need to make fixes to previous ones before later ones are rolled out.

    # author a new change on top of master and name it a
    $ jj new master
    …
    $ jj bookmark create a

    # author a new change on top of a and name it b
    $ jj new
    …
    $ jj bookmark create b

    # author a new change on top of b and name it c
    $ jj new
    …
    $ jj bookmark create c

    # edit a; nothing else is necessary to ensure b and c remain as descendants of
    # revision a
    jj edit a
    …

    # author a new change as part of b; nothing else is necessary to ensure c remains
    # up to date on top of b
    $ jj new --insert-before c
    …

    # point c at the new change
    $ jj bookmark set b

amanwithnoplan

Please kindly write one for a jj-specific issue: "my build vomitted out a bunch of files and I used any jj command before editing my .gitignore"

I've found myself using git to fix the mess in this particular instance.

stouset

    $ jj file untrack {paths-or-pattern}
Alternatively if you have a bunch of files spewed everywhere with no rhyme or reason which can't be globbed or enumerated reasonably:

    $ jj status | grep '^A' | awk '{print $2}' | xargs jj file untrack

hooper

One thing I really appreciate is that you can run `jj new master` at _any_ time to drop what you're doing and start a new change. The way jj handles the working copy, conflicts, and visible heads means there's just no need to think about uncommitted changes, unfinished conflict resolution, detached head, etc.. So many things that would get in your way just can't happen.

stouset

I haven’t thought about it at all but you’re right. It’s surprising how nice it is that I can enter a repo and `jj new main` without needing to remember any context whatsoever.

My post was a pretty naked attempt to showcase how much less convoluted basic operations are in jj vs. git and hopefully drum up some interest. Hopefully someone bites.

SebastianKra

We should start recommending UIs as the default way to learn Git. It would solve a third of these problems and another third wouldn't even come up.

If you later decide that the CLI is faster, go ahead. But first, people need to see visually how they can interact with the tree.

I like fork.dev, but most clients are pretty similar at this point.

koito17

Agreed that UIs generally provide a better UX for Git.

I use Magit and doing things like "abort cherry-pick" is discoverable in the interface itself and uses the exact same shortcut as the other "abort X" operations. If I had to use the Git CLI, I'd have no idea where to start.

Similarly, I've made mistakes in interactive rebases where I deleted a commit that shouldn't have been deleted. If I recall correctly, the start of every rebase creates a snapshot that is accessible from the reflog, so this is a safe way to revert changes from a rebase gone wrong. Magit's UI for the reflog is exactly the same as the UI for the log, so I was not lost when I saw it for the first time. With the Git CLI, I'd likely have no clue what's going on.

MrJohz

I've started recommending jj as a (git compatible) alternative to git, and one of the things I like about it is that the default action if you run `jj` with no arguments shows the relevant parts of the commit tree and where you are in it. This is a great reorientation tool, because you can see at a glance which commit you're working on right now, the branches and history associated with it, any other active branches in your repository, and for each you can see whether each commit had messages, changes associated with it, etc.

It's really powerful because it gives you precisely that visual layout that shows you what's going on in the repository, and what you're doing right now.

stouset

Holy hell, I’ve used it for months now and had no idea.

taberiand

I advocate this with every newbie to git - find a UI that works for you and use it over the cli. Barely anyone listens though, and then they struggle with the cli and make a mess of their branches and fall out of date with main and are scared of rebasing and have many other issues that are solvable with a drag and drop or similar in a UI. I figure it's a sort of masochism.

Izkata

> But first, people need to see visually how they can interact with the tree.

Interactive tutorial with tree visualization that has helped co-workers: https://learngitbranching.js.org/

alextingle

Thank you for posting that!

marssaxman

I have not used such a tool in a long time, and never with git: but my past experience with GUI frontends for version control was that they work fine when everything is working fine, but once you have a mess to clean up, nobody can help you.

It has generally worked better for me to use the same interface everyone else is using, even when that interface is awful, because that eases communication with the rest of the team. It also lets me take advantage of online troubleshooting resources, which all assume you are doing things the normal way.

nuancebydefault

The cli is faster if you know by heart but a real disadvantage is that it is hard to "see" what you did or what happened in the past. Good look finding where/whether an old branch got merged and find out if it is part of a release, using cli.

harrall

Is this really true? When I want to reorder commits, I just drag & drop in a GUI and I’m done. Or if I want to switch branch or manage a complex mix of rebases or cherry picks, it’s just 2 or 3 clicks. In CLI, by the time I’ve typed out the commit hash or locator, it’s already taken longer. And I type 130 words per minute.

dijksterhuis

> by the time I’ve typed out the commit hash

i don’t think i’ve typed out a full hash in … years? if i have i’ve used the short 8 character hash.

git rev-parse might be something useful to have a look at, especially when combined with aliases.

qalmakka

GUI git clients are amazing in the hands of expert users, but terrible for any newcomer that has to actually use Git (and it's not like a designer checking out the source once or twice a month).

The gripe I have is that unless you expose people to CLIs early on, they will just not learn how to use a CLI at all. Whenever something inevitably breaks badly due to GUIs abstracting away how git really works in favour of a nicer UX, they'll end up asking someone that know Git how to fix their mess. And then, it's too late - they already know how to be productive-ish with git and how to deliver something. They can't justify investing time into learning the CLI (especially if they're not that great with Powershell or UNIX shells) so they constantly keep leaning on a colleague instead of learning.

This is not an hypothetical scenario - this really happened regularly at a place I worked at. Innumerable internal training lessons on Git went wasted due to people forgetting everything immediately by using Fork instead of the shell, and then pestering a handful of senior devs. Once IT banned Fork people were forced to use the terminal more often, so they had to learn how to use git for good and actually retained that knowledge via muscle memory.

The adage I've learnt over the course of the years is that the majority of people will go to any length to avoid learning new stuff. It's mentally less tiring to either waste their time doing stuff in an unproductive way than learning new things. IMHO it's better to force people to learn stuff the "right way" early on than let them pick up bad habits and then having to correct them later.

FractalHQ

lazygit ! Just needed to throw out a mention because it’s an amazing tui for visual orientation, is super fast, and can be used with a mouse and keyboard (or just keyboard) from the terminal. I saw it mentioned on a thread here last year and have preferred it as a fast “oh crap I think I made a mess lets back up” tool over the more complex gui apps I’ve tried (almost all of them).

phtrivier

This will feel very weird in April 2025, when we celebrate the 20th anniversary of git.

I was there. And at some point I wondered if I should learn git, darcs, or bazaar, to replace SVN or CVS. Or did I try mercurial too ?

I wonder if the "GitHub" effect has basically killed the need for a newcomer in the space of VCS. Maybe at some point, the yak is shaved enough ?

steveklabnik

The GitHub effect is real, like all network effects, but that doesn't mean improvement is impossible. I've switched entirely to jj, and it having git compatibility means that I don't need to rely on anyone else to make that happen.

I am growing increasingly frustrated with various aspects of GitHub, and so I hope someday we all move past it too, but I don't think there's as simple of a path forward there.

jimbob45

SVN has always worked for me. You don’t have to “teach” people SVN because it’s intuitive and works just fine for the 99% case. I wish we would all stop larping as 1337 hackerz and just admit that git is overkill for the vast majority of people.

WorldMaker

Starting a new repo in SVN is find a server somewhere, if doesn't have SVN install SVN, if it does have SVN deal with whatever politics runs its trunk to find space/define a branch structure for you, and so forth.

It is its own footgun as well, but with git you can git init any directory you want and have a repo. Other than the learning curve of the not particularly well tuned CLI (and there are all sorts of graphical clients today and alternative CLI clients), it's very low barrier to entry to get something/anything in source control.

It's not just "larping as leet hackerz", there are real benefits to git over SVN. There are fewer barriers to entry for small teams and hobby devs, especially in a world with GitHub/GitLab/Sourcehut/and more, but also in the world where maybe a git repo never syncs with any other single machine or is hosted on the barest feature set SAMBA folder or is only ever using email-based PR flows.

git could be easier to teach. git could have a better out of the box CLI. That doesn't mean "git is overkill" especially from the point of view of "needing a whole central server setup just to store multiple versions of a file is overkill". Git is perhaps "underkill", it does the bare minimum as best it can in whatever environment you give it. It's intentionally dumb and stupid (hence its name, to be fair) and it's learning curve is as much because it is "too dumb" than because it "isn't intuitive". I've seen some "intuitive" UIs built on top of it. Another comment here remarked "git isn't a source control system, it's a low level toolkit for building your own" and that's not far from the truth and definitely not far from git's origins (and its name). That's a sort of "underkill". ("One day we'll finally build the right, intuitive high level API." That's basically how git was designed. You don't have to just take my word on that, you can search the old mailing lists for all sorts of interesting discussions and debates on "porcelain" options.)

zahlman

>Starting a new repo in SVN is find a server somewhere, if doesn't have SVN install SVN, if it does have SVN deal with whatever politics runs its trunk to find space/define a branch structure for you, and so forth.

Many years ago, SVN worked fine for me for single-user projects. I just made my own desktop be the server. But I wasn't publishing my code back then (it's not as if I would have have any effective way to tell people about it anyway). Now I have no idea how the equivalent of "pushing to origin" would have worked.

grandiego

I agree with your sentiment. I was kind of "forced" to use (and eventually fully migrate to) git because the IDE integration to SVN became more quirky every year. Instead, git is already integrated in IDEs these days.

orzig

Another anecdotal data point : I wasted a lot of time trying to figure out SVN too. I think I was using TortoiseSVN FWIW but basically gave up on my two person project

mardifoufs

SVN is more alien to me than git. Does that make you a LARPing epic hacker?

krick

Working with SVN was terribly slow on any decent size codebase.

hiAndrewQuinn

Clearly you have never needed code search across 50+ SVN repositories before, then. ;)

No, but seriously, I wrote https://andrew-quinn.me/reposurgeon for a reason. While I actually see some big benefits to SVN over git for areas with large binary assets like game development, the tool is just so ancient these days that it's just easier for me to convert SVN repos to git repos and then use those to figure out what I actually need to do. It feels a lot like working with Laplace transforms, translating and un-translating between two not quite equal ways of measuring reality.

That's not even considering that the kinds of companies still using SVN in 2025 tend to have a lot of code to wade through. Serious selection effects in play there -- mostly for good, to be clear.

dasil003

svn is perfectly fine and intuitive as long as you never want to branch and merge

bear8642

huh?

Svn can branch and merge - maybe bit more clunky but still works

paulddraper

Any improvement will have to be git compatible at the very least, e.g. jj.

argentinian

I believe I have a good mental model of what git does, but I never remember commands' arguments to use when they are moderately complex. I mean that the commands are not discoverable or easy to memorize.

I don't know if that's because the text UI is bad, or because it's simply difficult to explain with text what to do to manipulate a tree.

stackghost

Both.

Manipulating complex trees via text is not easy, but the text UI is objectively bad:

https://stevelosh.com/blog/2013/04/git-koans/

dgfitz

I’m pumped a search for hg+mercurial had hits in this thread. I am and will continue to be completely blown that hg lost the dvcs wars. It’s a better tool.

gmueckl

It is the unquestioningly better tool right now. It's available, stable, battle-tested and it's actively supported. It's a case of "being the change you want to see". Just use it. Claims that it lost are counterproductive. The implicit deterrence from those statements is what is actively keeping Mercurial's adoption low.

Hosting is available at least from Sourcehut and heptapod.host.

I'm running a private Heptapod instance (Gitlab fork with direct Mercurial support). It just works.

greazy

Are there any public forges that support hg?

gmueckl

Have a look at heptapost.host for paid hosting and foss.heptapod.net for free public hosting for OSS. Is that what you are looking for?

romaniitedomum

> Are there any public forges that support hg?

Sourceforge, Sourcehut, and GNU Savannah all support Mercurial.

dgfitz

I use it every day!

thatsthejoke

That sounds interesting. Does anyone have a GitHub link?

paulddraper

It's the slower tool.

guenthert

That was relevant to Linus in the early 2000s, but then, we didn't have NVMe SSDs.

paulddraper

It is still noticeably slower on a large codebase.

dgfitz

I don’t mind if it is slightly slower, if that is even true anymore.

Turns out while hg clone, pull, or whatever is running, I can do other things!

fschmidt2

Modern scum (members of depraved modern culture) always prefer what is worse over what is better, so of course they prefer Git over Mercurial. Anyone who wants Mercurial hosting can use my hosting: https://hg.reactionary.software/

behnamoh

Git is one of those technologies that I never got to wrap my head around of, because in so many ways it doesn't follow intuition and unless you have been using it for a long time, for literally every action you would probably have to Google or use the man page of the command.

dmd

As everyone knows, though, Git gets easier once you understand branches are homeomorphic endofunctors mapping submanifolds of a Hilbert space.

jimbokun

> homeomorphic endofunctors mapping submanifolds of a Hilbert space

Has to be easier to understand that then all the arbitrary git commands.

sampo

Git commands, while they may be cryptic, actually mean something. Whereas that was just gibberish made by putting random mathematical words together.

(There is a similar sounding joke about category theory, "A monad is just a monoid in the category of endofunctors" but this sentence has a mathematical meaning.)

riwsky

Unless you’re in a detached HEAD, in which case it’s xylomorphic to the left-aligned Galois group of R^3.

wbl

That's the Darcs joke and you told it somewhat incorrectly.

behnamoh

Oh geez now that clarifies everything, how could I miss that?

stephen_g

Did you start with Git or have you used other VCS systems before? I started with SVN and then coming to Git, there were obviously things to learn about how it was different but honestly it felt to me like it made things easier in many ways (since I'd experienced the horror of a very large codebase in SVN with lots of branches, and trying to track those and merge back together - git is so much better at that, it's crazy)...

I can see how it would be a much bigger learning curve if people come straight to git, but it's still hard for me to understand where the blocker is that so many people complain about using it...

nomel

I was trying to delete a file from history yesterday.

The built in way (filter-branch) pops up a warning, with a long delay, saying you should hit Control+C and go download a third party python script called filter-repo...

PaulDavisThe1st

Possibly consider that "deleting a file from history" is rather far outside the norm or recommended practice for git (even though it is, of course, entirely possible)

nomel

> even though it is, of course, entirely possible

I take the more realistic perspective: until git makes it impossible to commit something that shouldn't have been, like a secret, then deleting a file from history is a fundamental requirement of git.

bitwize

It's ok mate. Hackernews says we should be using jj to manage our rewritten-in-Rust code.

iimblack

Since you brought it up, I personally switched to jujutsu and prefer it greatly. I regularly help coworkers deal with issues in git and keep dropping hints like `in jujutsu this would've been done way easier like this!`. Nobody bites yet since I think most of them don't want to use the CLI but maybe someday if enough people keep talking about it the inertia will get to the point that we can get some really slick GUIs for jj.

behnamoh

Haha, I'm so glad I didn't fall for the whole rust thing.

stouset

Both you and GP, please aim higher for comments on HN.

frakt0x90

I'm not proud of it, but my #1 "Oh shit" git operation is to just delete my local repo, reclone, and reapply the changes. Works really well for me 95% of the time. The rest I ask dev ops guy to help.

spokaneplumb

I've been using Git for almost 15 years, and have twice built programs/products that use Git internally to achieve certain results (that is, the program/product itself uses Git for things, not just using Git to manage the source code for the program/product) and... sometimes before doing something a little gnarly in Git I'll still just do "cp -R .git ../git-backup" or something like that, so I can replace my entire .git dir with an older copy if I screw it up too bad. It's a ton faster than figuring out the right way to un-fuck any particular operation or set of operations.

fragmede

Reflog is your friend.

    git break-my-shit
    git reflog
        ... output saying where you were before things broke
        ... grab the good commit sha
    git reset --hard good_commit_sha_from_reflog

spokaneplumb

The copy-the-.git-dir trick works for worse issues than can be solved with a single reset --hard. Damn near anything, really, as long as you haven't touched any remotes. It also works if you don't remember/understand how you broke it, where it's broken, or which state you need to try to reset to.

compiler-guy

And yet up above we have others recommending to never, ever, use `git reset --hard ...`.

wruza

This should be a built-in

  git unshit
Or

  git add --unshit -f ~HEAD^^
If you’re using git version <= 2.844.

maleldil

Jujutsu has `jj undo`, but which undoes whatever was your last jj command, regardless of what it was. It makes much more confident to do an operation I'm uncertain of. And if I regret something many actions down the line, you have `jj op log` (a better reflog).

zahlman

If you `jj undo` a second time, does it redo (undoing the undo), or does it back up another step?

blharr

Such a simple operation, but it'd probably fix 95% of problems I've had with git.

"Uh oh, I tried to checkout a branch from remote by doing `git checkout origin/some-branch` instead of `git checkout some-branch` and made a couple source changes. Now I'm in detached head state. What is detached head ? I have to stash my revisions? Can I make a new branch with the same name or do I need to delete the origin/some-branch that I'm on?"

When you could be able to just revert the "operation" and check out the correct branch, that's amazing.

globular-toast

The one thing I wish people would internalise about git is it's an append only data store. To get work into the store, you commit it. Once in the store there is not a command in git that can remove it.

This is how the reflog works. Whatever you do, you can get back to a previous branch state and restore the work from the data store.

But git can't help you if you don't commit. So just commit. Commit all the time. If you think it's too much overhead and you always forget to commit, fix your tools. If you're writing code all day you should be committing at a minimum every hour, in general.

sundarurfriend

Silly comment, but:

Every time I see `git reflog`, I see it as re-flog. It's possible that's even the reason I never remember the command, I subconsciously can't bring myself to think of it as "reference log". Instead, it's asking git to sell something cheaply, again.

yonatan8070

I have that with fstab

dgfitz

It will always be f-stab in my head, can’t undo.

tantalor

I'm not a git user, but stuff like this really drives home the idea that "git commit" is meaningless, the only thing that matters is when your commits are pushed or merged.

It's like saving a textfile. Do you write a little message every time you save a file? No that's silly. Just move on.

trashburger

If I'm saving changes that were done because of a arduous debugging journey and other people are likely to have to refer back to it, yes. In fact, forget little; the smaller the change is, the bigger the text. Some of my changes have 2-3 paragraphs for a 2-3 line change because of the impact.

kstrauser

Same here. If it took me a week to figure out why to tweak a couple lines of code, I'm going to be explaining it.

(Although also/mainly in the comments if it's something I worry someone might accidentally change back later.)

Vampiero

It seems painfully obvious to me that local and remote commits serve different purposes. Local commits are a way to create checkpoints when everything compiles and works and you can move on to the next step. That way you can stash your changes and go back to a working state when you screw things up. Then, before you push those changes, you reset them and split them into proper commits. That way the history is all nice and clean on the remote and it's not just a bunch of "wip"s.

Or you can just squash-merge your PRs and reap both benefits.

nejsjsjsbsb

I have no pride. I push my dumb commits that chronical my weird journey full of sub-junior mistakes to get to the final state.

MetaWhirledPeas

I think people would generally have an easier time with git if they didn't try to fix their commit histories and instead just let their past mistakes be known. If you make a bad commit in git the best solution is to follow it up with a good one that fixes the problem. Squash commits on PRs help too.

Nullabillity

Even better, `git merge --no-ff` your PRs and use `git log --first-parent` for the simplified "as-if-it-was-squashed" history.

globular-toast

Commit is what causes git to make a copy of the file(s) internally. It's vitally important. But there is no point typing in silly messages like "more fixes" etc. What I do is make an initial commit with something like "(WIP) too feature", then keep doing amend commits until I'm happy, at which point I remove the "(WIP)" from the message.

zahlman

>It's like saving a textfile. Do you write a little message every time you save a file? No that's silly. Just move on.

Do you write a little message about your day every time you go to sleep?

I actually don't, and maybe you don't, but plenty of people do.

I think of the Git revision log as much like that sort of diary, offering the same sorts of advantages. It's more organized than having a separate TODO list document; and it encourages you to describe what you've already done in a clear readable way, too. When you're trying to figure out how far to roll back, it may be useful to understand what you were thinking at the time of the commit. And if something turned out to be the wrong approach, it helps you do the post-mortem.

And, of course: if your unmerged, unpushed work is organized into separate changes that have separate (and labelled goals), and part of that work turns out to be good and part bad, it becomes vastly easier to preserve the good parts.

shuntress

Well, it is all local until you push so you can do whatever you want.

With that said, it obviously is not meaningless at a technical level because without the commit there is nothing to push or merge. On top of that, at a non-technical level it can be extremely helpful to record some plain-english prose to describe why you are changing something. If you find yourself doing that too often you need to narrow your definition of what constitutes a "change" and/or be more discerning about what you work on simultaneously.

Out of curiosity, if you do not use git, what do you use for version control and change-management?

julianeon

I actually use git for my todolist/diary and I have an alias for when I want to save which does git commit -m "whatever." Basically I do that so I can view the updated version in the mobile app.

mfashby

Related, you can get this as a nice printed zine https://jvns.ca/blog/2018/10/27/new-zine--oh-shit--git-/

user4511

Oh how I wish more devs would just read the documentation for the tools they pick. I read the git documentation end-to-end. Never really had any issues with it and most commands feels natural to use.

blharr

If you memorize the documentation I suppose there's no problem with it, but even then there's confusing things from the beginning design approach.

Like, reset vs revert vs restore, using three similar starting synonyms for different operations.

Reset is particularly confusing because it sounds incredibly destructive, but if you do a soft 'git reset', it just moves the "changes to be committed" to be not staged for the commit.

Then, if you change a single flag (--hard) it is destructive, instead erasing all those changes from your disk.

And there's a reason nobody reads the docs, because they're laid out confusingly at first glance to an unfamiliar person.

If I go to git-scm.com, click docs, the first suggestion is the reference manual. The first guide is on "gitattributes" which gives no philosophy or context as to what it is even used for. Ok, maybe, I'll just check the complete list of git commands... and it starts talking about porcelain and plumbing. Nope, thats not what I need. Maybe try the link "git" under Setup and Config. There the description finally links to a useful "gittutorial" page.

The gittutorial even has some confusing or specific terminology that makes git seem hard. From the beginning of importing a new project "Assume you have a tarball project.tar.gz with your initial work"... why do we need to refer to tar at all, and it doesn't even do the credit of explaining the tar xzf command or what that tarball is. It could easily say 'assume you have a directory', which is the more common case and be 200% more simple.