
Two Commits and the User Experience of Git - dfabulich
https://redfin.engineering/two-commits-that-wrecked-the-user-experience-of-git-f0075b77eab1
======
michael_storm
> _Today, I specifically want to call attention to two commits by the lead
> maintainer of Git, Junio Hamano, that left the deepest scars._

Why call the maintainer out by name (and continually do so throughout the
essay)? What purpose does that serve? To embarrass him for volunteering his
time on an open source project that you've built your business on, just
because _you didn 't like_ these two features he added? There was no personal
behavior of his at play; he simply made some technical choices you disagree
with.

Keep it technical, dude. This is ad-hominem and smug.

~~~
dfabulich
(Author here.) Let me begin by saying that this post is intended to be
_historical_. If you've ever wondered why "git checkout" both switches
branches and restores working-tree files, or if you've ever wondered why "git
reset" both rewrites history and unstages hunks, this post has the answer.

History is about people making decisions. It wouldn't make sense to write
about the history of nations without naming any names, and I think it wouldn't
make sense here, either.

Second, while I am critical of these historical decisions, I don't purport to
be better than Hamano. There's a whole section of this post saying that "you
or I might have done the same thing" for the same reasons.

I especially don't accept your charge that this is "ad hominem." I don't argue
that you should agree with me because who Hamano is. I don't (and wouldn't)
call Hamano any names, call doubt on his character, or even question his
intelligence or competence.

Everyone should evaluate these historical decisions on their own merits. But
anyone who wants to understand why git works the way it does must understand
that these are Hamano's decisions, and decide for themselves whether these
decisions were good or bad.

EDIT: A number of comments here have said that the complaint has to do with
the _number of times_ I mention Hamano's name, so I've updated the post to
remedy this.

~~~
michael_storm
Thanks for updating your post.

> _History is about people making decisions. It wouldn 't make sense to write
> about the history of nations without naming any names, and I think it
> wouldn't make sense here, either._

Historians usually do quite a bit more research, including _talking to
participants_. Much more context would be required before zeroing in on one
person and saying it's his fault. Was there discussion beforehand? Why has no
one changed it thus far? All you did was post to a noisy mailing list from
which you expected no reply, then assumed that it was solely the committer's
decision.

> _I don 't (and wouldn't) call Hamano any names, call doubt on his character,
> or even question his intelligence or competence._

But that's done implicitly when you tie his reputation up so thoroughly with
choices that you wrote an entire essay disparaging. The damage is magnified
when he's not given a real opportunity to explain himself. Although you stated
in another comment you've now emailed him directly, which is a good call.

~~~
dfabulich
Hamano replied pointing me to this thread. [https://public-
inbox.org/git/Pine.LNX.4.64.0510171814430.336...](https://public-
inbox.org/git/Pine.LNX.4.64.0510171814430.3369@g5.osdl.org/#t) Linus proposed
adding the feature to git-checkout, subsuming git-checkout-index. Junio
expressed concern about file/branch ambiguity; Linus wrote back, "Yes, I know
it's ambigious at times, but it really is very convenient. Usually we allow a
"\--" to say where a filename starts when it _is_ ambiguous."

I think a separate command would have been convenient, too. ¯\\_(ツ)_/¯

~~~
michael_storm
Good to know. I hope that your essay will be updated to reflect that.

------
CJefferson
I really agree with this, it's too late for git now, but I consider the
overloading of 'checkout' to be one of the worst pieces of UI I've ever come
across, and the source of confusion for many people when I try to teach them
git.

I didn't know that 'git reset' has a similar problem.

If git gained the 'checkout-tree' and 'checkout-file' (and other) commands
mentioned, I'd personally use them instead of checkout.

~~~
SilasX
Agreed. When reading this I realized how I avoid the path-usages of checkout,
specifically because it breaks the whole checkout model. The only exceptions
is `git checkout -- .` (which I treat like a separate command that happens to
use the same words).

~~~
kiallmacinnes
Okay, now I'm curious, what does `git checkout -- .` do?

I consider myself fairly good with Git, having used many (but nowhere near
all) of it's more advanced features, some of them regularly - but this is a
new one to me.

The double dash typically indicates all CLI flags/options have been provided,
and the remainder should be treated as positional arguments. However, a single
. isn't any standard CLI flag, so shouldn't need the --?

What am I missing? :)

~~~
SilasX
Argh -- good catch. You're right, `git checkout .` does the same thing as `git
checkout -- .` (discard unstaged changes), and I'd been using the latter for a
while before I realized, so I usually use the longer one out of habit.

~~~
kiallmacinnes
Hey, it happens! I'm confident some of the flags I provide git daily do
nothing meaningful either ;)

Edit: I also googled it, as I thought up a reason why -- might be used. If I
have a file called master, and want to unstage my changes to it, I would have
to `git checkout master`. Given this command has multiple meanings, both of
which could be appropriate, I'd need to disambiguate between them. The -- does
exactly this, it prevents the tree-ish parsing.

------
ajarmst
_" It's like Unix!" people say. "rm doesn't warn you, either!"_

Reminds me of one of my common complaints about that response. The difference
is that 'rm' doesn't do anything but remove stuff---it removing things isn't
surprising. If a command has multiple potential effect types, and only some of
them are destructive, then good UI would offer a warning.

------
notheguyouthink
Git wrappers are interesting things. I know a lot of people dislike them in
concept.. but strangely enough, I _like_ them in concept. I'm far from a git
expert, frankly I'm just barely literate, but I firmly believe there must be a
more simplified UX for Git.

Again, never to prevent normal usage, but to focus on educating the various
states of git, and really trying to make the commands very obvious for normal
use cases.

I see so many of these.. I'm wondering if it's sort of a rite of passage.
Maybe I should write my own? I'm sure I'd become more proficient at Git if I
did.

 _edit_ : Since writing this, I think I decided on writing my own wrapper.
Specifically, focused on a very small subset of straight forward commands with
exceptionally obvious wording and step by step education.

I really like the way [http://www.git-town.com](http://www.git-town.com)
documents their command usage, so I'll likely adopt that - but with the added
feature of providing either inline or copy-paste commands to gain per-step
documentation. Explaining the _motive_ behind a command, and why it was used
in that case. I also dislike how git-town layers their own UX, as I think an
education wrapper's goal should be to improve education on normal git flows,
not make new ones (automatic syncing, etc). Wish me luck lol.

~~~
davexunit
I rarely ever use the Git CLI anymore. Instead, I use Magit, an Emacs
extension. It is, hands down, the best Git tool available. Things that are
cumbersome on the CLI become painless, like staging hunks of files,
interactive rebases, viewing a blame of the file I'm currently looking at,
quickly amending a commit that isn't the current HEAD, reverting parts of
commits, browsing stashes, partially applying stashed changes, and much more.
It is the single best tool I use every day aside from Emacs itself.

[https://magit.vc](https://magit.vc)

P.S. - to those worried about what git wrappers are doing behind the scenes,
in Magit you just press `$` to see the command log.

~~~
throwanem
> quickly amending a commit that isn't the current HEAD

How? I use Magit too, and my go-to method for doing this would be an
interactive rebase that only modifies the target commit. I'd love to know
about a quicker way!

~~~
davexunit
From the status buffer, press `c F` for "instant fixup". It is relatively new.

~~~
throwanem
Oh neat! Thanks for the steer.

------
davexunit
Tech blog writers, please stop invoking the "UNIX philosophy". It's always
used as a pseudo-technical argument replacement by people with subjective
opinions about why software X has too many useful features.

~~~
msla
Even the traditional Unix tools don't follow the Unix Philosophy extremely
well. ls is a prime example: It has too many ways to sort and format its
output. It "should", in the Unix Philosophy sense, simply list things in a
directory and leave sorting and formatting to other tools.

However, parsing the output of ls is generally a bad idea, as it's meant for
human consumption, so ls breaks another tenet: It isn't very composable, as it
is difficult to use it in a pipeline, even though it should be a prototypical
source program.

~~~
hprotagonist
and `find` has practically re-implemented half the rest of the toolchain.

last week i discovered the `-delete` flag. For years, i had been doing `find
<stuff> | xargs -rm` instead.

~~~
jff
I don't even bother using 'find' to actually find files. 'du -a <path> | grep
<name>' works easily enough and I don't have to remember find's weird syntax.

~~~
sametmax
I use the ffind program instead. It's just so much easier with saner defaultq.

------
gsylvie
For me the UX issue was that I came from "svn" and "svn checkout" is a VERY
different command. Obviously this is not git's fault. I suspect git is much
easier to learn for those untainted by svn.

As for "git checkout branch" and "git checkout -- branch" doing very different
things, I find the "\--" works well to bump my mental model into the right
state, and so in practice I'm never confused about the distinction.

It also helps (a lot!) that "git status" teaches us about these modes for "git
checkout --" and "git reset HEAD":

    
    
        On branch master
        Your branch is up-to-date with 'origin/master'.
        Changes to be committed:
        (use "git reset HEAD <file>..." to unstage)
    
        modified:   pom.xml
    
        Changes not staged for commit:
        (use "git add <file>..." to update what will be committed)
        (use "git checkout -- <file>..." to discard changes)
    
        modified:   src/main/java/com/bitbooster/FoxtrotHook.java

~~~
CJefferson
The problem is the `--` isn't compulsory.

Quick quiz, what does: git checkout x/y do? At least:

* Reset a file called x/y

* Check out a branch y from a remote x

* Check out a local branch called x/y

------
hprotagonist
>Today, Git has a reputation for being a tool that erases your working tree
without warning if you use the wrong command.

This is news to me.

~~~
jakebasile
>> Today, $SHELL has a reputation for being a tool that erases your working
tree without warning if you use the wrong command

What a vacuous statement. If you use something incorrectly, it works
incorrectly.

~~~
tqkxzugoaupvwqr
If a tool allows you to easily destroy something, the tool needs a safety
switch.

------
makecheck
It would be nice for a "git 3.0" to introduce a few new commands with
consistent separation of responsibilities (new terminology as needed), and
deprecate the twisted ones for removal in "git 4.0".

Whatever is confusing today will only be worse after more years of piling
stuff on top.

------
switch007
I think a bit of perspective may be required by the author: nothing is
perfect, people make mistakes, and to remember the time before Git existed.

Moreover, to mention the person's name ~10 times is extremely crass.

------
colordrops
What does it mean to check out a file from the stage to the working tree?
Aren't staged files already part of the working tree?

~~~
ekimekim
You can (for example) modify a file, stage it, then further modify it. You now
have three copies of your file - the original, the staged version, and the
working tree version.

A "git checkout -- file" would revert your working tree copy to your staged
copy, whereas a "git checkout HEAD -- file" would revert both your staged copy
and your working tree copy to the unmodified copy.

There are other scenarios but this is by far the most common. The index (aka.
staged files) can be thought of as a completely seperate copy of your working
tree that can have arbitrary changes written to it independent of the working
tree. It's just that in normal usage you're only ever copying to the index
(via git add) FROM your working tree.

------
rootlocus
> Aside: This is when git started performing destructive actions without
> warning

Is there anything truly destructive in git though? Unless there's a GC, you
can still recover any unreferenced objects.

~~~
Karunamon
Not if they were never committed in the first place. It sounds like if you
made changes without committing them (new files or modified committed files),
and then branch switch, that content is gone with no warning.

~~~
rootlocus
Git usually aborts any checkouts or merges that destroy the working directory.
You can however use `git reset --hard HEAD` (which I aliased nuke) and it will
happily oblige.

------
whataretensors
As a counterpoint, I like both 'checkout' and 'reset' as they are. Changing
them for aesthetics would break a lot of scripts. Additionally I find the
commands easy to use and remember.

------
allan_golds
Unclosable pop-up

~~~
dfabulich
(Author here) That's not supposed to happen! What device? (Phone or desktop?)
Could you post a screenshot to imgur?

