@b0rk You’re on point here. One criticism of git is that its model is too complex. Even if the complexity is warranted, an issue layered on top of that is inconsistent terminology — it makes the model seem even more complex than it actually is. The uses of HEAD are good examples. Another one is “stage”, “index”, and “cache” which all refer to the same thing.
the more I write about git the more I think it's important to be a little critical of git's design and terminology choices when teaching it. A lot of things in git are counterintuitive and it's important for people to know where to be careful.
but it's important to me to be really specific about the critiques, I've heard “the CLI sucks” a million times but I don't find it that helpful.
"here are 12 specific counterintuitive things git does and here's exactly how they work” is much more useful
@b0rk my favorite counterintuitive thing is git rebase in full format. Like, what do you think git rebase branch1 branch2 even does and how does that differ from git rebase branch1 branch2^0 because they DO differ and significantly so.
@b0rk on the other hand, some of the suckiness doesn't really benefit from a conceptual explanation, it just needs memorisation, or aliases, or a cheatsheet. Like the way so many commands spell the 'delete one of these things' option differently, and I don't think there's anything really driving the choices.
@simontatham@b0rk It seems like we're stuck with Git in some ways because, you know, it's how all our code is maintained. But has anyone created a wrapper that uses a git service like GH in the backend, but just totally changes the API?
Starting with a CLI that maps a set of commands that actually make sense, to git commands?
@aadmaa@simontatham@b0rk One of the co-founders of GitHub is now working on something with a new spin on handling branches: https://gitbutler.com/
It lookes interesting, but I haven't tried it myself, yet.
@simontatham I'm not sure why but I'm not so bothered by that kind of inconsistency. like it would be good if it were consistent but it doesn't upset me in the same way as some other git design choices
(like the fact that git status says "you are up to date with origin/main" and gives you absolutely no clues at all that "origin/main" itself might be 3 months out of date, you just have to know)
@b0rk perhaps because the problems you're talking about cause actual confusion and misunderstanding? The inconsistent spellings of delete (and rename too) are annoying, but you're not confused, you just think 'grr, this one must be spelled another way' and look it up.
And you can be too consistent. svn went the other way, having a uniform set of options for all subcommands. Including 'svn ls -v', using the standard verbose option. My fingers always wanted to type 'ls -l'!
@b0rk I think a specific critique I have is that it does not follow the philosophy that there’s only one clear way to do something.
I’ve used git for 14 years and have never once used “git merge”. All you need is rebase. I can’t think of a single reason you’d need git merge if you know git rebase, and if there are cases it’s not clear
@tjc the situation with merge and rebase is interesting! I've been hearing a lot of “what's the point of merge, all you need is rebase" and “what's the point of rebase, all you need is merge" and "I use both all the time" from different people on here
I think different people have different workflows and, at least with the way git is designed, it makes sense to have both.
(personally I definitely use both merge and rebase pretty regularly for different reasons)
git checkout main; git merge mybranch; git push origin main
obviously this isn't what you'd do if you're working on a team where you use pull requests and merge them on github/gitlab/etc, but not every repo works like that
@b0rk you're doing great work with these analyses.
Honestly, I kind of wish someone would put out a version of git that just used consistent terminology and command-line flags; it would still be overly complex for source management but at least it wouldn't also be confusing.
(Like, why does git add -A add everything including unversioned files, but git commit -a only adds modified files? Why is one -A and the other -a, and there's no git add -a or git commit -A?)
s/When you do a merge, HEAD in the merge conflict is the same as what HEAD when you ran git merge. Simple./When you do a merge, HEAD in the merge conflict is the same as what HEAD was when you ran git merge. Simple./
@b0rk Excellent reference, thanks. This needs +was: "When you do a merge, HEAD in the merge conflict is the same as what HEAD when you ran git merge."
I am not sure what the second line means here:
commit 96fa6899ea (HEAD -> main)
commit 96fa6899ea (HEAD, main)
If that is your current commit, and HEAD, and main, then how can HEAD not point to main like in the first line? Does it mean that you're on a different branch than main? But then shouldn't that other branch be listed too?
@b0rk Not quite, sorry. You say on the blog that eveything in parentheses points to the commit. So HEAD points to the commit (directly) but main also points to the commit (i.e. the commit is part of the main branch?). But I don't understand how that is different from the first line where HEAD "is on main" as indicated by the arrow, and that combination points to this commit. Maybe this is too esoteric and minor, sorry!
@b0rk Thank you for indulging me. Yes, that makes it more tangible to me. I think my confusion was born from my idea that a branch is a history of commits, where in fact it is also just one pointer to one commit (which then has 'parent' or 'prev' refs, I presume).
So the first and second situation are effectively the same where the only difference is that HEAD is an indirect pointer in 1 and a direct pointer in 2, but in both 1 and 2 'main' points this commit in the same way.
@ednl yeah exactly. it's a really important difference though because in situation 2 your HEAD is "detached" and so any commits you make will be orphaned
@b0rk Ah yes. Then the most confusing part to me is that Git chooses to display 'main' for this commit, presumably because it is in the history of the main branch. But it's not the actual latest main pointer?
EDIT: or it is, and we got here via a really convoluted way e.g. by checking out a prev commit and then the next one again?!
I guess I just never noticed this or registered it whenever I happened to be in detached head state, because I always try to get out of it as quickly as possible.
@b0rk I have done "git checkout <tag>" before for use-cases like "there's a bug on <tag>" so you check out the tag, confirm "yes, that's indeed a bug" and can then re-create a working branch for that release tree (e.g. "git checkout -b <tag + 0.1>" or similar).
@b0rk@meejah I can only assume it's just git being built out of a set of primitives, and since a ref is an arbitrary label for a commit it's a thing that you could do.
Add comment