
High-Level Problems with Git and How to Fix Them - jordigh
https://gregoryszorc.com/blog/2017/12/11/high-level-problems-with-git-and-how-to-fix-them/
======
jcranmer
One personal anecdote:

In a decade of using Mercurial, I've managed to get a repository in such a
confused state that I had to blow it up and start from scratch just once. In
the same time, I've had to do the same for git repositories at least 5 or 6
times--and I never really used git all that much. Even nowadays, where I'm
more or less forcing myself to use git [1], I'm incredibly hesitant to try any
sort of complex git operation because of the risk that I might lose my in-
progress commits.

While the problems of git's UI is well-known, I don't think it's as
appreciated that git's complexity generally discourages people from trying
things out. I'm never really sure what could cause me to lose unpushed commits
or uncommitted working files, especially with git's predilection for reusing
the same verb to do slightly different operations for different concepts. In
stark contrast, it's more difficult to get Mercurial to chew up your work
without telling you [2], and it's easier to explore the command lists to see
if there's something that can do what you want it to do.

[1] The repositories I'm working on involve submodules, which hg-git is
exceptionally poor at supporting well. Even so, the list of git alias commands
on the whiteboard is entitled "Forcing myself to use git before I rage-quit
and switch to Mercurial."

[2] I have done it, though. Worst offender was discovering that hg qrefresh
<list of files> causes only those file changes to be committed, and files not
mentioned are decommited to working-directory-only changes... which hg qpop
happily deleted without warning me, causing me to lose a large patch. That was
when I put the patch queue directory in version control.

~~~
popopopo
wow.

> I'm incredibly hesitant to try any sort of complex git operation because of
> the risk that I might lose my in-progress commits.

correct way of learning git is every time you want to use a
"complex"/dangerous commands in git, you `cd /tmp` and create a git repository
there. then create some commits, branches and experiment. you will NOT lose
anything, but you will learn a lot.

I did and do this every time I have doubts. and with time you will understand
how non-dangerous git commands are.

"dangerous" part comes from the user, not the software, and it is okay. when
you accidentally kick a corner of a furniture, that does not make that
furniture "dangerous".

~~~
lucideer
If this is the "correct" way to learn, the problems are not with the user.

A well designed ui should be learnable while being used - creating a fresh
learning repo for dedicated learning for fear of losing data while using the
tool in your "real" repo is the ultimate red flag in terms of usability.

~~~
popopopo
a still, while people cry about UI (which is irrelevant part), other people
just take time and learn git. by whatever measures.

PS. throwing out things you test on. isn't that how every programming tutorial
works? write code NOT in your main repository, test things out, throw it away
(or keep it, whatever)?

~~~
lucideer
> _UI (which is irrelevant part)_

UI usability is irrelevant... ?!??

> _isn 't that how every programming tutorial works_

Firstly, learning programming and learning to use a tool that is a component
of your workflow are two independent things. The latter should generally
(ideally) have a much lower (aiming towards zero) learning curve. Yes, this is
possible, with good UI design.

Secondly, even programming language designers strive towards lowering this
learning curve. There is, imo, a necessary complexity, or "table stakes" for
any reasonably useful language, but its still very evident to any language
designer that this is a trade-off. Usability is desirable.

For a tool as popular and essential as Git, usability should be a much more
central goal than it seems to have been in the past.

~~~
nine_k
Is writing programs and building them a part of your workflow? Do you even
spend time on setting up your build? On acquiring an actual understanding of
how it works?

Other tools are no different. If it's an important tool, it pays to actually
study it. Do not expect tools to do what you mean before you know well what
you mean.

~~~
lucideer
I think you're misinterpreting my post. I completely agree, you're absolutely
right, but there's a huge different between what you're saying and what the gp
was: that usability should not be a consideration for these tools.

I do understand, in quite a bit of depth (which I'm sure is still far from
complete), how Git works. I still prefer to use Mercurial because it doesn't
require me to be continuously aware of the dangers of those internals on a
day-to-day basis - it doesn't supply me with a loaded foot gun. Sadly, I use
Git every day, and rarely Mercurial, because that's what the masses have
chosen and I've found using in-between compat tools not to be worth the extra
hassle for me.

~~~
nine_k
OK, I can agree: git is not as safe, mistake-proof, or consistent as it could
be.

What I like about git is that it gives a simple algebra of patches (diffs),
and allows almost any sensible operation over them.

It would be great to build a different CLI on top of the same data structures
(and maybe the same plumbing) that would expose a more consistent and usable
interface, while allowing for [most of] the same capabilities, and preserving
interoperability. I suppose git-hg tries to achieve something similar, too.

For me, much of the pain of git CLI was alleviated first by a set of aliases,
and then by using magit from within emacs.

------
taeric

        If you requested save in your favorite GUI application,
        text editor, etc and it popped open a select the 
        changes you would like to save dialog, you would 
        rightly think just save all my changes already, 
        dammit
    

I'm sympathetic to what this is asking, but I have to feel that this would
lead to much better practices for many people. I'd wager a ton of folks would
be more "why in the world does it think I changed that?" than they would care
to acknowledge.

That is, by and large, the quick sanity check of "these are the things you
did?" is actually quite valuable. Regardless of any annoyance it may give.
Similar to the "did you mean to print all 500 pages?" warning you can get if
you print something huge. Sometimes, yes. Yes I did. Often times, no, not what
I intended.

~~~
CSMastermind
On source control anti-pattern I've run into is people thinking a commit is a
'save'. It's not. A commit should move you from one working state of your code
to another. Compare that to saving my files which I do compulsively every 5
minutes or so regardless of the state the file is in.

~~~
jononor
I agree, but... It is nice that one can compulsively (or automatically) save,
with full history. And then when things are in a nice working state, be able
to distill this into one or more clean changesets.

`git rebase -i` makes this relatively easy/nice. I think even better tools for
this would make it way more realistic to get away from the staging area as
step in the default 'commit' process.

------
gumby
I'm surprisedly the staging-area hate. Does it really violate peoples'
assumptions? I like the ability to make a big, complex change and checkpoint
stable portions (subsets) of the work as I go.

~~~
jcranmer
Coming from someone who learned hg well before git, and who's now being more
or less forced to use git long after developing comfortable hg workflows, the
staging area feels like a half-baked implementation of what it's supposed to
be doing.

I'm used to thinking of commits as atomic commits--roughly, each commit is the
smallest change that atomically makes sense. So you should be able to use the
staging area to build up that commit as you find more pieces to make it in.
But while I'm slowly hacking away at this commit, I talked with someone else
and decided to try an idea which turns out to be a small commit. But I've
already got this half-built-up commit that's staged, so I need to commit that
to make the new commit, and then somehow reverse the patches and restage the
commit (which is something that's well outside my git comfort level).

What hg has is a few different features that make it possible to build up not
just a single commit but an entire commit sequence and do operations on that
commit sequence (like reorder them). The git staging area is kind of a weak
version of that, but it ends up being in limbo: too complex for the simple
just-commit-everything model but too simple for try-to-craft-the-public-
changelog model.

Edit: the model I adapted to from my hg workflows is effectively commit
--amend. Mercurial has an interesting way of doing history tracking such that
if I really need to, I can actually follow the history of the "oops, I need to
change these commit because I missed a compiler failure" or "I forgot to save
this file before committing," which is a feature that git doesn't have. If you
promoted amending the last commit, you wouldn't need a staging area to build
up a commit, the last commit does it for you already.

~~~
maccam94
Some useful commands I'd use in scenarios like this are:

    
    
      git add -p # select what to add to the staging area
      git reset -p # deselect chunks that I decided I don't want anymore
      git stash # saves your working state in a temporary commit (not in your branch)
      git stash pop # restores the working state from the last git stash command and drops the temporary commit
      git rebase -i $start_point # where $start_point is either a branch or some commit in your branch history. -i means interactive, which allows you to reorder/edit/reword/squash commits
      git checkout -b $newbranch $start_point # you can make a new branch at any commit in history, so if you decide to reorder your commits, then declare and older commit as a branch and get it merged first, that's fine. $start_point is optional, if unspecified the current HEAD commit is used.
    

For your specific example, with a staged partial commit on branch idea-1, I
would:

    
    
      git commit -m 'WIP idea 1'
      git checkout -b idea-2 HEAD~1 # makes a new branch at the parent of the HEAD commit
      git add -p # pull in the desired changes
      git commit -m 'idea2 implementation'
      git checkout idea-1
      git add -p # stage the rest of the changes
      git commit --amend
      git rebase idea2 # if idea 1 depends on idea 2
    

I do wish git had some notion of sub-commits with their own messages, and a
better UX for cleaning up a set of commits before merging them.

~~~
mmebane
> I do wish git had some notion of sub-commits with their own messages

You could use the "fast-forward with merge commit" style. [1] Then you can
treat the commits from the branch as sub-commits, and the merge commit as the
parent commit.

[1]: [https://stackoverflow.com/questions/15631890/how-to-
achieve-...](https://stackoverflow.com/questions/15631890/how-to-achieve-git-
no-ff-ff-only-which-is-not-allowed)

~~~
maccam94
Interesting! That's almost what I want, except here's the specific behavior I
want to achieve:

    
    
      A clean git log view that shows only meta-commits
      Git blame shows both the meta-commit and the sub-commit for each line
    

I suppose I could get creative by enforcing that tags be embedded in commit
messages and then filtering them in the log view, but it would be better if it
was standard.

~~~
mmebane
You can show only merge commits with

    
    
        git log --merges
    

However, I don't know if there's any way to achieve that with git blame, and
not all tools may offer that kind of log view.

------
blux
_The Git staging area doesn 't have to be this complicated. A re-branding away
from index to staging area would go a long way. Adding an alias from git diff
--staged to git diff --cached and removing references to the cache from common
user commands would make a lot of sense and reduce end-user confusion._

\--staged is already an alias for --cached in the latest Git version.

~~~
BlackFingolfin
And it has been for some time (years, I think)

~~~
majewsky
Years, yes. At least since 2.0, though probably longer.

------
klodolph
It's clear that there will be a successor to Git some day, in the sense that
Git is a successor to SVN (yes, I know Linus's viewpoints on SVN). But the
successor won't be a "better Git" just like Git isn't a "better SVN".

The driving features of Git's successor will be unrelated to Git UI gripes. If
Git's UI gripes were important enough, people would just be using Mercurial
(which has it's own quirks). The biggest problems I've personally seen with
Git are the same problems you see with other VCS, where two people change the
same file, and one commits the changes first. Now you have to merge the
changes and sometimes it's a pain in the ass when you just want to submit your
changes and go home. A better VCS isn't going to solve that!

I'm still glad that Git delivered me from all the merging problems that SVN
had, although I've heard that SVN is a little better these days.

~~~
Klathmon
> A better VCS isn't going to solve that!

It might!

I feel like a "language aware" VCS would be able to help a lot in this area.

I know there are 3rd party tools that can do this, but if a VCS came along
that natively knew about some languages it could unlock some really cool
features.

Imagine instead of your VCS storing the text source, it stores the AST!
Language aware means it can also integrate tightly with language package
managers.

~~~
klodolph
That's not really a "better VCS" that's just a better 3-way merge. You can
already plug your own 3-way merge into Git and other VCS.

~~~
scotty79
Can you recommend any language aware 3-way merge tools?

~~~
ygra
Semantic Merge comes to mind, although it only does an AST merge on the
structural level while blocks are still merged traditionally (I guess it's a
good tradeoff of complexity, though). It has however proven to be rather slow
with large merges (or large files; the source file I tried to merge had ~4
kLOC), in my recent test (was merging a major version of one product into
derived ones, with lots of refactoring, so I thought SM could help).

------
cannam
This is a really interesting piece that introduced a number of ideas I hadn't
ever thought deeply about, particularly in the section on nameless workflow.
(I do think of myself as reasonably capable with both Mercurial and Git.)

If your instinctive response is to defend Git, boost Mercurial, ridicule
people who can't use their tools, ridicule the tools for being unusable, or
whatever -- suppress it for a while and give this article some thoughtful
attention.

~~~
cryptonector
My instinctive response is to explain _why_ git is easier. TFA is very
unfortunate. I think the author is missing out on a better experience, and
their git hate isn't helping them.

~~~
Sheeriapi
Let me suggest to suppress that instinct and read the article again. There's
not a shred of "git hate". Quite the contrary: The author is a Git user
himself and has detailed suggestions to improve Git and its ecosystem. That is
a good thing, not "very unfortunate". Also, Git is easier than what exactly?
Than the proposed improvements? I suspect you haven't read the article. This
is neither a "Mercurial vs Git" nor a "Git sucks" piece, which you should have
noticed while reading.

------
sharp11
Lots of food for thought in this article, but the part I find most interesting
is his distinction between "soft" and "hard" forks. (Viz., are you "forking"
in order to collaborate or in order to go your own way?)

If collaborate, it _would_ be nice to lower the barrier to participation ...
more like Wikipedia. I know that I often don't bother to submit a PR for small
changes bc of the overhead of setup. Whereas I fairly often make small edits
to Wikipedia changes, because it's so easy.

~~~
ygra
GitHub at least has a simple way of making small changes: You can simply click
'Edit' on any file, which will make a fork, commit and pull request in one
operation. It's only good for single-file changes you're confident to make in
a simple web text editor, though. I've only used it for typos.

~~~
majewsky
I don't like that doing so frequently litters your personal repository list
with forks. Would be nicer if they allowed arbitrary users to push to branches
like `incoming/<username>/<branchname>` in the original repo.

~~~
ygra
Isn't that up to the repository maintainer whether they allow pushes from
other people? And sure, it litters your list with forks, but you can remove
them as soon as the PR is merged. And that's the case for pretty much anything
you contribute to (unless you can push there, of course).

~~~
majewsky
No, there is no process in Github to allow _everyone_ to push to a certain
branch (or branches matching some regex). Github forces people without
collaborator rights to use forks.

I've seen this lead to confusion where people fork a repo and send PRs even
though they have contributor rights, because they didn't understand the
difference. As an extreme example:
[https://github.com/flystack/misty/pull/101](https://github.com/flystack/misty/pull/101)
\- This is a repo with only one person with write access [1]. Yet this same
person only commits to their private fork of this repo and sends himself PRs
that he then immediately merges. I don't mean to blame the developer, only the
Github UX.

[1] It says "6 contributors" on the mainpage of the repo, but four of these
are from my team and we definitely don't have write access.

------
phillco
Anecdotally, being able to commit anywhere, without needing a branch name, is
one of the biggest conceptual differences when we teach new hires Mercurial at
Facebook.

~~~
nhaehnle
As a convinced Git user, this is the one aspect of the article that I found
genuinely interesting. I mean, you can commit anywhere in Git, too (and I
occasionally do for some advanced use cases), but it tends to not be well
supported by Git.

(And, I might add, probably not by Hg either if it requires enabling an
extension...)

Anyway, _hg show work_ looks like an interesting thing. It's perhaps a bit
more magic than Git usually goes for, since you can't just show all DAG sinks
(it would get messy really quickly with amends and rebases). And I don't think
I'd benefit too much from it, since I've come to prefer having all my work-in-
progress on a single branch. But I can see how someone could appreciate the
feature.

~~~
rbehrends
> As a convinced Git user, this is the one aspect of the article that I found
> genuinely interesting. I mean, you can commit anywhere in Git, too (and I
> occasionally do for some advanced use cases), but it tends to not be well
> supported by Git.

"Not well supported" in this context means that you can lose data (as commits
that are not reachable from a ref can be garbage collected). Mind you, you
have to ignore warnings/errors to get there, but without ignoring them, you
also won't be able to actually have anonymous branches.

> (And, I might add, probably not by Hg either if it requires enabling an
> extension...)

It does not require an extension. The `show` extension that Greg talks about
is a smart log display, which shows a contextual log for your current work.
Anonymous branches have been part of Mercurial from the beginning, long before
the `show` extension existed.

For what it's worth, it's not so much that Mercurial supports it, but that Git
doesn't. Git is rather unique in its setup in this regard. No other VCS that I
know of uses garbage collection and in particular branches to prevent
revisions from being garbage collected. (Some may require you to name branches
for other reasons, but not so you don't lose commits.)

It's probably also worth noting that we have an implementation detail leaking
into user space here. Git uses what is essentially purely functional data
structures underneath to achieve atomicity in the absence of an actual
database engine [1]; a different implementation, such as on top of SQLite,
could avoid this.

[1] Atomicity here is not about locking, but about a transaction being
interrupted by the user or an external event, such as a shutdown.

~~~
nhaehnle
It is supported, it's just that the only thing keeping commits not on a branch
alive is the reflog.

Question: How does Mercurial deal with garbage collection? After all, the
desire for garbage collection is by far not unique to Git -- any version
control system that has the equivalent of `commit --amend` and rebase should
provide it.

As for not needing the extension, it seems to me that having "dangling"
commits would be very difficult to use without some decent visualization of
the dangling commits, such as what the blog post shows with `hg show`.

> _It 's probably also worth noting that we have an implementation detail
> leaking into user space here._

I find this comment _absolutely_ fascinating, because truly, this is not an
implementation detail leak _at all_.

The point of the purely functional data structures isn't to achieve atomicity
(although potentially being a bit more robust to power loss etc. is certainly
a nice side effect), it's a way of thinking about version control. I've never
heard functional programmers use atomicity as the main argument for immutable
data structures, either...

The whole point of Git's design is that it chose a robust and crystal clear
way of thinking about distributed versioning as its underlying model of what
version control _is_ , and then simply provided tools for manipulating that
DAG. Some of those are a bit ugly because of how the system grew over time,
but still, this is how software _should_ be designed: have a clear model of
what the data is, then provide tools for manipulating it.

For what it's worth, the underlying data store of Git could quite easily
support "unnamed" tip commits as well. What you'd need is a change in the
garbage collection policy, and "porcelain". Which brings me back to the
question of how Mercurial does it.

~~~
rbehrends
> Question: How does Mercurial deal with garbage collection? After all, the
> desire for garbage collection is by far not unique to Git -- any version
> control system that has the equivalent of `commit --amend` and rebase should
> provide it.

Garbage collecion is an issue that is 100% unique to Git. No other VCS even
thinks about throwing user data in the repository away without the user
explicitly telling it to. Once you have a user telling you to throw the data
away, it can do that. There is no need for GC; this is purely an artifact of
Git's implementation. I'm honestly not sure why you think you'd even need a GC
for `hg commit --amend` or `hg rebase` (or similar operations in other VCSes).

> As for not needing the extension, it seems to me that having "dangling"
> commits would be very difficult to use without some decent visualization of
> the dangling commits, such as what the blog post shows with `hg show`.

I'm not sure where you get the idea. This feature is, after all, not unique to
Mercurial. It's Git that has the oddball semantics that no other VCS on earth
has. Mercurial has been able to graphically show the graph for ages and the
ability to just list open heads, too (`hg heads`). If you look at the code,
the implementation of the show command is largely just a templated graphlog of
a particular revset. For example, `hg wip` [1] (for "work in progress") has
been doing something similar just using revsets and templates from core
Mercurial.

> The point of the purely functional data structures isn't to achieve
> atomicity (although potentially being a bit more robust to power loss etc.
> is certainly a nice side effect), it's a way of thinking about version
> control. I've never heard functional programmers use atomicity as the main
> argument for immutable data structures, either...

This is because functional languages do not have to worry about their state
being destroyed by the user hitting Control-C or a power outage. This will
simply terminate the program, whereas for Git it will interrupt a transaction
in progress.

> The whole point of Git's design is that it chose a robust and crystal clear
> way of thinking about distributed versioning as its underlying model of what
> version control is, and then simply provided tools for manipulating that
> DAG.

This is what other version control systems do, too, without relying on purely
functional data structures. The fact that the data structures are purely
functional is, after all, not a property that is visible to the user other
than through the side effects of garbage collection.

[1] [http://jordi.inversethought.com/blog/customising-
mercurial-l...](http://jordi.inversethought.com/blog/customising-mercurial-
like-a-pro/)

~~~
nhaehnle
> I'm honestly not sure why you think you'd even need a GC for `hg commit
> --amend` or `hg rebase` (or similar operations in other VCSes).

Maybe you don't call it GC, but doing a rebase in Git leaves the old, pre-
rebase version around. That is a feature: over the years, it has happened to
me more than once that I'd missed something when resolving complex conflicts
during the rebase. Being able to refer back to the state from before the
rebase was very helpful in these cases.

If Mercurial throws the old version away unconditionally, that would suck very
much indeed.

If Mercurial keeps the old version, then perhaps at some point in the future
I'd really rather have all that old data removed as a simple matter of saving
disk space. Surely nobody wants to do that manually?

Hence: you either have a system that makes it much easier than Git to lose
data, or you need garbage collection.

I don't know what Mercurial does, but somehow, the fact that this dilemma
isn't obvious to you -- somebody who clearly seems to know a lot about
Mercurial -- doesn't instill a lot of confidence in it.

~~~
rbehrends
> If Mercurial throws the old version away unconditionally, that would suck
> very much indeed.

Which is why it isn't done.

In core Mercurial, the old revisions are stored in a backup bundle in a
separate backup directory. Note that bundles can transparently be used as
read-only repositories, so you can view their logs as though they were still
part of the parent repo, diff against them, pull from them, etc.

With the evolve extension, those revisions will simply be marked as obsolete,
with obsolescence markers showing which revisions were replaced by which. The
commits will be hidden, but are still part of the repository. If you ever want
to get rid of the old revisions, you'd have to use (say) `hg strip -r
'exctinct()'`, which would store them as bundles as described above, or clone
the repository and delete the old repository.

Plus, there are public, draft, and secret changesets. Public changesets are
immutable and cannot be changed without user override.

Bazaar rebase will simply hide the old revisions; you can recover them with
`bzr heads --all`. To permanently delete the revisions, you have to clone the
repository and delete the old version (and all backups). And, of course,
there's rarely a reason to use rebase in Bazaar.

> Hence: you either have a system that makes it much easier than Git to lose
> data, or you need garbage collection.

As I described above, neither. In every case, you need to go through several
steps each requiring the user to affirmatively express their desire to delete
data.

And as disk space is really cheap these days, hardly anyone ever actually
deletes the data in practice, as there's no point to it.

> I don't know what Mercurial does, but somehow, the fact that this dilemma
> isn't obvious to you -- somebody who clearly seems to know a lot about
> Mercurial -- doesn't instill a lot of confidence in it.

I think your dilemma is largely an imaginary one, fretting over a resource
(disk space) that is too plentiful to require micromanagement.

Keep in mind that most of the data in your repository will come from other
people; there's only so much source code or text that a single person can
write in a day. If you're generating massively large binary assets, a DVCS is
probably the wrong tool, anyway, because of scaling concerns. This inherently
limits the amount of "wasted" data that you can have in a repository to a
percentage of the repository size.

~~~
tonfa
> I think your dilemma is largely an imaginary one, fretting over a resource
> (disk space) that is too plentiful to require micromanagement.

I think the need for GC in git is likely also tied to its original resource
intensive implementation. Pack files were added to fix that, but then you need
to GC the blobs to prevent storage blowup.

Mercurial and most other VCS have delta storage as their base format which
avoids this issue.

------
giomasce
The thing I love of git is that its internal structure is very simple and
transparent, so for any given repository it is possible (although not
necessarily easy, if things are well messed-up) to understand what is going
on. It is true that the interface is often messy and inconsistent, sometimes
annoyingly so, but if I can understand what things are, I can somehow work out
how to do things with them (in extremal situations, I know I can fast-export
everything, process with custom Python scripts, rather easy to write, and
fast-import again). While if I do not understand what things are, the most
powerful and user-friendly tool will have very little value to me. Also,
knowing what is git under the hood makes me very confident that I will not
lose pieces: as soon as the hash of my interesting commits is in another
repository (which is not corrupted), I can do whatever I want with the first
one and everything I care will be safe anyway.

All of this depends on me knowing what happens inside git and the git insides
being rather simple (a hash tree with a handful of object types); I think I
would like Mercurial (or any other DVCS) much more if I knew the same about
it: can someone suggest places where this is described?

~~~
bluGill
As a long time mercurial user being forced to switch to git I find that I know
more about git internals than mercurial. Mercurial provides a nice abstraction
so I don't have to care. In face I've seen a few hints that mercurial has
actually changed their internals over the years, but since everything still
"just works" I don't think about it. By contrast git is forcing me to care
even though I have better things to do with my time.

Sadly github and CI systems provide community tools around git not mercurial
(mercurial is an afterthought at best in most CI systems). Those community
tools are compelling enough that I'm switching to an inferior system just to
get them.

------
camgunz
I find the Git UI discussion the least interesting part of Git discussions.
Git's UI isn't great, but it usually doesn't matter because 90% of what you're
doing is what you always did with Subversion and Hg. The commands are a little
more obtuse but that doesn't matter either.

I really like Gregory's discussion about nameless workflows. For small side
projects I use Git on a personal server and I don't care to name every change
or even use branches. I essentially just want my work sync'd remotely and
maybe -- __maybe __\-- I 'll look at the history, but almost never. I used to
use tar/scp for this before I got worried about overwriting local/remote
changes.

I use Git for this now and guess what, my commits are all '.'. I blatantly
don't care, and some commits I'm rewriting the entire source tree in a day
(like I said, small side projects) so the message would be "Old version
sucked, rewrote" and there would be 20 of them.

Mostly this is because I don't care to waste my brain power on managing my
changes. That's what I'm using a VCS for. They don't need summaries -- that's
what diffs are for -- and they certainly don't need names.

Gregory's argument against forks makes sense to me too. Semantically a fork
doesn't really sync back up with the main branch; if it does it's pretty rare
-- or just look at the terminology here: branch, root, trunk... FORK. Fork
doesn't really go there.

------
dmitriid
One of the main problems I still face is: Git's CLI is incomprehensible. It's
a tangled mess of poorly named commands with even more poorly named options.

If there's a "create" command, it's "modify" counterpart is likely to be
"create -b -u -t --modify", and it's "delete" counterpart is likely to not
exist at all or be an arcane incantation of several non-related commands and a
push.

Unfortunately, most GUI tools are just thin wrappers on top of all this mess
(with the possible exception of GitUp whose abstractions are also paper-thing
at times)

------
cryptonector
I find this depressing.

I do think the git model is superior to Mercurial's, by far.

I also find the git commands much, much easier to use than Mercurial's, unless
I stop caring about having meaningful commits.

With Mercurial I end up just committing lots of work in one go, with minimal
logical splitting, and that's that. The Mercurial repos I share with others
are full of useless (to me) merge commits because no one can bother rebasing
as rebasing is not as easy on Mercurial.

With git I can spend the effort of making clean, logical commits and rebasing
so that no one need see pointless internal-to-my-way-of-working commits. I can
do this _because_ git does not hide its internals from me. I know git is a
pile of anonymous commits that form trees, with branches and tags as symbolic
names that resolve to specific commits in the pile.

See
[https://news.ycombinator.com/item?id=15910094](https://news.ycombinator.com/item?id=15910094)
in this same thread for more on how the git model is natural.

------
yoodenvranx
In the beginning I tried to use git exclusively through command line because I
thought that's how real men do it.

But after using gitkraken (or similar products) I will never go back to using
git through its command line interface. Being able to see the whole repo
structure is just so much more convenient and it helps to resolve most merging
problems much faster. git guis all have their own problems, but overall using
a git gui for me is the better choice in 95% of all use cases.

~~~
michaelgrafl
I always have both open. Gitkraken for having a broad overview of local and
remote branches, stashes, and for staging chunks of code. And the command line
for everything else. Some things just confuse me in any GUI Frontend or don't
work as fast as on the CLI.

~~~
cbcoutinho
This is what I do too. I like looking at my entire staging area commits using
the CLI, but merging makes much more sense using GitKraken. Version control is
just too complicated of a problem (for me) to reason about in one 'view', and
using different tools to look at separate scales of a repository is the way
I've found to work through that difficulty.

------
andreareina
The reflog is an invaluable feature, I'm glad that I learned about it right
when I started learning how to use git. Knowing that there's little I can do
to permanently mess things up has given me the confidence to just try things
out, speeding up my learning tremendously.

I agree that the default git porcelain leaves something to be desired.
Personally I use and absolutely love magit, slightly tweaked to give me the
defaults I want.

The staging area is one of the things that magit really improves on; you
retain all its power while making it very cheap to use -- one keystroke to
stage the selected file, or all modified files. I stage individual hunks à la
`git add --patch` a lot, and sometimes need to include only part of the hunk
I'm presented with. It wasn't uncommon for my editing the diff to result in an
error with the git cli, now I select the lines I need and just stage those.
And if you really don't want to use it, `--all` is easily set as a default for
committing.

------
phasmantistes
While I appreciate and agree with the last section regarding replacing
GitHub's "fork" model with something lighter-weight, it amuses and saddens me
that the author puts a lot of effort into describing something that they feel
is wholly novel... but is actually just the model already implemented by
Gerrit
([https://www.gerritcodereview.com/](https://www.gerritcodereview.com/)).
There are alternatives to GitHub already! Some do things better than GitHub
already! Give it a shot.

------
saulrh
A lot of this I can't really speak to, and for some things I can see the point
- I use stashes to follow almost exactly the no-staging-area workflow - but in
other places this suffers at least a little from being a generalization from
one example.

In particular, named branches significantly reduce friction in my day-to-day
work. I often have PRs for multiple smallish tickets in flight simultaneously,
working on the code for each ticket while I wait for review on the others.
This means that branches generally live for about eight hours and I care about
maybe three of them at any given time, a different set every day. Invocations
are stable, named refs fit in cache and my CLI does fuzzy LRU to improve that
further, and I can bounce to the current code for a given ticket/PR at almost
the speed of thought. By contrast, the "view the log and copy-paste a hash"
workflow makes me cringe - I have to inspect the log every time I want to
bounce to the code for a PR, and the invocation to go to a PR changes four
times a day? I agree that that that makes sense if you have thirty long-
running PRs open at any given time and you don't have brainpower to remember
branches anyway, and that's very reasonable for a big open-source project, but
that's not all use cases.

------
Philipp__
I was holding to that famous UNIX mentality for many years, since I started
programming almost... Use many programs that do one single thing, pipe the
data, print it on the main output etc etc. That’s how I used git too, writing
160 chars wide commands... I tried playing with Emacs year and a half ago, and
it was big cultural shock, for first year of using it I still couldn’t adopt
kitchensink philosophy. But after barely scratching what emacs had to offer I
went back to my unix terminal workflow it felt clunkier than ever.

Takeaway from this story can be Magit. It totally changed my perception of
git, and felt like more robust and semantically better way to use/understand
git. Maybe it was like that because of few years of terminal git usage, don’t
know how will complete beginner react to magit. But what shocked me the most
was how my approach to git changed with dofferent interface. GUIs never
appealed to me, but magit represented the middle ground of GUI like
functionality with terminal like interface that got many things semantically
right.

~~~
globuous
<edit> TD;DR: Magit is a lesson of UX. </edit>

+1 for Magit. When I discovered it is when I discovered git. I assume tig must
be similar although I've never looked into it but...

With magit, from my editor, I can easily check: my git status, choose which
hunks to stage or unstage, commit easily. I can switch branches with less than
10 keystrokes. etc. But to me, what's coolest about it is that I can easily
see commit histories and graps for branches, files, and the repo. With less
than 5 keystrokes. And I can navigate these histories easily. Finally !!! Same
story for pushing, pulling, merging, rebasing.

Which all of this you can do in CLI obviously. But Magit makes it _easy_ , and
because now I have everything I need with regards to git just a few keystrokes
away, I can interact with git easily. So I do it.

~~~
masklinn
My only beef with magit is that it's _really_ slow for nontrivial projects
($dayjob's main codebase is 1MLOC and has ~110000 commits reachable from HEAD,
"status" takes a good 5s, and adding/removing stuff from d/u or d/s quickly
gets painful).

------
arunc
The only reason I am urged to use git is for it's remote branch deletion (that
mercurial frowns upon) and Gitlab (posh UI) that lacks with any mercurial
hosting solution.

If hg supports to strip a remote repo, it would be just fantastic!

hg evolve is awesome and topics are fantastic, but a `hg strip <remote-url>`
would remove the need for any of these and help me keep my simple histedit,
strip, push loop intact!

~~~
rbehrends
1\. The problem with `hg strip` is how it is (and has to be) implemented,
because of the revlog format. Basically, revlogs store changesets in
chronological order. Stripping a branch means first saving all commits that
are not being stripped (but occur chronologically after the first stripped
commit) to a bundle, then truncating the revlog before the first stripped
commit, then restoring the commits from the bundle. This can already be an
expensive operation locally, but it's even more of a problem on a server
shared by multiple users who may have been pushing their own commits.

2\. The general recommendation would be to use `hg prune` (part of the evolve
extension) instead. Pruning commits will just hide the pruned commits and
pushing will then send the obsolescence markers for those commits to the
server, hiding them there, too. This is an append-only operation, so it's
cheap and works well even with multiple users.

3\. In general, though, it is a problem that Git and Mercurial treat remote
branches/repositories as second class citizens. This is something that Bazaar
got right: Bazaar abstracts over the storage, so (except where limited by
network performance) you can do pretty much anything on a remote branch/repo
that you can do locally.

------
tomxor
I like what this guy has to say so far, but it's a bit surprising that someone
with his knowledge got this wrong:

> The Git staging area doesn't have to be this complicated. A re-branding away
> from index to staging area would go a long way. Adding an alias from git
> diff --staged to git diff --cached

I dislike the mess of crappy synonyms too, but git diff --staged is already an
alias, in fact it's the primary way I diff the staging area because it's a
well fitting synonym.

~~~
masklinn
> I dislike the mess of crappy synonyms too, but git diff --staged is already
> an alias

Problematically it's very little used, it's only mentioned once at the very
tail end of the `—cached` description, `--cached` is still the primary flag,
and the essay remains correct that references throughout the documentation are
inconsistent.

It's not exactly hard to miss, if e.g. you learn git through the Pro Git book,
the option is not mentioned once, and even the full text search has not
indexed it.

Furthermore, there are many other commands which take a —cached flag but don't
have a —staged alias (diff-index, submodule, check-attr, apply, rm)

------
zanchey
This article can be summarised as: 1\. Cache invalidation 2\. Naming of things

They seem like straightforward problems.

~~~
rntz
Could you elaborate for those of you who don't quite understand?

I understand the reference, but is there more to this comment than a joke?

~~~
zanchey
Sadly not.

------
nurettin
>> And the Git staging area should be an opt-in feature.

Thousands of yes! `git commit -a` just doesn't add the untracked files it is
the most annoying misfeature of any version control system. Oh, I added
unwanted project directories? I would then just remove them and put them to
`.gitignore`, or create a `.gitignore` prior to `init`.

Of course I could just alias `add -A . && commit -m` on every machine I ever
connect to for developing. There's a great, practical solution.

~~~
jononor
Automatically adding untracked files would make it extremely easy to commit
(and without other safeguards, push) unwanted changes. Annoying things like
node_modules or binary screenshots that now bloats the repo history forever
(unless one rewrites the history), or potential security breaches like
passwords/keys or logs/configs with confidential information.

~~~
nurettin
Then there is .gitignore_global for your node_modules/

------
qznc
Wow, usually criticism of git is attacking strawmen. This one is actually
reasonable and I agree with it.

------
bluetwo
I've just never found a reason to move on from SVN, to be honest.

I use GIT when I am forced to, but for everything under my control, SVN works
the best.

I was unaware you could get rid of the staging area in GIT. I may need to look
more into that.

~~~
influx
Git is over complicated for most people’s use cases, but I wouldn’t give up
the fast branching and the rebasing features.

~~~
BurningFrog
GIt is only as complicated as you make it. You don't have to use all the
features :)

~~~
qznc
No. That would be like handing a professional compound bow [0] to a newbie
instead of a simple one [1]. The note "only as complicated as you make it"
would be quite sarcastic in this case.

[0]
[https://outdoorsexperienceonline.files.wordpress.com/2013/07...](https://outdoorsexperienceonline.files.wordpress.com/2013/07/pse-
compound-bows.jpg) [1] [https://www.archery360.com/wp-
content/uploads/2016/05/tradit...](https://www.archery360.com/wp-
content/uploads/2016/05/traditional-bows.jpg)

------
yeukhon
Here are the commands I use:

git init

git clone

git checkout

git commit

git commit -m

git commit —amend

git rebase

git add/rm/diff [—cached]

git push

git branch

and a few more I can’t remember exactly.

I try to keep my git workflow simple. The most complex is probably checkout
abd rebase with several different branches.

~~~
saltedmd5
"I try to keep my git workflow simple," and yet I see 'git rebase' but no 'git
merge' in that list.

~~~
zegl
`rebase` is simpler than `merge` in larger teams/projects, as the history will
be much cleaner.

~~~
saltedmd5
Rebase is not simpler in any context - rebase rewrites history, which, if
branches have been pushed to remotes, then necessitates force pushes, which in
turn breaks any other instances of the same branch. By using rebase to "keep
history clean" you are largely undermining git's power as a DVCS.

Rebase as a tool is not inherently bad but it is definitely not _simpler_ than
merge - it introduces additional considerations, requires a deeper
understanding of git for effective use and is a dangerous tool in the hands of
people who do not understand what it is doing and in my experience most teams
that are using it as a core part of their workflow are doing so for the wrong
reasons (generally because of a fundamental misunderstanding of how branching
and merging works in git and _why_ it works that way).

~~~
kazinator
That is not correct; rebase _per se_ doesn't rewrite history.

Rebase is basically just cherry picks. You can rewind a branch and then
cherry-pick, so that the picks are non-fastforward. That's rewriting history.
Cherry picking without rolling back is fastforward, and so doesn't rewrite
history.

The Gerrit review system on top of Git is based on cherry-picking; it doesn't
rewrite history.

If some developers are collaborating on a feature which is on a branch, they
could agree from time to time to rebase that branch to a newer mainline.

Whether or not that is done by a history rewrite simply hinges on whether the
same branch name is used for the rebased branch or not. The rebase is what it
is: if you plant that rewrite as the original branch name, then you have a
non-fastforward change. If a new branch name is made, then it isn't a rewrite.

Merging a feature branch onto a trunk can always be done in a fastforward way
using rebase/cherry-pick.

Rebasing is provably simpler than merge. Merge depends on complications in the
git object representation which could be removed while rebase remains what it
is. If you only ever rebase, you never see a commit with multiple parents; the
feature is superfluous and turns git histories into hairballs.

~~~
saltedmd5
AFAIK rebase always rewrites history. If you rebase your feature branch, you
have at least rewritten the history of the local branch even if you then
delete it and push to a new remote branch. The _trunk_ will subsequently get a
FF merge.

Rebase might give you a "simpler" end result in terms of what the history
looks like but conceptually it is much less simple in terms of its mechanism
and its implications (e.g. rebasing a branch with multiple contributors screws
up the audit trail as it now looks like they made their changes at a different
time and in a different context to when they actually did) than the idea of a
graph with two branches and a merge commit.

I have seen teams with limited git experience switch from habitually rebasing
public branches to accepting merge commits and suddenly cure a whole host of
workflow problems.

If you _can_ rebase directly onto a fresh branch (not something I've seen)
then I am fairly sure that that's not part of your average workflow -
establishing new branches every time you want to update from trunk comes with
its own communication overhead too.

------
jonthepirate
In practice I have found the biggest missing feature that I wish existed is
the ability to pull a single file from a known ref on a remote server. For
example, give me the Dockerfile located in the root of the master branch tip.
Can't do that. Instead, you have to clone the entire repo first.

~~~
drchickensalad
Well you can clone with --depth=1, so performance wise it's not too bad at
all, but yeah still gets all the other latest files.

------
hyperion2010
While many of these criticisms are valid, they seem to be written from the
point of view of a user that doesn't really want control over their versions,
they just want versions.

I'd suggest that there are other tools for this, such as a properly configured
ZFS setup which are atomic beyond most users widest dreams (especially if all
the versions of a vim .swp file are retained).

If you just want versions, yes, git gets in your way, because git is about
control. Yes if you have all those versions you could go back and 'squash'
them into a commit, but can you imagine trying to bisect those to find a bug?!

Somehow I also get the feeling that this perspective is similar to that of a
friend who really didn't like the idea of pulling, and that he should just be
able to push to the remote repository. A seeming lack of awareness of the
collaborative side of version control and development.

Git isn't just about control, it is also about providing additional provenance
information and context needed in order to understand what code does and the
reasons why changes were made. Sometimes in order to get there we have a bunch
of low intention commits that we use to checkpoint, and let's be honest, how
many have created commits that put a tree in a broken state? I know I have.

The bit about making the documentation more consistent in its use of
vocabulary seems like something that will have a couple of pull requests by
the morning.

tl;dr If you want versions use ZFS if you want control and communication use
git. Talking defaults is always a good exercise. with and EDIT: show work
would be amazing ...

edit2: I just don't see the workspaces model passing the bag of dicks test. I
want to code, I don't want to have to become a community moderator and clean
up people committing all sorts of crap to my project and on the other side I
just wan't to code and not have to deal with the bags of dicks who are going
to gate keep their projects. There are just too many social edge cases and the
differences are mostly just semantic (iirc the way github implements forks is
basically as workspaces anyway...). That said, I would really like it to be
possible to have pull requests be part of the repo history instead of, say,
the listserv.

~~~
chrisan
Well said.

> Most people see version control as an obstacle standing in the way of
> accomplishing some other task. They just want to save their progress towards
> some goal. In other words, they want version control to be a save file
> feature in their workflow.

That was horrifying to me. We _religiously_ have a JIRA-XXX tag in our commits
so that every commit has some kind of perspective in addition to the comment.
This has been extremely useful in getting more context of why something was
changed when dealing with a long lived project spanning multiple developers.
Even if the commit message itself is weak or lazy we still have the user story
in JIRA to get full context.

------
Sujan
Workspaces please, now.

That would be so much nicer than the current need to fork.

~~~
beagle3
Are you familiar with the 'git worktree' command? It's rather new, and it
might be what you are after.

------
milofeynman
This may be sacrilege on here, but at my previous job I really enjoyed TFS.
It's been awhile but the workflow felt a little more intuitive than git. And
some of the merging features were nicer.

~~~
maxxxxx
I used to work with TFS but after working with git I can't stand it anymore.
But it shows that people have different tastes.

~~~
kylemuir
Strongly echo this sentiment. Last time I used TFS was probably in 2011 so
things might have changed significantly since then but off the top of my head
things I use to hate but now don't need to worry about since using git over
TFS:

\- Offline? Looks like you'll have to wait to make that change or any other
change for that matter since you can't check out files if you're offline. When
doing remote work this use to do my head in.

\- TFS On Premise putting readonly locks on every file you have. Why? Great
question. Made writing build scripts and their ilk much harder to accomplish.

\- Want to make a branch? That'll be an entire copy of the source code times
however many branches you want

~~~
maxxxxx
Add to that detection of new files or deletions outside of visual studio. This
is so easy in git and a real problem in TFS.

------
Grue3
Git staging area is nothing compared to Mercurial's branch/bookmark
ridiculousness. And the need to use plugins for things that Git trivially
supports out of the box.

------
waterphone
Reading this article is harder than learning how to use git the way it's
intended to be used.

