Email or username:

Password:

Forgot your password?
89 comments
Rylie ✨💖 Pavlik

@b0rk oh this is great!

Some of the confusion I think comes from git adding less confusing subcommands but not removing the old ones. So like, "git switch" can switch which branch you're on, a simpler checkout, but checkout still exists. I am guessing "git restore" is similar, because that didn't exist in the past I think, and the things you mention for it, I have done painfully with "git reset".

So now there is two ways to do things, an easier one, and one more people know...

Rylie ✨💖 Pavlik

@b0rk I manage to remember the order of commits vs files because sometimes you have to put the files after -- to indicate you're done with options and things that aren't files, so I tend to always include -- when doing branch and file stuff to help me remember the files must always go at the end. (I feel like I've used other commands that did this thing with -- as well.)

Arne Brasseur

@b0rk thank you for putting this together. Instant classic!

leif

@b0rk the dots thing is worse than what you described: it's inconsistent between log and diff matthew-brett.github.io/pydago

Janne Moren

@b0rk
Re: heads - this is how I understand them:

A "head" isn't a branch; it's the tip of the branch. Branches are like Hydras, with a head at the end of every branch.

HEAD is like a cursor in database operations or in an editor, or your current directory. It's where you are and your operations take effect. Usually it's the commit at the head of the current branch, but if you move it to a non-tip commit, it's no longer at a head - HEAD has detached from the head of the branch.

Peter Nerlich

@jannem @b0rk Came here to say that, essentially. My mental model is that there is no fundamental difference between HEAD, branches and tags – all are refs, fancy names for human navigation, which are pointing to a commit. Git uses HEAD to keep track of where I am, meaning which commit my working directory should be compared to and any new commit should reference as its parent. If I came to the current HEAD by checking out a branch, that branch ref is now also being updated to point the new commit, while tags always remain where they point to.

@jannem @b0rk Came here to say that, essentially. My mental model is that there is no fundamental difference between HEAD, branches and tags – all are refs, fancy names for human navigation, which are pointing to a commit. Git uses HEAD to keep track of where I am, meaning which commit my working directory should be compared to and any new commit should reference as its parent. If I came to the current HEAD by checking out a branch, that branch ref is now also being updated to point the new commit,...

Julia Evans

@peternerlich @jannem thanks but I know all of this, I chose to explain it a different way intentionally

jleedev

@b0rk

> Git doesn’t have any built in mechanisms to track how recently you fetched

The reflog does keep dates! But,

1 — you have to ask for them:

¶ git reflog --date=human

Then instead of HEAD@{1} etc, you get HEAD@{time}

2 — By default, `git reflog' shows you the reflog of HEAD, but git keeps other reflogs, so you can see fetches of a remote-tracking branch which you did not check out.

¶ git reflog origin/main --date=human
or,
¶ git reflog --all --date=human

@b0rk

> Git doesn’t have any built in mechanisms to track how recently you fetched

The reflog does keep dates! But,

1 — you have to ask for them:

¶ git reflog --date=human

Then instead of HEAD@{1} etc, you get HEAD@{time}

2 — By default, `git reflog' shows you the reflog of HEAD, but git keeps other reflogs, so you can see fetches of a remote-tracking branch which you did not check out.

Screenshot of `git reflog origin/main --date=human`, showing some commits, such as
 5d5c28b refs/remotes/origin/main@{Thu Oct 19 14:59}: pull: fast-forward

(and so on)
Lea Rosema

@b0rk oh yes. Most confusing to me is the "ours" and "theirs" terminology when resolving merge conflicts.

Tully

@lea @b0rk The trick with "ours" and "theirs" is to remember that git was written by Linus, whose workflow involves rebasing other people's patches onto his `main` branch.

So, despite the fact that you had some feature branch checked out when you started, "ours" is main (because you're Linus) and "theirs" is the feature branch (written by the person about to get cussed out on the LKML).

Zuri (he/him) 🕐 CET

@lea @b0rk It gets worse when this is not a `git pull` but a `git stash apply` for example

Juan C Nuno

@lea @b0rk When IntelliJ IDEA throws up a three way diff I slowly back away

Stephen Darlington

@b0rk Nice work! For a tool that I used almost every day, it's odd how much I have to search for answers on how to use git...

MarkD

@b0rk Very nice. Git is a complex world for something that wants to manage source code changes, that's for sure.

I noticed what might be a minor error in the fast-forward discussion. You mention "doesn't have X and Y" but there's no "Y" in the aforementioned state.

Anthony

@b0rk another deep arcana is changing remote URLs - I memorized this years ago when teaching H.S. kids who would often miss the “fork” step of an assignment and had cloned the template repo instead of their own copy.

Anthony

@b0rk more recently, have used this to move a repo fully from bitbucket to GitHub

Colby Russell

@stormsweeper that's the right way to do it. Pre-emptive forking is only a thing because GitHub want to exploit the same network effect that social media companies were milking, so they decided to force every prospective contributor not just to create a superfluous fork, but insist that it be hosted on GitHub, too, and use their proprietary PR system.

Colby Russell

@stormsweeper in a more perfect world, you'd clone the original, make your changes, and just submit that (a patch). But GitHub refuses. Can't grow the garden that way.

Osma Suominen

@b0rk Thanks a lot for this, you did an amazing job here!

arclight

@b0rk I still refuse to accept that rebase is a legitimate operation. It has no equivalent in other VCSs even other DVCSs. It's frequently cited as a solution for all manner of problems but I've never seen a coherent single-sentence description of it. Every example is so mired in git minutiae that it's not worth pursuing. It's just safer and easier to extract critical changes, wipe a mangled repo, and reapply/merge changes than waste the time and effort deciphering git. Catering to the gratuitous complexity of the tool is not a good use of my time.

@b0rk I still refuse to accept that rebase is a legitimate operation. It has no equivalent in other VCSs even other DVCSs. It's frequently cited as a solution for all manner of problems but I've never seen a coherent single-sentence description of it. Every example is so mired in git minutiae that it's not worth pursuing. It's just safer and easier to extract critical changes, wipe a mangled repo, and reapply/merge changes than waste the time and effort deciphering git. Catering to the gratuitous...

aburka 🫣

@arclight @b0rk You're probably not looking for answers here, but you kind of *did* give a single sentence description of rebase 😂 it's exactly what you said about reapplying the changes you want, except without making a new clean repo, and it's using the original commits. Of course there's all kinds of bells and whistles and finger traps, but that's the gist.

Graydon

@arclight @b0rk Rebase recreates all the commits in a branch as though the branch had been created from the commit to which it is being rebased. (While leaving the originals in existence!)

Last night, I rebased to pick up a couple sprints worth of changes from other people because my particular task took way too long for Reasons. No manual conflict resolution seems like a win. Whole thing took seconds, and doesn't assume I know where the critical stuff is in other people's work.

Graydon

@arclight @b0rk In general, it's important to remember that git was designed to handle projects that are much too big to understand. A lot of what it does makes no sense if you don't recognize that the point is to handle stuff a[n individual] person cannot understand due to volume.

arclight

@graydon @b0rk And that's a big challenge for my projects; I don't have the same problems as the Linux kernel developers have so the tool really isn't suitable for my use. It's incredibly difficult to identify and isolate the relevant parts of git. You're faced with full complexity at the outset whether you need it or not. Fine for kernel devs, a nightmare for everyone else. Project scale should also be a warning sign amazon.com/Limits-Software-Peo

Graydon

@arclight @b0rk Don't agree with the "nightmare for everyone else", not anymore than the shell is a nightmare. Some instruction is required. It's one of those cases where the more you know going in the more instruction is required, even; git is rough on autodidacts who want to be able to infer function. I had a lot less trouble when I started using the git-scm documentation and any workplace ought to have a "this is how we use git" explainer in wide currency.

arclight

@graydon I'll agree with the latter point - a "how we use git here" guide is essential. I just remember trying to understand git in the late 2000s and how git terminology seemed to intentionally and unnecessarily differ from every other VCS. Docs were sparse and impenetrable and I wound up learning Mercurial instead because it was learnable. I hate that git is conflated with DVCS in general because it is such a niche tool that doesn't translate well outside its niche. We've had commit, branch, and merge forever - why a whole new vocabulary was needed is beyond me. But that's not surprising since I'm not a kernel developer.

@graydon I'll agree with the latter point - a "how we use git here" guide is essential. I just remember trying to understand git in the late 2000s and how git terminology seemed to intentionally and unnecessarily differ from every other VCS. Docs were sparse and impenetrable and I wound up learning Mercurial instead because it was learnable. I hate that git is conflated with DVCS in general because it is such a niche tool that doesn't translate well outside its niche. We've had commit, branch, and...

Graydon

@arclight Something that is free and sufficient tends to win, and git's selling point is that your source control problem is not as challenging as the problems it was created to solve.

The terminology mostly comes in because it isn't doing commit, branch, and merge, it's using a distinct abstraction, and the words for the abstraction weren't there so people just grabbed whatever didn't run away quickly enough. I agree that the result is horrid confusing. (git-scm docs make a heroic effort.)

Graydon

@arclight Which is why the "here are our standard patterns" seems so important; it gives people some place to stand on.

I've had good results from that in the past but it is definitely setup work for the people part of the dev environment.

Thomas Broyer

@b0rk Currently reading…

It seems to me like "fast forward" would be easier to understand if not "duplicating" de commits between the branches (main and origin/main).

```
A - B - C (main) - D - E (origin/main)
```
You can just move the main label/head forward to origin/main. You can just "play" the commits forward to bring main to origin/main.
This is reminiscent of oldish cassette/VHS where you "fast forward" to reach a timecode.

Simon Tatham

@b0rk I should have thought of this *before* you published (sorry) but the .. vs ... section reminds me of one: log and diff kind of disagree on which is which.

"git log foo..bar" shows changes on bar that aren't on foo, whereas "git log foo...bar" shows changes on _both_ sides.

But "git diff foo..bar" asks for a diff that shows foo stuff *and* bar stuff (one being removed, one added), whereas "git diff foo...bar" diffs against the merge-base, showing only the changes on the bar side.

Juan Luis

@b0rk OMG so ours/theirs works backwards for rebases? This explains so many things...

Fanny Matrice

@astrojuanlu @b0rk we usually learn this the hard way, I think...

Greg

@b0rk I was thinking, "this could be turned into a zine ppl pay money for..." and oh hey, store.wizardzines.com/products . Maybe Volume 2? Great work on this post and thanks for listening to the crowd & doing that research.

Brian Fenton

@b0rk my understanding of "git pickaxe" is it's the same as "git's meat cleavers" in Travis Swicegood's old talk from more than a decade ago. In other words, various flavors of interactive rebase (`git rebase -i`)

Brian Fenton

@b0rk my mistake, he also covers bisect, normal rebase, and amending commits speakerdeck.com/tswicegood/git

... and a quick google search shows I'm completely wrong 🤦🏻‍♂️ Apologies. It's a way to search the commit log for when a specific term was added/removed/modified philandstuff.com/2014/02/09/gi

Julia Evans

@brianfenton oh it's `git log -S`? I love that flag, no idea it was called a pickaxe (???)

Brian Fenton

@b0rk apparently? no idea how that came to be, but I think it's one of those plumbing (non-porcelain) that escaped into the wild git-scm.com/docs/gitdiffcore#_

Rich Puchalsky :anarchism:

@b0rk

Git can't be explained because it is an incredibly badly designed program that routinely fails in real world use. I've been a programmer for 4 decades and I hate projects that make me use that horrible pile of junk

Grant Hollingworth

@b0rk This is great! I finally internalized the two uses of `git checkout` when I realized the `--` before the file list ("pathspec") was optional but more clear. That is, `git checkout main -- file1 file2` is checking out main, but only for those two files.

I will probably never remember `switch` and `restore` at this point.

Andy

@b0rk The bit about “Your branch is up to date with ‘origin/main’” was a HUGE eye opener for me, I've been so flummoxed by this message as a fledgling Git user, and that knowledge explained everything.

Jakub Narębski

@b0rk

- the **dots thing** working for diff is, I think, to be able to copy and paste line from `git fetch` output to git

- **refspec** for fetch tells Git how to map references in remote repository (usually branches or branch, like refs/heads/main) into references in your repository (usually remote-tracking branches or branch, like refs/remotes/origin/main); compare how they look like when you clone the repository vs when you clone repository in the _mirror mode_.

Julia Evans

@jnareb yeah i understand what the refspec does in theory but what I don't understand is -- does ANYONE understand the syntax for the refspec and change it from the default? why? is it worth my time to try to understand more deeply than "yea it's some nonsense just ignore it”?

Jakub Narębski

@b0rk I use it to push **all** my local feature branches to remote with `git push origin :` - here `:` is a special kind of refspec

Before `git push` got `--delete` option it could be used to delete branch in remote repository with `git push origin :branch-to-delete` (push empty into branch).

If GitHub/GitLab/... is configured so that 'main' branch is protected and you cannot push there, you can use `git push origin main:other-name` and then create pull request/merge request.

Not usual cases.

Jakub Narębski

@b0rk Also, I think if you want to share git-notes (for example to add information to existing commits, such as that it was the cause of the bug, and was fixed later), or git-replace (which can be used to connect current history with historical repo converted from some other SCM), you need to hand-craft refspec - as they both use references which are not branches.

git-scm.com/docs/git-notes
git-scm.com/docs/git-replace

bits

@b0rk omg this is so amazing. Adding it to onboarding material along with a bunch of your other work (including brag docs).

Jakub Narębski

@b0rk “remote-tracking branch” vs “branch that tracks a remote”: one tracks branches in remote repository, one tracks the remote-tracking branch ;-)

anarcat

@b0rk great post, thanks! one note: i'd be careful around saying that "`git reset --hard` and `git restore .` do basically the same thing": it's true if you read this literally, but the second you add an argument to `git reset --hard`, that goes out the window and you're moving the branch pointer! very dangerous, as you can lose commits!

C.N.

@b0rk very nice! I don't use git a lot, but while reading your explanations, a question popped up that might be good to answer if it fits: In a merge commit, is the first parent ours or theirs?

I just read the explanation of "ours", then HEAD^2 was introduced, that seems pike it should be expressible in these terms?

Ross Light

@b0rk If it helps any, Git calls things like `HEAD^^^` "revisions": git-scm.com/docs/revisions

It's a little weird though, because it's usually a "revision argument" or "revision parameter", and they can name Git objects that aren't commits (like blobs, trees, or tags).

bbhtt

@b0rk I'd love to know more about sparse checkout. I heard of it when Github did the blog post but didn't look too closely so it went over my head.

I'd rarely need to use it but sparse cloning seemed to be useful.

Julia Evans

i'm always a bit frustrated when people look at all this confusing terminology and respond "oh yeah but all you need to understand is that everything in git is a reference". Like -- sure! That's a useful thing to understand!

But there are a bunch of different kinds of references (branches, tags, remotes, the stash) and git's commands affect them in different ways. You actually do need to understand how they all work and git’s terminology doesn't do a lot to help you do that.

cliffle

@b0rk I'm really glad you're compiling all of this, I've always felt like git managed to have most of its terminology designed exactly backwards. It confuses _everyone_ at some point.

Quixoticgeek

@b0rk "all you need to understand is everything is a reference"

My reply would be "so all I need to do is read all the source code to understand how to use this ?"

Alas far too common in the foss world these days :(

Rachel Rawlings

@b0rk "You have to understand everything is a reference" as if references haven't been biting C programmers in the face for nigh forty years.

Kevin Granade

@b0rk right, tags for example act very differently to other kinds of git references, and they have special metadata you can attach to them.
"It's all references" can help when understanding branches, but it falls apart when it's time to build a tagging workflow into your release process.

Graydon Hoare

@b0rk Yes! And that there are always at least 3 copies of all references in play at once (yours, the remote's, and your most recently-made copy of the remote's). The mental model is quite a lot to internalize, as powerful as the resulting tool may be.

Jeff Miller (orange hatband)

@graydon @b0rk Gollum Wiki was a puzzle at first because it doesn't look at the checked out workspace but only the local branch head. Pay no attention to the filesystem behind the curtain...

Luis Villa

@graydon @b0rk As someone who honestly has stopped contributing anything that can’t be done via GH web interface in part because I find git monumentally confusing, I wonder if jj (which looked very promising to me, a no-longer-active-dev) actually does what it promises to remedy some of these mental model challenges? Or is it off on the wrong track?

github.com/martinvonz/jj

Julia Evans

@luis_in_brief @graydon i haven't figured out how to get jj to compile yet but I want to try it out! definitely curious about it

Dan W

@b0rk The thing that gets me about git terminology isn't any one specific term (though I have problems with plenty), it's the lack of a consistent metaphor.

Are we operating on tree that have trunks and branches and pick cherries? Are we on a river where we go upstream and downstream? Are we adventurers that need to reset our basecamp and merge with other groups? All of the above!?

Joachim Wiberg

@b0rk everything seems easy when you know how it works. I’ve used got for a loooong time and I’m not ashamed of admitting I don’t really know what I’m doing.

Drew Breunig

@b0rk Understanding everything is a reference does not explain the at least 4 different metaphors going on in their UX!

Drew Breunig

@b0rk Upstream/downstream, head, branch, fast-forward, checkout, base...Ok, at least 6.

Drew Breunig

@b0rk Gonna fund a new X Prize: identify a unifying metaphor for git.

gkrnours

@b0rk in a way, a head is a tag floating at a top of a branch while an actual tag have sunk in the ground (truth?). The stash are also tag to free/lost bit of code but to me, stash being long lived feel more like an implementation mistake or a quick fix to a practical problem than a real feature

Sinjo ✌🏻💖

@b0rk 100% Also the way the CLI refers to references can be confusing as heck.

I didn’t reply because I saw someone else already mentioned it, but ^ and ~ always trip me up. Like I know ^2 is something about the second parent branch, but I can never remember which way round the branches are.

rhempel

@b0rk hey thanks so much for posting this, and I'm going to assume you are OK with sharing it :-)

This is a fantastic resource for developers like me that have years of deep git experience and a solid mental model - and struggle with explaining git new newer developers, or developers that are just now moving from svn or that have never used git.

It is so awesome to have a clearly written explanation for the most confusing parts of git - will be my go to resource!

siedlerchr

@b0rk Great idea! Personally, I found this ebook very helpful to understand the concepts from git: jwiegley.github.io/git-from-th

Orc

@b0rk that’s a terrific explainer, but oh lord every time someone lifts the lid on the swiss army kludgepot I more and more want to run screaming back to cvs!

slackline

@b0rk Another great article, thanks for writing and sharing.

With checkout the '--' can be used between a branch and a file on that branch and it will copy that file from that branch to the current one. Kind of like cherry-picking files rather than whole commits.

git checkout -b new_feature # Create and checkout a new branch
git checkout old_feature_branch -- path/to/file/in/old_feature_branch

#git

Larry O'Brien

@b0rk This is very helpful for git newcomers. The one that always terrifies me is getting "yours/theirs" wrong.

Go Up