Git needs a new interface
blog.reverberate.org
blog.reverberate.org
No, you can't, and I'd be horrified if I ever worked in a system that did that again.
First, there's never an excuse to not commit. What you commit is not necessarily what you publish. rebase -i to clean up artifacts of the junk you've done if you're worried about it (I always do it that way).
But more importantly, when I pull code and what ends up is worse than what I had before, I'd like to go back to where I was.
I think Git's alternative of stash, pull, pop is an improvement. That way I can fix conflicts properly at each stage, and handle big changes before reapplying my hacked up stuff.
A huge motivation for using a DVCS is that you can commit as many times as you want without worrying that your in-progress, hacky changes will affect someone else's work. Combined with git's powerful branching, you should never have an excuse not to commit a change set before a merge.
Also, he whines about not being able to merge with uncommitted changes, and then gives an example of how to do it! Ridiculous. It's three intuitive commands. That's what stashing is for.
I do agree with his points about git reset. The args are very confusing. Reverting to the most recent commit should be more intuitive. However, these arguments do not in any way point toward a need for an entirely new interface.
No, but the sum of the arguments does.
I'd wager that most git users feel that the UI is more complex than it should be and outright hostile in many situations. The problem is that not many people are deep enough into all the concepts to make a coherent proposal for something better. And those who could often don't see the problems anymore because it's just the way it is for them (can we coin the term VCS-blindness right here?).
It's sort of a chicken/egg problem and hard one.
I, for example, am not exactly happy that my standard procedure for pushing changes upstream has to involve 7 commands and two rebases. But so far I've had neither the time nor the insight or motivation to come up with something better. My general feeling is that git is still exposing itself at too low a level. That's also the reason why so many people are using it "wrong" (wanna make a bet on how many projects on github consist of only a master-branch where everything happens?).
Consequently my best stab would be that git could perhaps use a bit syntactic sugar for the most common workflows. There are not so many of them after all - why does almost everybody have to repeat the same 7 commands every day?
The concepts underlying git -- a content-addressible filesystem, trees, commits, etc -- are elegant. They're nearly pure. The "front end" exposure on the command line? Not so much.
What this article should say is:
1. Git needs a shared-library interface, so that interfaces to git can be built without quoting and parsing (the two most error-prone operations in computing).
2. Git's default scripts are a mix of shell, perl, and C that all parse command-line options in their own unique way. Eventually you find what works, but it's by random experimentation rather than by noticing patterns.
3. Git's default scripts are bloated. Quick, how many different ways are there to rebase ("git rebase", "git pull --rebase", ...) or fetch ("git fetch", "git remote update", ...)? Since the code is not necessarily shared, these operations may not even be exactly the same... just mostly the same.
Basically, git is the best version control system available -- but that doesn't mean it's not a software engineering nightmare. It is.
("So why don't you fix this?", you ask? Easy: git works so well that this theoretical annoyance is very low on my list of things to fix. In a perfect world, all software would be beautiful snowflakes. In the real world, there's always something that needs even more fixing.)
Every special tool I've ever wanted to create in git I've been able to do with pipes in either shell scripts or python scripts using data export options generally used by the porcelain.
People have built good APIs on top of this, and in a few cases built direct object access interfaces, but I stopped thinking library access was important when I started actually writing tools.
I went from SVN to Git a while ago and it was a great improvement. Then a friend pointed me at mercurial and I just felt it was a revolution. Finally my code felt portable.
With all that said Git has some great tools for large and complex projects with many contributors.
The one thing that sells HG to me though is it's insistence on non-destructiveness. If you made changes to files it always reminds me to merge changes properly without overwrites. I cant recall how many times that has saved me a n hours work :)
Plus because of the concepts behind it, cherrypicking is Simply Trivial.
Of course, the downside is that those concepts generate other issues.
Yeah, so? "checkout <branch>" changes the working dir's files to match those of <branch>, and "checkout <file>" reverts <file>'s contents to its original. It's documented and does what it says.
2. You can’t merge upstream changes into your local, uncommitted modifications
Why not just commit your "hacky" modifications onto a local branch? The great thing about Git is that your commits/checkins don't have to be perfect, since no-one else can see them until pushed/pulled/merged anyways.
3. Git’s merge conflict resolution workflow is unintuitive
If you've used Git at all, you should be comfortable with the fact that you need to "git add" files before you "git commit" them. Resolving changes is no different. "git add" each conflicting file and then "git commit".
4. Interface for working with the index almost universally confusing
Maybe you have a point there.
I'm actually using Git GUI a lot more than the command-line now. The only time I've used the command-line recently is for pruning, which the GUI has no way to do (it can only compress). Git GUI is pretty friendly.
Yes, it's documented, but git seems to really try and make it difficult to lose any work.. but has a command that makes it simple to irreversibly lose your upstaged changes, without so much as a warning?
At least "git revert" sounds like it might undo changes.. In what way does "checkout" imply both "checking out" branches, and reseting a file to it's previously commited state?
*NIX commands do things without asking. Your computer serves you, not the other way around. If I wanted my computer to double check everything that I do, I'd run Windows.
"(y/n/a)" just leads to awkward shell scripts.
What you think of as intuitive is not universal. Your reasonable defaults are unreasonable to me. This seems to be a problem of audience. Git can be challenging to learn, but it's not aimed at learners. It's aimed at power users.
As others have pointed out, there are other front-ends to git. You don't have to use the default porcelain.
I dont understand why people should have uncommitted changes for days
Branching is cheap and local, commits are local, no need to expose private work until it is ready to be merged with master, team accessible branches.
If you're just hacking around, shouldn't the procedure be to have a branch specifically for that and keep master clean for urgent work?
Confused.
My working copy is littered with uncommited files nearly constantly, simply because I work and play on many things in parallel and committing every time I save a file would be a noticable overhead, just as merging between topic branches all day.
I'm not complaining, we get along, my working copy and I, but I agree in some situations git could be more helpful than "xxx needs update" - and that's really just one symptom of many.
I've been playing with mercurial a bit recently (yes, heresy) and can see why the git guys despise it, it's just not as pure. The main thing that stuck with me though, is that even though it's roughly comparable to git in terms of features, it is mindboggingly much more intuitive to use.
Git could take a lesson from that and would not even have to give up much of the purity. Yes, "everything" should still be possible - but they damn sure could make the beaten paths a bit less spikey.
I don't either. I pretty much always work on master. If I find myself down a deep path that should've been a topic branch, I make one then and reset my master back to origin.
> I've been playing with mercurial a bit recently (yes, heresy) and can see why the git guys despise it, it's just not as pure. The main thing that stuck with me though, is that even though it's roughly comparable to git in terms of features
I used hg heavily for a long time before ever touching git. I did a bit of work on an older project that used hg recently and found myself unable to figure out how to do something I often easily do in git.
I don't remember the exact details, but I ended up committing and tagging after doing a "trivial" change that ended up being wrong (I noticed before pushing). Rather than pollute my public history with garbage, I just wanted to undo the mess and go back and fix the brokenness and retag.
When I used hg heavily, I was very well-versed with the ins and outs of mercurial queues for doing simple fixups like this. Not having used it recently, I had to go to the manual because I couldn't remember which combination of q-commands would do a simple effective rebase.
I was especially sensitive here because the last time I was a heavy hg user, I did lose changes to queues because hg doesn't have anything like the wonderful safety net of reflog to allow me to just put my tree back in a prior stat and let me try a complicated merge/rebase again.
Well, yes, I also kludge my way through it like that. It just always leaves that old sour taste of "it should be easier than that".
You know how it goes, the next pull comes along and you better remember to merge it to all your branches...
I did lose changes to queues because hg doesn't have anything like the wonderful safety net of reflog
This is an absolutely wonderful example, thanks for coming up with that. Yes, I know the reflog exists and you can do magic things with it. It involves commands like 'git reflog show --date=relative HEAD' and refs like 'HEAD@{1}' (I admit I had to look them up).
So, do you know that stuff from the top of your head? Or how much man-page wrestling did you have to do before the safety net actually caught you?
My wet dream of a git porcelaine would be different. It would include commands like "undo" and "redo" and they would do exactly what one expects and warn explicitly when their little world is not consistent anymore (e.g. an undo can't be performed because an affected file was modified). So to be clear: Undo would undo absolutely any operation (within git's ability) and same for redo.
Ofcourse there are many horrible corner cases to be worked out for such an truly intuitive command-set to survive reality - but imho a porcelaine grown in such a spirit would eventually do better justice to the elegance of git than the "raw API" interface we have now. Oh and while I'm in dreamland, my dream-porcelaine would ofcourse also max out at 10-12 commands.
git branch branch-i-should-have-made
git reset --hard origin/master
How would you make it easier? Why is that kludgy?> So, do you know that stuff from the top of your head? Or how much man-page wrestling did you have to do before the safety net actually caught you?
Uh, no. I type "git reflog" and look for the thing I want. Then I copy the hash and do whatever I was going to do with it -- the same thing I do in hg went I look at its logs and want to reference something older.
You are contriving a complex use case of a rarely used safety-net command, when in reality, it's just not that hard to use. I was listening to an SE at work introducing another guy to it earlier today when the guy needed to recover from a mistake he made.
That is not the kludgy part. The kludgy part begins when you have created a few branches that way and start bringing in changes from upstream, or when the branches begin to conflict with each other. Suddenly there is housekeeping involved, merging stuff back and forward.
I just don't understand why git lets me comfortably stage individual hunks (a feature that I love and use extensively) but refuses to deal with uncommitted changes as intelligently as with everything else.
I don't want to commit the three spurious lines I just typed into the editor before going to the bathroom. Yet I'd like to do a quick pull from my co-worker when I come back, before continuing on those lines and without commiting my 3 lines first. This is rather contrived again, but it shows the underlying problem of git not staying out of the way as it should but instead forcing me to do something completely unrelated to my actual goal - a sure sign of a bad UI.
You are contriving a complex use case of a rarely used safety-net command
Well, I agree it was rather contrived.
Yet I still wonder how many git users, when asked, could quickly type out the command to undo their last rebase and how many know, without looking, how to weasel out of the various follow-up errors that may appear ("Interactive rebase already started" anyone?).
Speaking of the examples in this article:
hg update
allows you to fast-forward without making a commit. The equivalent of git checkout
in Mercurial is (roughly) hg update -c
which will refuse to update if you have changes. (You can override that with "hg update -C".)In the case of merge output, when you've fixed a file, you simply run
hg resolve filename
You can at any point see what's not merged by running hg resolve --list
Notably, if you have a three-way merge tool set up properly, you'll never do this, because Mercurial will walk you through doing the merge properly as part of the...well, merge.No it's not. Speaking from experience here.
Edit: Okay, here's some real data: http://git.or.cz/gitwiki/GitBenchmarks http://whygitisbetterthanx.com/#git-is-fast
It shows that git wins by most metrics, with a few exceptions. Mercurial's push/pull format is particularly good.
Is the SPEED of any revision control system ever an issue? To anyone? Surely it's 'fast enough that it doesn't matter' vs 'fast enough that it doesn't matter'
Yes, darcs used to have (and probably still has, though to a lesser level) an issue with merge times blowing up exponentially. Anyway stevejohnson took issue with the statement that Mercurial performed roughly on part with Git, you might want to take the issue up to him not davidw.
For example, running status to see the state of the working directory. With svn it takes a couple of seconds on small repositories, and it can easily climb above ten seconds on larger repos. With git it very rarely rises above a second, even with a cold disk cache. The difference in speed is large enough that I find myself running "git status" far, far more often than I ever ran "svn status".
A more dramatic example is 'git bisect' which, when given a command, it will do a binary search on the repository to find the commit which caused that command to begin to fail (return an exit code of something other than 0).
Speed is a feature.
Classic hg branching came into different forms: 1) a tree clone and 2) the hg "branch" commands.
1 can be very slow on a large tree and can often require you reconfigure your tools to go look in a new place.
2 is permanent. Your code now forever references the name of a branch so you have to stop and think about whether it's really the right thing to do.
In either case, anything that requires me to stop and consider the cost of an operation makes the operation less valuable. Where that cost kicks in varies for people. Is it minutes? Seconds?
As a former perforce user, there were definitely plenty of things I wouldn't do there because they weren't instant. In git, I tend to make decisions based solely on my desired outcome.
Edit in response to above edit: those stats are for Mercurial 0.4 through 0.9.5. The current version is Mercurial 1.3.1. Seeing as there have been piles of speedups, including several that fell out from Python modules getting rewritten in C, I find that page highly dubious.
I'm not overly impressed with git in terms of it doing the right thing by default. The best options are often sort of hidden away under some option that you have to remember. svn is much simpler and nicer from this point of view in that I don't often have to use flags with it to do the basics.
Perhaps the title could be rephrased as something like: "if git hopes to grow outside the world of early adopters, it probably needs a better interface", although that's not nearly as snappy...
While we're at it... can anyone:
* Comment on mercurial and bzr from this point of view?
* Comment on TortoiseGit?
Most VCSs have a destructive command. If one doesn't expect the destruction from the command, they will no doubt be perturbed.
* You can’t merge upstream changes into your local, uncommitted modifications
Commit.
* Git’s merge conflict resolution workflow is unintuitive
Subjective but I'm curious which VCS has an intuitive merge conflict resolution workflow. I assume this means that without previous experience with the VCS a developer can (immediately?) intuit the workflow.
* Interface for working with the index almost universally confusing
Three switches for a single command. If you want to operate in a business as usual DWIM manner you can just remember --hard.
All that said, I do believe git reuses some commands in a semantically elastic way, sometimes to the confusion of new users.
And the name tips you off that you're about to do something destructive. Like svn's "revert". On the other hand, 'checkout' sounds fairly innocent to me.
> intuitive merge conflict resolution workflow.
IIRC, recent versions of svn walk you through it, asking which one you want to keep or letting you edit the file(s).
I actually use git (the branches are really nice for some things), but I'm considering VC systems for office use, and I'm not quite so sure I'd stick my neck out and recommend git.
FWIW Mercurial now has git-style branches (called bookmarks, hg's branches are different beasts)
It's all a matter of perspective.
I tend to commit lots of incremental progress. I talked to a couple of bzr users who do the same. They insisted that it was important to their project to include these changes that definitely don't compile (e.g. "committing because I'm getting up to go to the bathroom"), etc... in the long-term history of the project because it can help them remember what they were thinking when they were writing the code.
Personally, I do the similar, but I rebase -i before I push and squash and reorder most of that down into small, working chunks that most accurately reflect the real changes to the project.
That is, it's a higher priority to me to say what happened than how it happened. I'm thoroughly uninterested in what the state of the codebase was when you came up with the idea to work on something, how many times you pulled upstream code down, how many times you went to the bathroom, etc... I just want to know when your change went in that affected the state of the project, and exactly what it did.
Theoretically, a recursive merge message would tell me this. In practice, it does not, and the merge diffs don't necessarily show the change that affected the tree, but the delta that had to be applied to resolve conflicts on the merge.
I used to be a gnu arch user, and as such, I used to find it of utmost important to record every minor file save in my permanent history. I've learned since then that it doesn't help me actually understand the project.
Also, the use of sequence numbers (hg has the same problem) causes a lot of confusion. Humans seem to like the idea that there's an easy to understand sequence, but that's false on any project with more than one developer.
Specifically I had this problem with "buildbot try" -- the default implementation does a working-tree diff against the same revno upstream (e.g. if "bzr version-info" says 571, then it uses "bzr diff" to create a patch and sends it upstream against 571). However, if I've committed locally (because...that's just the right thing to do), then it will find either than upstream doesn't have a 572, or it's a different 572. Enter "bzr revision-info -rsubmit:" which introduces network (took 7.49 seconds for me just now) and "bzr diff -rrevid:[long-string-i-can't-double-click].."
I can't tell you all of the exact scenarios that were causing me confusion, but they came down to things like the above.