
Still hatin' on git: now with added Actual Reasons - MikeTaylor
http://reprog.wordpress.com/2010/05/12/still-hatin-on-git-now-with-added-actual-reasons/
======
ynniv
I have read a fair amount of documentation on git. I still don't even know
what most git commands do. When people recommend a rebase, I pretty much just
pretend that my repository isn't going to die. I wince every time I have to
revert a file, as "git checkout" is friendly but "git checkout <filename>" is
destructive without warning. I once destroyed a number of local files (I don't
remember how) that were thankfully still open in emacs.

git is awesome, and git is terrible, often for the same reasons. git is a
Sharp Knife, which is fantastic if you need one, and horrible if you don't. I
personally have basic distributed version control needs and git is far too
sharp for my needs. I have managed to not cut anything important off, but I
tread carefully around each command, often worrying that I will irrevocably
destroy my files.

So, +1 for being way better than previous version control systems, but a
friendlier modern version control might easily win.

~~~
pyre
> _I wince every time I have to revert a file, as "git checkout" is friendly
> but "git checkout <filename>" is destructive without warning._

From my point of view, that's like saying that 'rm -rf' is destructive
'without warning.' It does what it was meant to do, you can't expect
everything to warn you all the time in an effort to save you from yourself. It
would get really annoying, really fast if it asked you to confirm every time
you performed an operation in git.

'git checkout <filename>' == 'replace <filename> with the version of
<filename> in the index' (if the index is empty, then index == HEAD)

I'm also unsure how 'svn checkout <filename>' is less destructive.

[Note: Though these 'git debates' tend to get heated because you have
"frustrated people that don't understand git but are trying to use it" on one
side and "people that understand git and are frustrated at the 'ignorance of
newbs'" on the other side, this is not an attack.]

~~~
ynniv
_that's like saying that 'rm -rf' is destructive 'without warning.'_

Well, "rm -r" when run as root _does_ warn. You have specified a flag that
_explicitly_ doesn't not ask. Also, "rm" only ever destroys things, whereas
"git checkout":

    
    
      - creates a new branch (non-destructive, additive)
      - switches to an existing branch (non-destructive)
      - obliterates uncommitted changes to a file (destructive)
    

And the two major modes (destructive and non-destructive) have no syntactical
differences. "git checkout X/Y" is non-destructive if X is the name of a
remote and Y a file, and destructive if X is the name of a folder and Y a
file. Deep down, you know this just isn't right.

The chief problem with git is that the user interface is a simple reflection
of the mechanics and vocabulary used to implement it. That first part is great
for performance, and the whole thing might seem like a good idea if you happen
to be a filesystem hacker, it's arbitrary nonsense to the rest of the world.

git has been purposely architected so that you do not lose data. "stash"
allows you to temporarily store local changes while you test things out.
Extensive branching is key to sharing patches in a trackable way. Most
destructive commands actually alert you that data will be lost. I really like
this philosophy of never accidentally losing anything.

But the interface is inconsistent and needlessly complex, and should not be
defended. git could be vastly improved simply by changing the command
structure and vocabulary. Destructive commands could universally prompt unless
an option ("-f"?) is specified.

Maybe someone could make "tig", which is a proper version control system
organized from an everyday user's perspective, based on git.

~~~
avar
> Well, "rm -r" when run as root does warn.

No it doesn't, but perhaps you're using a distro (like RedHat) where "rm -r"
is actually aliased to "rm -ri".

------
dasil003
The complaints about git porcelain are totally understandable. The UI is
horrendous. However the internals are elegant and actually much simpler than
subversion, so if you understand the internals and do a little rote
memorization then suddenly you can manipulate your repository expertly,
creating better commits and handling unusual needs with ease. It's the
difference between being good with a unix shell and running windows
configuration wizards to do everything.

I don't expect the OA to grok that, however one thing that I hope the author
takes to heart is this:

With git plumbing it would be fairly straightforward to create a new porcelain
VCS combining any balance of elegance, simplicity and power that you desire.
With subversion on the other hand, it is almost unimaginable that they would
ever be able to correctly track a merge without a hundred bizarre edge cases
due to the fact that their repository structure muddles branches and
directories.

Why hasn't someone who knows git created a better porcelain yet? Probably
because once you know git well enough you don't want to give up any of it's
native power just to satisfy some noob's anxiety attack.

~~~
dlsspy
> Why hasn't someone who knows git created a better porcelain yet? Probably
> because once you know git well enough you don't want to give up any of it's
> native power just to satisfy some noob's anxiety attack.

They exist. gitx, magit, etc... are all great tools.

Most people who use it for any length of time seem to be just fine with it,
though. I can recognize a few things as... silly (e.g. checkout), but not so
much as to try to introduce a new verb and redefine the semantics just to come
up with a better separation when I already know what I'm doing with the tool.

I certainly don't have enough of a problem to write an entirely new interface
when the existing one continues to get incrementally better.

Similarly vi and emacs are both great editors whose power cannot be harnessed
by those who don't spend time trying to learn them. The vested are less
affected by idiosyncrasies.

------
kscaldef
> So I edit the file, fix the trivial conflict, and git commit filename.

This is a bit vague, but I'm guessing you mean you open up the file in your
text editor of choice and edit it directly.

What I do these days, probably originally prompted by exactly the sort of
frustration you describe, is to run "git mergetool", which I happen to have
configured to fire up emerge, but you can use any of a number of tools. Once
I'm done, git is happy and I can just push. Initially I was a little grumpy
about learning how to use emerge when before I just edited the file with all
its <<<< and ==== and >>>> directly, but after learning the tool, I wish I'd
been doing conflict resolution this way back when I used CVS and SVN. It's
really much nicer and less error prone.

So, I hope that's a helpful hint. You're absolutely right about a lot of the
unhelpful & unintuitive messages and commands.

~~~
jrockway
Isn't the message intuitive? Git usually says, "merge conflict, fix the
conflict, stage the fix, and run git rebase --continue". If you follow the
instructions exactly, everything works.

Now, I know he wasn't doing a rebase (but should have been), but still; when
you edit things in git, you stage them, and then commit them. After a merge,
the index is mostly ready to be committed, except for the conflicts. So you
are supposed to manually stage the fixed conflicts, and then plain "git
commit". Simple. It works exactly like anyone who understands git would expect
it to.

Now, if you don't like the index, then git simply isn't for you. It's a core
concept, you just can't avoid it. (Personally, when I was using non-git
version control systems, I often had a separate working copy I used exactly
like git's index. So having that built in is a joy for me.)

~~~
kscaldef
No, “! [rejected] master -> master (non-fast forward)” is not intuitive. The
example you give is one of the pleasantly surprising examples of a good error
message from git. But, by and large, git's error messages are pretty awful.

~~~
jrockway
What else should a one-line summary of syncing master -> master say?

"Hello dear user. The remote server refuses to accept your branch, because it
would delete information. As a result, your request has been rejected. Please
rebase and try again. If you need help, hang up, and then dial your operator."

Personally, I am fine with [rejected]. I figured it out, after all...

~~~
stevelosh
Mercurial says this in the same situation:

    
    
        abort: push creates new remote heads on branch 'default'!
        (you should pull and merge or use push -f to force)
    

Yes, it's two lines. I think we all have large enough monitors now that an
extra line of output to be a bit friendlier is a good thing.

~~~
jrockway
Where does it end? Should a compiler say "unknown identifier 'foo': perhaps
you need to import a header file that defines foo, or spell it better, or
write code that works instead of this garbage" instead of a simple "unknown
identifier: foo"?

No. You just look up the error in the manual, and never think about it again.

~~~
ryanpetrich
LLVM will attempt to guess the proper name of a misspelled identifier for you:
[http://blog.llvm.org/2010/04/amazing-feats-of-clang-error-
re...](http://blog.llvm.org/2010/04/amazing-feats-of-clang-error-
recovery.html#spell_checker)

------
Qz
All the people suggesting various git commands he should be using are
completely missing the point.

~~~
pyre
The point -- as I understand it -- is that the author feels that a version
control system should act like X. So he takes a random version control system,
reads about it briefly, and then tries to use it assuming that it acts like X.
The version control system doesn't act like X, which leads to problems for the
author. Author writes a blog post about how random version control system
sucks because it doesn't meet his assumptions.

~~~
Xurinos
That is what I got out of it. A big clue was his "git commit filename". git is
not svn. Is git's greatest sin really that it uses a few of the same words
with different meanings from how they have been used in the past?

Someone in another post mentioned revert. svn revert file == git checkout
file, while git revert patches out a whole change. It actually makes sense if
you forget about cvs and svn's version of the API.

I saw the word "intuitive" thrown around...
<http://www.asktog.com/papers/raskinintuit.html>

~~~
phaylon
You mean it actually makes sense if you forget the other VCS versions of that
command, _and_ know git's? Because ignoring all VCS experience I have,
"checkout" would indicate to me that I could "check something out" as in "have
a peek at it."

~~~
Xurinos
In git, it means "checking out the current branch version of a file" or
"checking out an entire branch". Pulling all the patches from another resource
also makes sense.

Think of checkout as a local thing to do, and pull/fetch as a remote action.

It can make sense if you do not have prior associations. It is not as if the
English language is not terribly abused and ambiguous in our languages. What
does "cancel" mean? In linux with CUPS, it cancels a print job. To cancel a
process, you have to kill it, but you cannot kill print jobs. Shocking.

What is OOP? How does that definition change between C++, Java, Smalltalk,
Lisp/CLOS, and vimscript? How about MVC? What about the differences in GUI
lingo between Object Windows, McClim, Gtk, and qt? In other words, you have to
learn the language of each different tool you use, and it is unreasonable to
expect that it is the same as your other tools.

Aside from that, it is terribly unfair to try to compare svn to git in any
meaningful architectural sense; they are completely different creatures that
accomplish some of the same goals. git does it better. And it uses different
language to describe its new approach.

I have not had a difficult time switching between using svn and git after
using both for a long time, except for missing nice features like "git log -p"
and "git add -p" when I work with svn. It seems petty to me to quibble over
command names.

~~~
phaylon
"What does "cancel" mean? In linux with CUPS, it cancels a print job. To
cancel a process, you have to kill it, but you cannot kill print jobs.
Shocking."

Those are different projects. Git is one. OO is an abstract concept, we're
talking about git communicating to the user, and what level the user must have
to understand that communication.

The problem is not that git and svn have different opinions on what "checkout"
means, it's that _git itself_ has different opinions on what it should mean.

Again, why is it so bad that we say the interface could be better? Why do so
many of those that managed to get a grip on this feel the need to strictly
deny that there could be any problem in the interface itself?

Yes, Linus designed it. But he's just human too.

I still have yet to see a single useful advantage of mixing up different
meanings in the same word. All I get is "It is that way, learn it or GTFO."

------
moe
Don't give up faith my friend. Git and mercurial are not the end of the story.

I'm optimistic that, within another few years (give or take a decade), someone
will come up with a VCS that's both powerful _and_ usable. :-)

Other than that: I enjoyed your article. Very nice walk through some of the
problems and hair-pulling that I'm going through regularly, too.

Pro-Tip: My life with git got a bit easier since I keep this big bucket of
paste-ready shell-snippets near my terminal window. You know, those handy
little 14-liners that look like line-noise at a glance, but get you out of
"situations" when chanted.

~~~
DTrejo
link to your 14-liners?

~~~
moe
Sadly I guess they most of them don't make much sense without context or the
discussion/thinking that led to them.

But for example, this is my snippet for merging my current branch into master
and pushing that to origin (hopefully merging/cleaning up in between as
appropiate):

    
    
      # on local branch
      git fetch origin master
      git rebase origin/master
    
      # tidy up
      git rebase -i origin/master
    
      # merge to master
      git checkout master
      git merge $my_branch
    
      # push to master
      git push origin master
    
      # go back to local branch
      git checkout $my_branch
    

I didn't originally come up with all that, mind you. It's pulled from a
workflow tutorial and you'll find basically the same procedure recommended in
every tutorial (keywords: feature branches and "don't work directly on
master").

And yes, I paste that multiple times per day and still throw up a little bit
inside my mouth every time.

~~~
dlsspy
Your first two commands can be replaced with (one time):

git config --global branch.autosetuprebase always

Then "git pull"

Without doing the config change, you can just do a "git pull --rebase"

Your "git push origin master" is typically what I spell "git push" (recommend
"git config --global push.default tracking" just to make sure you're pushing
the same stuff that's tracked automatically).

So, my every day workflow (rebasing upstream and building on top of stuff)
looks like this:

    
    
        git pull
        [edit a bunch of files]
        git add -p
        git commit
        git push
    

My mouth is minty fresh.

~~~
moe
I guessed I'd have something like that coming and yes, that might work.

Except when you regularly need to create new working copies on varying git
versions where those config details tend to differ in subtle ways. Too often
did I have to wrestle problems on someones MacBook simply because the config
or command syntax changed slightly between a few git revisions.

I'd go as far as to say that _local_ (i.e. not repository-wide) config
settings that change the semantics of how the commands work are deeply harmful
in a team-setting.

It's yet another layer of complexity that you have to keep in mind when
debugging problems.

Sure, it can save typing when everyone involved uses the same git version
_and_ has a good understanding of git. Unfortunately neither has been the case
on most projects I've been involved with.

------
amanfredi
'git commit -a' is not the answer to your merge problem. You should 'git add
<file-you-merged>', then commit.

~~~
llimllib
Also, doesn't it tell you to do thst when the conflict happens?

edit, yup:

    
    
        $ git status
        # On branch master
        # Your branch and 'origin/master' have diverged,
        # and have 1 and 1 different commit(s) each, respectively.
        #
        # Unmerged paths:
        #   (use "git add/rm <file>..." as appropriate to mark resolution)
        #
        #       both modified:      readme
        #
        no changes added to commit (use "git add" and/or "git commit -a")

~~~
MikeTaylor
Nope.

~~~
llimllib
see above, I edited to add what I'm talking about

~~~
MikeTaylor
My bad, I was wrong on this.

I would just delete my comment, but that would leave your replies looking
dumb. Instead, feel free to downvote my earlier "Nope." into oblivion.

~~~
llimllib
No worries, color me unoffended. I should have generated the message I was
talking about before I posted anyway.

------
kgrin
1) "pull --rebase" might help with one of the particular problems mentioned
(though obv. not appropriate for all occasions)

2) More broadly: yeah, if you expect it to work Just Like Things You Already
Know, it just won't work out for you. It really is a totally different
workflow, and it either works for you, or it doesn't. Empirically, it works
great for many people/organizations (though, absolutely, not without some
trade-offs). But truthfully, yeah, if you're a single developer, disinclined
to use some of the niceties and very interested in maximizing
simplicity/reducing keystrokes... svn really may be a better fit.

------
dreyfiz
> Cheap branching is better than expensive branching, sure, but that’s like
> saying influenza is better than cancer. I’d rather just not be ill at all,
> thanks.

Please stop posting this know-nothing's uninformed rants.

------
andreyf
_I think the move back to CVS/Subversion might be the way to go_

From your complaints, it sounds like you'd really like Mercurial. Give it a
shot.

~~~
sshumaker
No, mercurial has exactly the same problem he mentioned: You want to push
'some' of your local uncommitted changes, but if the remote head has also
changed you often can't. (hg shelve is a poor substitute).

This happened to my team enough in practice that they rebelled and forced a
switch back to SVN.

~~~
lars512
Looks to me like he had two problems.

The first was that git gave very poor error messages and needed a confusing
series of command-line options. In mercurial, this is much better; the pull
creates a new head, and tells you what to do if you want to merge them. Commit
is one-step, instead of via the staging area. It just gives more feedback,
more usefully, and requires less knowledge about the underlying
representation.

His second problem is common to both systems, namely that during a merge-with-
conflicts you have to essentially re-commit both changes. This is actually a
kind of safety feature, since the conflicted change might have screwed up
something and it's up to you to make sure the code is in a good state after
the merge. If the merge isn't conflicted, then Mercurial (and probably git)
does it cleanly without forcing you to read over your teammate's change, which
he hates doing.

~~~
sshumaker
There's two problems with this approach:

1\. If you have other uncomitted changes, you're simply hosed. You can't
continue with the merge process with local changes at all.

2\. It pollutes your change list with unrelated, unconflicted changes
belonging to your teammate (the whole changeset).

~~~
pyre
> _1\. If you have other uncomitted changes, you're simply hosed. You can't
> continue with the merge process with local changes at all._

That's what 'git stash' is for. It stores away all changes to the local tree
and index (but leaves ignore and untracked files alone). Then you can perform
operations like changing branches, doing merges, etc. Then you run 'git stash
pop' or 'git stash apply' to pull back your changes. (You'll need the '--
index' option if you want you index back in the same state though. It won't
'trash' your changes. They will only be in the working tree, not in the
index.)

> _2\. It pollutes your change list with unrelated, unconflicted changes
> belonging to your teammate (the whole changeset)._

Yes. The automatic algorithms have failed to determine how to cleanly merge
the changes, so you have to rebuild the final state that you want the tree to
be in. When you commit that state, it commits the diff of what is needed to
resolve the conflict.

Remote:

    
    
      A-B-C
    

Local:

    
    
      A-B-D
    

Final:

    
    
      A-B--D--E
         \_C_/
    

You are basically building the state that you want the source tree to look
like in commit 'E'. Then the diff of what needs to be done to resolve the
merge of states 'D' and 'C' is recorded in the merge commit 'E'.

It's not 'polluting your change list' with those changes. It's setting your
index into what the final state will look like and pointing out to you what
the files that it couldn't figure out. You just need to make them look like
they are supposed to (i.e. resolve the conflicts), put them into the index
(i.e. 'git add') and push out the merge commit (i.e. 'git commit').

~~~
sshumaker
1\. Yes, git stash is an option. Unfortunately, if it's anything like
mercurial's shelve, it's not a good one. What happens when you 'unstash'? Does
it properly give you conflict markers, or does it generate patchfiles?

The latter is mercurial's behavior, and it sucks. To do the former, the stash
command needs to also track the repo version that the local changes were based
upon, so it has the historical information to present conflict markers
(effectively, enough info for a 3-way merge).

 __FWIW, This is actually the only thing holding my team back from switching
back to hg. If hg shelve were part of the standard distribution and rock solid
(multiple shelves would form a queue, no chance of corruption, and unshelve
preserved historical information so it could properly merge conflicts), the
world would be a better place. :) __

2\. Yes, I know WHY it does what it does. But the way it presents this info is
unfortunate, because after a merge it's far more difficult to know exactly
which changes you were making - many programmers are in the habit of reviewing
their changes before committing. Does GIT let you easily tease out these
differences?

[edit: call to action]

~~~
pyre
> _many programmers are in the habit of reviewing their changes before
> committing. Does GIT let you easily tease out these differences?_

I'm unsure what you're asking here. I can say that 'git diff' shows you the
difference between the working tree and the index and 'git diff --cached'
shows you the difference between the index and the most recent commit (i.e.
HEAD). Both of those accept a filename as an argument to just git the diff of
that file in the respective spaces. All of the changes that cleanly merged are
in the index, and all of the unresolved conflicts are not when you first enter
the 'unresolved conflicts' state while attempting a merge.

If your asking something more specific than that you'll have to rephrase or
explain for me.

------
weavejester
Mike, Git seems unintuitive because you don't have a good grasp of what it
does behind the scenes. Imagine trying to get to grips with a Unix shell, if
you had no concept of files or directories. In such a scenario, even a simple
command like "cat" would seem incomprehensible.

If you'll indulge me, I'd like to propose a thought experiment.

* * Designing a patch database * *

Consider you're responsible for administering a busy open source project. You
get dozens of patches a day from developers and you find it increasingly
difficult to keep track of them. How might you go about managing this influx
of patch files?

The first thing you might consider is how do you know what each patch is
supposed to do? How do you know who to contact about the patch? Or when the
patch was sent to you?

The solution to this is not too tricky; you just add some metadata to the
patch detailing the author, the date, a description of the patch and so forth.

The next problem you face is that some patches rely on other patches. For
instance, Bob might publicly post a patch for a great new scheduler, but then
Carol might post a patch correcting some bugs in Bob's code. Carol's patch
cannot be applied without first applying Bob's patch.

So you allow each patch to have parents. The parent of Carol's patch would be
Bob's patch.

You've solved two major problems, but now you face one final one. If you want
to talk to other people about these patches, you need a common naming scheme.
It's going to be problematic if you label a patch as ABC on your system, but a
colleague labels a patch as XYZ. So you either need a central naming database,
or some algorithm that can guarantee everyone gives the same label to the same
patch.

Fortunately, we have such algorithms; they're called one-way hashes. You take
the contents of the patch, its metadata and parents, serialize all of that and
SHA1 the result.

Three perfectly logical solutions, and ones you may even have come up with
yourself under similar circumstances.

* * Merging patches * *

Under this system, how would a merge be performed? Let's say you have two
patches, A and B, and you want to combine them somehow. One way is to just
apply each in turn to your source, fix any differences that can't be
automatically resolved (conflicts), and then produce a new patch C from the
combined diff.

That works, but now you have to store A, B and C in your patch database, and
you don't retain any history. But wait! Your patches can have parents, so what
if you created a 'merge' patch, M, with parents A and B?

    
    
       A   B
        \ /
         M
    

This is externally equivalent to what you did to produce C: patches A and B
are applied to the source code, and then you apply M to resolve the
differences. M will contain both the differences that can be resolved
automatically, and any conflicts we have to resolve manually.

Having solved your problem, you write the code to your patch database and
present the resulting program to your colleague.

* * A user tries to merge * *

"How do I merge?" he asks.

"I've written a tool to help you do that," you say, "Just specify the two
patches you want to combine, and the tool will merge them together."

"Um, it says I have a merge conflict."

"Well, fix the problem, then tell the system to add your file to the 'merge
patch' it's making."

Your colleague dutifully hacks away, and solves the conflict. "So I've fixed
the file," he says, "But when I tell it to 'commit file' it fails."

"Remember, this is a patch database," you reply, "We're not dealing with
files, we're dealing with patches. You have to add your file changes to your
patch, and then commit the patch. You can't commit an individual file."

"What? That's not very intuitive," he grumbles, "Hey! I've added the file to
the patch, but it tells me the merge isn't complete!"

"You need to add all of the files that have differences that were
automatically resolved as well."

"Why?!"

"Because," you explain patiently, "You might not like the way those files have
been changed. It needs your approval that the way it's resolved the
differences is correct."

"Why to I have to re-commit everything my buddy has made?" he complains,
"Seriously, I want to just commit _one_ file. What the hell is up with your
system?"

~~~
ewjordan
_Mike, Git seems unintuitive because you don't have a good grasp of what it
does behind the scenes._

In other words, Git's abstraction is leaky. That's usually considered a bad
thing in our profession.

Except that since it's Git, we all use it, and it's better than the
alternatives, we all pretend that's a _good_ thing in this case.

I'm fine with the way Git works internally, and by now I've come to deal with
the fact that sometimes it takes five commands to carry out what is, in fact,
one desired action.

But Git's main point of failure is typical of all young projects that are in
any way involved with Linux - there's no effort to make it elegant or pretty,
and anyone that points that out and suggests that _maybe things could be
easier_ is ridiculed for not understanding it.

Usually "That's not very intuitive" is, in fact, an indication of something
that could be improved...

~~~
tow21
_In other words, Git's abstraction is leaky_

No, it means you're using the wrong abstraction.

As you change your codebase, files are modified. To the untrained eye, it
looks like this a simple linear progression of history, and you just want to
record savepoints as you go along. CVS lets you pretend this is the case.

Actually, that's not the case at all. What you actually want to record is the
changes you're making, and the relations between them. In the vanishingly
small edge case where you never have any collaborators, never any experimental
code, you never need to backtrack, you never need to work on more than one
portion of the code at a time - this is isomorphous.

The rest of the time, it's not. CVS & SVN try to stretch the first abstraction
to take care of these differences, but fail.

git makes you face up to the fact that your abstractions are wrong.

~~~
moe
_No, it means you're using the wrong abstraction._

It seems a large number of users prefers to work at a different level of
abstraction than git requires.

 _git makes you face up to the fact that your abstractions are wrong._

Strangely mercurial tackles the exact same abstractions, yet has a much
friendlier user-interface. The standard-rebuttal at this point will be "Fine,
use mercurial then". I wonder if, at some point, more users will start doing
that than the git-community would like. I, for one, am certainly tempted, but
have so far held out due to the switching cost and because hg is, of course,
not without flaws either.

However, I don't think this "If it hurts then you're doing it wrong"-attitude
can be healthy for git in the longterm.

A bad user-interface remains a bad user-interface, no matter how you spin it.
The big problem I see is not even with git currently having this bad user-
interface, but rather with the widespread reluctance in git-circles to even
think about ways to improve it.

------
santry
Seems to me he should just use Subversion. He has a mental model of how
version control works (based on his prior use of CVS) and apparently doesn't
wish to invest the time to learn git's model.

------
timdorr
Someone needs to introduce this guy to the concept of rebase and then maybe
he'll get on board with branching. And _then_ maybe some of his problems with
be solved. I used to have these issues until I started branching and rebasing
off a core "develop" and "master" branch.

------
twoism
Am I the only one who reads posts like this and wonders why people make
merging/pushing/pulling with Git so complicated? It's really not that hard.
Sure there are plenty of esoteric and strange things you can do with Git but I
have seen devs go from zero knowledge to using the basics in no time. Some
never have to go beyond the add, commit, push, pull and merge commands (with
their respective switches) and work quite happily. Sure they aren't Git
experts by any means but they are able to get their work done and the tool is
just as transparent to their workflow as any other SCM tool.

------
pyre
I sort of take issue with this statement:

 _“git is bad for me because it makes assumptions about how I work that don’t
match how I actually work”_

I think that it's the other way around. git was built with a specific type of
workflow in mind. If you take git and try to insert it into your current
workflow with a minimal understand of git (or its intended workflow), then
isn't it really _you_ that are making assumptions about how the tool works,
rather than the tool make assumptions about how you work?

{edit}

    
    
      - I make a one-line change to a single file.
      - I commit my change.
      - I git push, only to be told “! [rejected]	     master -> master (non-fast forward)”. 
        (This is git’s cuddly way of telling you to do a pull first.)
      - I git pull, only to be told
        “CONFLICT (content): Merge conflict in filename.
        Automatic merge failed; fix conflicts and then
        commit the result.”
    

This is because you've created your local tree as such:

    
    
       A-B-C
    

And the remote tree looks like:

    
    
      A-B-D
    

When you run a 'git pull' it's a combination of two operations:

    
    
      1. 'git fetch' or 'git remote update' which updates your local
         copy of the remote tree. This is stored locally in the branch
         <origin_name>/<branch_name> (e.g. origin/master).
    
      2. 'git merge <branch_name> <origin_name>/<branch_name>' most
         of the time this merge is a fast-forward merge which you don't
         notice at all. It's when your local commit 'C' has conflict
         with remote commit 'D' that you run into an issue.
    

In the end your tree will look like:

    
    
       A-B--D--E
          \_C_/
    

Where E is a commit with that resolves the conflict and has two parent commit
IDs which point at D and C. In general, you want to just avoid this kind of
stuff in the first place by either using rebase or doing the 'git pull'
_before_ you commit you changes, _then_ push them out.

[ I'm having a hard time picturing how you would have resolved this with SVN.
If you have a conflict between a local file and an update that you are pulling
down with 'svn up' what happens? (I've not used SVN extensively) ]

{edit}

> _Well, darn. So, OK, no problem, I already fixed the conflict, so now I’ll
> just git merge again to get it to make its world consistent, right? Nope:
> “fatal: You have not concluded your merge. (MERGE_HEAD exists)”. Well, duh!
> I was telling you to merge, you stupid git. You’re the branches-and-merges-
> are-easy version-control system around here._

You're telling git to start a new merge, not complete an in-progress merge.
Think of 'git merge' as 'create a commit that merges these two things
together.' When you run into a conflict 'git merge' tells you, "Sorry I
couldn't automatically resolve this for you, but I got you as far as I could
go. You'll have to manually resolve these conflicts and create the merge
commit." At this point your working directory is in a state of 'middle of a
merge.' You just have to resolve the conflicts that were pointed out to you
(adding them to the index once they are resolved), and run a 'git commit' to
push out the merge commit.

~~~
jrockway
"svn update" will randomly delete your work with no way to ever get it back.

I don't think new git users migrating from svn actually understand that they
can rollback any changes git makes. If a pull goes bad and you don't want to
deal with it, just reset to your last head. All is forgotten until you feel
like fixing it. And no data is ever lost.

(svn loses data by merging everything it can with your uncommitted changes,
and then barfing when it can't do the merge. You're left with a bunch of
conflicted file, and a bunch of merged files, with no way to ever get the
unmerged files again. With git, this simply cannot happen. Git will not touch
untracked files or unstaged changes, it will just die. And what it does
automatically merge can be unmerged with reset. Or, you can commit the
conflict markers to a separate branch, deal with it later, undo that commit,
and move on with your life. Git makes easy things a little harder, but hard
things easy. Subversion makes anything except hair loss very difficult.)

Oh, and also, the OP really wants his tree to end up like:

    
    
        A--B--D--C
    

This can be achieved with "git pull --rebase" (or automagically with the
config key branch.<name>.rebase = true). That is what Subversion does, modulo
the ability to revert to your original branch.

~~~
AngryParsley
_"svn update" will randomly delete your work with no way to ever get it back._

No, it will not.

 _You're left with a bunch of conflicted file, and a bunch of merged files,
with no way to ever get the unmerged files again._

Umm... no? Take a look at conflicted-filename.mine. Whenever there is a
conflict, svn appends .mine to your copy of the file. After that, it creates
conflicted-filename.r123 and conflicted-filename.r124, and goes crazy with the
angle brackets in conflicted-filename.

I've lost plenty of data with git. Most of it has to do with innocuous-
sounding commands that don't ask for confirmation when deleting data. For
example, git checkout filename is equivalent to svn revert filename. Of course
git checkout branchname does something completely different. If a branch and a
file share the same name, git will default to switching branches, but that
doesn't stop bash autocomplete from ruining the day.

Here's a crazy idea: If you have an innocuous action and a dangerous action,
do not label them with the same command.

~~~
jrockway
_No, it will not._

Yes it will. Imagine you check out revision 1, which consists of two files:

    
    
        foo:
          a
    
        bar:
          b
    

You do some hacking, and end up with:

    
    
        foo:
          a
          
    
        bar:
          b
          d
    

While you were doing that, though, someone else committed, revision 2, which
is:

    
    
        foo:
          a
          b
    
        bar:
          c
    

You go to commit your changes, and svn tells you you can't, because you are
out of date. So you have to svn update. Now you have:

    
    
        foo:
          a
          b
    
        bar: CONFLICT
          >>>>
          b
          d
          ====
          c
          <<<<
    

Now, how do you roll back to what you had before you updated? The state of
"foo" has been lost forever by the successful merge.

 _For example, git checkout filename is equivalent to svn revert filename._

Annoying, maybe, but this is user error, not design error. With git, if I want
to losslessly discard my working copy, I can just "git stash". If I want to
losslessly update my svn working copy, though, I have to make a copy myself,
and then manage the copy.

By your logic, "rm" is flawed because it doesn't ask for confirmation when you
pass -f instead of -i. Well, yeah. Sorry.

~~~
AngryParsley
You didn't lose any data in that scenario. Every line of code you wrote still
exists in those files. The problem is that you are in a conflicted state that
must be manually resolved. Unfortunately, making updates atomic across a
branch has disadvantages. For example, svn lets you update individual files or
directories instead of the whole branch. If you want to avoid this pitfall in
the future, run "svn merge --dry-run -r BASE:HEAD ." before a real update. (I
wish svn update had a --dry-run flag. Just because git is bad doesn't mean svn
is perfect.)

Also, your scenario is extremely unlikely. I've used svn for 5 years and I've
encountered that problem once. It was for a binary file. Two versions of the
same image. It's not very often that two developers create a new file with the
same name at practically the same time. It's even less often that those files
can be properly merged.

 _By your logic, "rm" is flawed because it doesn't ask for confirmation when
you pass -f instead of -i. Well, yeah. Sorry._

$ git checkout blah

This command either switches to branch blah or it erases all uncommitted
changes in a directory or file named blah. Without more information, you can't
tell. I find that frustrating and annoying. Your analogy would be more
accurate if rm somename was the equivalent of apt-get update, and rm othername
was rm -fr othername. Oh, and somename is never tab-completed but othername
is.

~~~
pyre
> _You didn't lose any data in that scenario. Every line of code you wrote
> still exists in those files. The problem is that you are in a conflicted
> state that must be manually resolved._

I think that the point is that file 'foo' has already been merged, regardless
of the conflict in file 'bar'. There is no way for you to revert to the pre-
update state. In git, your previous commit still exists in the objects store
even if it is no longer connected to the tree. And garbage collection won't
even clean it out right away because it is still in the reflog ('git reflog').
The point being that once something is committed, it's permanently (barring
garbage collection) in the repository. Whenever you make a change to a commit,
a new commit is created, some pointers are changed, and the old commit still
remains in the repository.

> _Oh, and somename is never tab-completed but othername is._

Responsibility for the tab-completion falls squarely on your shell (or where
ever you got the tab-completion setup from). Don't point your finger at git
and say, "git sucks because bash tab-completion screwed me up." Neither rm nor
git can control how your shell bothers to determine tab-completion.

~~~
MikeTaylor
Still, it can't be right that "get checkout foo" does one of two COMPLETELY
different things depending on whether or not there is a file called foo in the
current directory. Surely one of those two commands should have a different
name.

~~~
thenduks
I've always felt like `git branch` could be the one to switch branches (since
it's used to create them too). But `git checkout -b` also creates branches...
I think semantically checkout is the right command for this.

It's never come up as a problem because I tend to know what files are in my
project and I also tend to know what it is I'm about to/want to do. I very
rarely switch branches with a dirty working copy anyway and my branches are
never named even remotely close to what files are named (by coincidence, I
suppose, but I name branches after: 'releases' which have names like "2.2.2";
'bug fixes' which have names like "bug2598" or 'features' which have names
like "dashboard-rewrite" and "chunk-load-thumbnails").

------
sqrt17
Duh. I've never got the hang of git either, but I figure git is for those
people who don't get enough mileage out of Mercurial because they have 42
megatons of source code, or need git rebase, or just want to shock and awe
their friends. But no need to hate git for that. No one's forcing you to use
it. You don't like vim? Go ahead and use emacs/gedit/eclipse/perl. You don't
like git? Use mercurial. Or subversion, if it makes you happier. Merging is
_always_ a pain with VCS, more so with DVCSs because they let you conveniently
forget a bunch of patches in that repository copy on the USB stick in your
back pocket . And these long mumblish version numbers are also going to stay -
but they're not so bad when you use a graphical repository browser (hg
view/hg_web/git-web/tortoise _).

And if you just need versioning for a couple .doc files: use Apache's
mod_dav_svn and be done with it (i.e., you can mount it as a directory in
Nautilus or the Mac finder and it _just works _). Even though I like all the
fancy stuff in Hg, I started using it and never looked back because it's
always been painless. (Oh, and you can work_ directly* on the repository,
version history is mostly useless because it reflects weird program behaviour
including temporary files, but you'll never get any conflicts unless you mess
up _really_ badly).

~~~
MikeTaylor
"But no need to hate git for that. No one's forcing you to use it. You don't
like vim? Go ahead and use emacs/gedit/eclipse/perl. You don't like git? Use
mercurial"

If only this were true. But it's not: if I work on a project where my
colleagues keep the source in git, then I have to use git (whereas if they use
vi, I am still at liberty to use emacs). That's the problem with CVSs -- they
have a lot more inertia than most other tools because they are, by nature,
shared by groups.

~~~
ionfish
I presume you haven't come across Hg-Git, then.

<http://hg-git.github.com/>

~~~
MikeTaylor
I've seen things like this. I would be very wary about trusting them with my
precious source code, wouldn't you? However good they are, they're an
additional layer in which (A) things can go wrong, and (B) you're insulated
from the reality that you need to access to fix problems.

I fear that Hg-Git would be one of those things that is an absolute delight
for as long as it Just Works, then abruptly transitions into an absolute
horror the moment something goes wrong.

~~~
fragmede
I suspect that many people feel the same about computers in general...

I don't use Hg-Git, but I do use git-svn. Just like Hg-Git, I'm sure there are
some caveats, but what finally got me to switch is that (at least for git-svn)
it's a real live Git repo. Which means the code I write, my actual work, is
all still there. The fix to my nightmare scenario? I do a fresh svn checkout
to a new directory, and I do git checkout, I copy the files out, I do an svn
commit of that version of files.

Definitely far from ideal, to be sure, but in my mind, knowing that was the
worse case scenario, put me quite at ease. (Sure I'd read _about_ git svn
dcommit, but knowing that did set my mind at ease.)

------
angrycoder
I get the idea that he wants version control that 'just works'. Unfortunately
version control, like most other software, has become complicated.

He needs to spend less time typing and more time reading. I don't think git is
something you can reason out just by throwing commands at it.

------
UnknownSource
The fact that you need to install either Cygwin or a 100MB collection of Msys
files put us off it for our project. We stuck to Mercurial because we thought
it would be easier for newbies to get involved, and it seems to be working.

Also, unlike Git, Mercurial is coded in Python.. Git is a mesh-mash of half a
dozen different languages. I'm guessing long-term, Mercurial will be
maintained better (but might be wrong).

------
Mc_Big_G
The length of the explanations in this thread says something about the
usability of git. Not hating, just saying.

------
overgard
I generally agree with the sentiment of this article, though I feel like I
need to get better at using git anyway. It's a leaky abstraction and very
unintuitive, no doubt, but after using Git regularly I just can't go back to
subversion, the workflow in Git is so much nicer (when everything goes as
expected, at least...)

------
alfredp
I felt the same way about git but I wasn't able to articulate it as well as he
did. Go mercurial!

------
IsaacSchlueter
We all need to throw a tantrum sometimes when things don't work the way we
like. It's ok. Let it out. We're computer users, we understand.

...feel better?

Now shut up and learn how to use your tools.

~~~
jodrellblank
It's true. www.ventatme.com isn't registered...

