Git commands are not consistent. This is a result of it growing organically, and having a maintainer who strongly values backwards compatibility. But you know, having used git for years that doesn't bother me. Let me explain.
Git is conceptually straightforward[1]. It is difficult if not impossible to discern those concepts from its baroque CLI. So learning git from its built-in help and/or man pages is suboptimal if not a complete waste of time. Either read the Pro Git book[2] or pop the hood[3]. Then the inconsistent CLI is no longer a big deal. If it were, someone would have come up with a new porcelain (high-level CLI) that would have taken hold by now. And no one really has. There is Easy Git[4] but I don't know anyone who uses it.
And really, I see git as not much different than Unix in this regard. Unix commands vary widely in their usage. Does knowing awk help with sed? cpio and tar? wget vs curl? Eventually you understand concepts such as pipes, regular expressions, file descriptors, etc and you become familiar with the commands where you are able to apply these concepts, at which point does strict interface consistency between them matter? I dunno, but I started with both git and hg around the same time and ended up happier with git. Mercurial had the "better" CLI, but I found git was better at doing what I wanted. Before that I used clearcase, which has a very consistent CLI and yet I was constantly cursing at it.
Or maybe I just have stockholm syndrome and I'd feel differently if I'd ever used plan 9. :)
Master Git and a novice API developer stood together in a crowded office.
"I am trying to merge two massive XML files," explained the API developer, "and I am getting many tedious conflicts. You must have a strategy for making the lines match up in a human-readable manner. What is my best option?"
"Patience," said Master Git, turning away from the monitor.
"But I have been manually resolving for hours!" protested the API developer. "My mergetool ought to be able to imply context and match lines properly!"
Master Git sighed and left the room without a word. Hours later, he returned to find the office empty and silent. Only the API developer remained, his head bowed in defeat before the monitor.
The 'Only the Gods' one seems weird to me; maybe I just don't understand Git well enough, but it seems like that one's just a case of failing to understand what a branch is.
A particular commit isn't made 'on' a branch, so to speak; it's just a SHA hash that has a particular SHA as a parent. And then a branch is a pointer to a particular SHA. So there's no way to know which branch a commit originated 'on', merely which branches happen to contain a commit at present (because their current commit has the desired commit in its history).
So, if you merge two branches together, of course you can not tell which branch a given commit originated on just by looking at history. That information was never in the history in the first place.
I could see it being useful to track this in the merge somehow, though.
Most developers think of branches as something they commit to. Therefore they are surprised when later they can't tell which branch they committed to.
This could trivially be fixed with porcelain that puts the name of the branch the commit was made to at the end of the commit message.
The issue of branches not really being branches is not unique to git; most VCs I've ever used have had significant differences between what you would think of as a branch and actual branches (SVN doesn't even have branches, except as a convention).
Git is predicated on the notion that it's actually quite simple and elegant how Git works, and programmers are clever enough to understand how their software works, so not much porcelain is necessary. There's a refreshing honesty to that.
That's not my understanding of it's history; it was originally planned to have, potentially, multiple porcelains, but that idea died of a couple years back.
I believe that is the root problem of git. Originally, the assumption was: porcelains will provide a nice UI, so core git can leak implementation details. However, it turns out the implementation details are so simple and intuitive for many developers that core git is now the dominant UI.
For example, the multiple use cases of 'checkout' or 'reset' are quite intuitive, if you have the implementation in mind.
That, and constantly creating your own test repositories to test new commands/concepts. With a distributed VCS it's easy and fast. Just run "git init test" somewhere and start committing!
I also recommend you to use "gitk" when you're learning to constantly look at your history tree. That should clarify the concept "branches are pointers" after a few tests.
Although in implementation, a commit is not done on a branch, for the user, that is precisely what happens. You checkout a branch and you do a commit while on that branch. Thus, to the novice, it might be perfectly normal to expect Git to tell him what branches the commit was made on, even though that makes no sense from Git's point of view. I've never had that use case (knowing what branch a commit was made on), but if many people need it, surely explaining to them how Git works doesn't make Git any better for not being able to provide what the user needs.
Branches are just mutable pointers to commits. You're imposing a mental model onto Git that simply isn't present.
Git is revolutionary, in part, because instead of asking "what's the mental model we want to emulate and then never mind if that matches the implementation", they gave developers credit that they can understand a handful of simple concepts and made the actual design and data model as simple as possible. Programmers know what pointers, SHA-1 hashes, and DAGs are, so just use them.
You're making the same mistake as GP. I love Git, and I know that asking "what branch was this commit on" makes no sense. My point is that some users have different requirements as to what a DVCS should provide. If Git can't fulfill some of these requirements, then just say "Git can't do this" rather than beating around the bush.
I'm saying that "for the user" is a distinction not necessary for a tool designed for the type of people who actually know how software works. For the user, Git does the same thing that it does for the programmers who wrote Git. There's only one side of it. It's not two-faced like most software. The only possible explanation is to explain how Git works.
No, a possible explanation would be 'that is unnecessary, because: ...'. Except of course tagging commits with branches is not unnecessary, because that's the only way a user might make sense of ancestors of a merge commit...
Unless you like manually tracking parents of a commit, and trying to guess which branch is which based on commit messages. Fun!
I've seen many workarounds to this clear deficiency, the simplest of which is simply putting the branch name at the start of the commit title. Of course that usually cuts the space available for the actual commit title down from ~60 characters to 20-40, depending on how verbosely you call your branches.
> Unless you like manually tracking parents of a commit
Git tracks the parents of a commit. There are ways to visualize that, even with command-line git. When you do this, it's usually clear which branch is master and which branch was the feature branch, and since the merge commit has a default commit message like "merge <<branch-name>> to master" it's even easier to figure out.
It's also entirely possible to use a Git workflow where you rebase your branches onto master before merging then in with a fast-forward, which means there are no merge commits. This workflow usually involves squashing. Conversely, you can use a workflow where you never fast-forward merge to master, in which case master is the branch that consists of a series of commits called "merge <<branch-name>> to master".
Open `git glog` and look at the tree is exactly what I ment by manually tracking parents of a commit. I shouldn't have to do that.
That means that to see commits that were done in a particular feature branch I need to do some magic that walks up the commit tree from a given merge commit, guesses which one of the parents is the master and which one is the feature branch, remembers the commit ids, keeps track of merge commits it hits and makes sure it is still tracking the right branch (merges of master into a feature branch are common, merges of feature branches into other feature branches are occasional), etc.
All because gits branches aren't really branches at all.
As to fast forwarding a feature branch onto master, I can't believe anyone would actually do that. You might as well be developing on the master branch from the beginning there, what's the point of having branches if they disappear from history?
I have to admit some confusion. You're not looking through your commit history when you're trying to figure out which branch a historical commit was on?
The reason for the rebase/fastforward workflow is to basically synchronize changes. You might have five developers working on seven different feature branches at a time. But they're only going to be merged back to mainline in the order they're completed, which you don't know ahead of time. Once they're merged back, you don't necessarily need to record the history of the feature branch separately. Often you're going to squash changes anyway.
It's also a better idea to rebase from master and merge to master, but even if you merge both ways, the commit message still reports the source and destination branches. If you're disciplined about using feature branches, master is the branch with commit after commit saying "merged to master".
>I have to admit some confusion. You're not looking through your commit history when you're trying to figure out which branch a historical commit was on?
I shouldn't have manually filter the commit history to get the information that interests me.
>The reason for the rebase/fastforward workflow is to basically synchronize changes. You might have five developers working on seven different feature branches at a time. But they're only going to be merged back to mainline in the order they're completed, which you don't know ahead of time. Once they're merged back, you don't necessarily need to record the history of the feature branch separately. Often you're going to squash changes anyway.
But if you fast forward your changes on top of master and push that out as the new master, you lose all the benefits of branching. You could have as well made your changes on master, do pull --rebase once you've completed and then push out. That's how the history looks like.
>It's also a better idea to rebase from master and merge to master, but even if you merge both ways, the commit message still reports the source and destination branches.
Sure, if the rebase is clean, but conflicts are common.
>If you're disciplined about using feature branches, master is the branch with commit after commit saying "merged to master".
Unless you use the github UI, in which case it will say 'Merge pull request ...'. Or you had to hotfix something on master - it happens.
> But if you fast forward your changes on top of master and push that out as the new master, you lose all the benefits of branching. You could have as well made your changes on master, do pull --rebase once you've completed and then push out. That's how the history looks like.
Which benefits?
To me, the main benefits of branching happen as I'm developing a feature. I can work on something and quickly context-switch either back to master or to a different feature branch. For instance, if I'm working on a feature but a high-priority bug fix needs to be done first, I can switch back to master, check out a bug fix branch, and keep my feature work separate from it. When the bug fix is done, I merge it in and rebase my feature branch from it. The only thing I really care about historically is that features land in master all-at-once, which squashing, rebasing, and fast-forwarding does for you.
> Sure, if the rebase is clean, but conflicts are common.
You have to resolve conflicts either way though.
> Unless you use the github UI, in which case it will say 'Merge pull request ...'.
Still a recognizable pattern.
> Or you had to hotfix something on master - it happens.
That's called "not being disciplined about using feature branches". I suspect you run into this problem because you're suffering under a self-imposed constraint that branch names have to be globally unique and meaningful throughout your version history.
This isn't a problem unless you want to enforce a workflow where you always merge to master--the rebase/squash/ff workflow wouldn't actually pose a problem here, since all ancestor commits to master were deliberately put on master. My point is, if you follow an inconsistent workflow, no wonder your history is difficult to look through.
Someone who can't understand the Git data model is, by programmer standards, a fool. If you actually understand the data model and have reasons for not liking Git, I'm curious what they are. Unfortunately, most criticisms come from people who can't be arsed to grok how Git works and just ragequit.
>If you actually understand the data model and have reasons for not liking Git, I'm curious what they are.
Well, the fact that I can't easily find what commits were done on a branch, for one. And yes, I understand why it's not possible, they way git does branches.
Or that it doesn't track renames/moves, other than on similarity basis that often fails to detect rename when viewing a file history, for example.
The git answer to both of these is usually 'well your viewer could be doing that', except they don't, because it's hard when you don't track that information in commits.
Lets back up: why are you trying to figure out what branch a historical commit was on? What problem does that solve for you? I've never found that information terribly important or interesting.
Say I bisect some issue to a particular commit. Knowing which branch it was made on tells me what issue it was trying to solve, which gives me context for the entire branch.
It also means I can easily request all commits made in the master branch, to see what features were merged in recently.
> Say I bisect some issue to a particular commit. Knowing which branch it was made on tells me what issue it was trying to solve, which gives me context for the entire branch.
How is the branch name going to give you sufficient information on that anyway? You're going to have to look at the commit message and probably the surrounding history anyway if you want context for the entire branch.
> It also means I can easily request all commits made in the master branch, to see what features were merged in recently.
In this use-case, you're probably viewing some form of "git log" anyway, so it's not hard to turn on the option that shows the tree view, which should make it readily obvious.
>How is the branch name going to give you sufficient information on that anyway?
Well, in my workflow it will give me the issue number, since branches in my repo all look like '530-issue-description'. I suppose that's because we used to work with mercurial, where that is actually meaningful and helpful.
>In this use-case, you're probably viewing some form of "git log" anyway, so it's not hard to turn on the option that shows the tree view, which should make it readily obvious
The tree view will still interleave master commits with all the other commits, and if there's a lot of parallel branches and significant distance between master commits it's easy to get lost.
Tracking your single 'pipe' character down a couple pages and not losing which one you're watching... Not what I call good UI.
Going back to my comment, I think for a lot o peole it's not that they can't understand the Git data model, it's that they:
1) don't magically know that they're supposed to understand the data model in order to use it
2) Come for help when they screwed up because they don't understand it and are basically told "You screwed up because you're an idiot"; since they are already in a bad mood, they then ragequit.
One thing that does happen though is if you have a non-ff merge, the default commit message has the name of the branch being merged from (though not the branch being merged to).
From "git --help config": "To avoid confusion and troubles with script usage, aliases that hide existing git commands are ignored."
> “Use git checkout.”
This is just a jab at how "checkout" does too many different things.
> “I have a historical record of a merge commit with two parents. How can I find out which branch each parent was originally made on?”
Git doesn't let you do this.
> “Surely some of these could be made more consistent, so as to be easier to remember in the heat of coding?”
Git commands can be inconsistent.
> “git -h branch“.
This command tells you nothing about "branch" (throws error), whereas "git --help branch" launches the browser at git-branch's manpage. This goes against what people usually expect (that "-h" is just shorthand for "--help".
I'm not sure what Silence is about. My git-config-fu fails me.
One Thing Well demonstrates how one command (git checkout), while it does do 'one thing well' (changing your working directory to match something in the index), can handle a wide variety of seemingly unrelated use cases.
Only the Gods demonstrates how history can mean different things: commit parentage, which is usually immutable, and branch history, which is ephemeral.
The Hobgoblin probably refers to the Emerson quote "A foolish consistency is the hobgoblin of little minds."
The Long and the Short of It is about how git accepts `git --help command`, `git command --help`, and `git command -h` but `git -h command` returns an argument error.
A theme here is that git does not try to anticipate your workflow. Its commands and options are named based on what they do, rather than how you should probably use them. This makes git a very powerful tool, but with an interface that is often counter-intuitive.
Apparently, git does not allow you to alias default git commands (like "pull", "push", "commit", etc) to something else. In the given situation, a user is trying to alias the default "pull" command to "pull --ff-only" so that pull will not automatically merge changes and will only fast-forward.
However, since git does not allow you to alias default commands, when the Git Master runs "git pull" it ignores the alias entirely and does a normal pull, and in so doing merges changes. The student is confused, but eventually figures it out.
A lot of these are just plain wrong though or have a good reason and it makes me sad because I know Steve Losh uses a lot of Mercurial which I think has a much worse UI.
1. Silence: you can't alias built-in commands — for good reason. You don't change the defaults of commands, that's just going to cause problems with scripts. Why is it ignored silently? For starters because new git versions can add a command that would conflict with your config that came from an older version. You can trivially see that an alias is ignored by just adding ``--help`` to the command and it won't show you the alias.
2. One Thing Well: I fail to see the problem with that? All of these do the same thing. They check out a revision. With the exception of branch creation which is actually not a feature of `git checkout` but `git branch`. There is just a convenience operation on `git checkout` that also changes branches. The better question is why branches in mercurial are called bookmarks :P
3. Only the Gods: thankfully you don't know which branch something came from. Mercurial fucked me over more than once where I accidentally pulled a patch that came from a named branch that was conflicting with one I already had (you would not believe how many people name their branches "bugfix"). Mercurial's named branches are among the stupidest idea in software maintenance.
4. The Hobgoblin: these are all different commands. "How can I view a list of all tags": that's not what all tags is, that is "what's the list of tags I know locally". In mercurial you can't even figure that out properly because depending on the branch you're on you get different defined tags since tags are stored in the tree. Git's tag system is much superior to that.
"How can I view a list of all branches": that's a question you will never ask because it's the wrong question. A branch could show up multiple times. The correct question to ask is "How can I view a list of all local branches?" `git branch`, the second question is "how can I view a list of all remote branches?" And for that the answer is `git branch -r` or `git branch --remote` which makes a lot of sense in my mind.
"And how can I view the current branch": Same way you show all branches, just `git branch`. Shows you your current branch as well as all other branches and neatly colorizes it. `git rev-parse` is a command you really never have to use unless you script something.
Yeah, some could probably more consistent. The odd one out are `git submodule` and `git remote` which are sub commands instead of using parameters to remove/modify them. That being said, you rarely need to use those.
In direct comparison with hg, git's UI and functionality is a present from god. I'm pretty sure hg only has a better UI reputation because git's UI used to be pretty bad. Now however? Pretty sure it wins over hg hands down.
1. If a new version of git adds a command that shadows one of your aliases, you'd really prefer "git foo" to silently run the new command? That's better than telling you "hey this isn't going to do what you think any more"? Do you have `try: foo except: pass` in your Python code too? Come on. There's no reason git shouldn't at least warn you on stderr here.
2. `git checkout -- somefile.py` does not have anything to do with checking out a revision. It changes a file's contents, but doesn't change the parent of your working tree at all. Unlike `git checkout -b` which is inconsistent for convenience like you mentioned, the "make file x look like it was in rev y" function here is unique to git checkout as far as I can tell. Then again this is git, so there's probably at least four other ways to do this with cryptic plumbing commands.
3. I like Mercurial's named branches because I don't need to create a branch for every single commit. But even if you don't like them, the "record which branch X was made on" and "use that recorded information to group branches and such in the UI" are two different things. The second requires the first, but git could record which branch was checked out when each commit was made as some metadata in the commit, instead of dropping it on the floor. It would be a godsend when trying to figure out what actually happened during a merge.
4. `git branch` doesn't show you the current branch. It shows you all the local branches, with colors and padding spaces and asterisks and crap. What if you want the current branch in your prompt? Or just want to grab it in a script? Your choices are "pipe to grep and sed" or "use this ugly-ass rev-parse thing" instead of "git branch --current".
Git's UI is still abysmal. Mercurial preserves more data (branches), gives you the same power as Git and has a UI where "--help x", "-h x", "help x", "x -h", and "x --help" all do the same thing (pop quiz: how many different things does git do for those? hint: it's more than two).
> 1. If a new version of git adds a command that shadows one of your aliases, you'd really prefer "git foo" to silently run the new command?
Yes. Otherwise command line scripts will start doing horrible things. It was never intended that you can alias builtins, the only reason you might think you can is because it is allowed in mercurial which I think is a horrible idea.
> `git checkout -- somefile.py` does not have anything to do with checking out a revision.
Of course not. `git checkout` checks out things. You can check out a lot of things. Just because it works differently in hg does not mean it's wrong in git. git checkout does what it says on the tin: it checks out a branch or paths to the working tree.
> I like Mercurial's named branches because I don't need to create a branch for every single commit.
I think the concept of named branches in mercurial are hugely flawed and were the final nail in the coffin of why I migrated over to git. And I never looked back.
> What if you want the current branch in your prompt?
Then you use the underlying low-level commands or even better, you cat `.git/HEAD` which will be even faster. Not sure why for your prompt to look nice you need to have non-cryptic commands. Exactly for things like prompts there is the plumbing.
> gives you the same power as Git and has a UI where "--help x", "-h x", "help x", "x -h", and "x --help" all do the same thing
How is this different from mercurial? If anything mercurial has even more ways to show help: `man hg` which shows the manpage for all the builtin commands. `hg help` which shows a short version or `hg -v help` which shows a long version (which is not the man page). git only has two help pages: man pages for long and short text for short.
> Yes. Otherwise command line scripts will start doing horrible things. It was never intended that you can alias builtins, the only reason you might think you can is because it is allowed in mercurial which I think is a horrible idea.
You should understand what you are responding to before typing your answer.
Say today I create an alias “boom = <whatever>” and next week git introduces a boom command.
Now my alias is ignored, my scripts are broken and I might destroy my working copy or kill my cat by using it without knowing it now means something else.
Git gives some very stupid hints “branhc is not a git command, did you mean branch?” it should also warn me when I defined an invalid alias.
Maybe that alias was valid two versions ago or I just don’t know that command exists, since there are eleventy thousand of them.
> Now my alias is ignored, my scripts are broken and I might destroy my working copy or kill my cat by using it without knowing it now means something else.
Your scripts should not rely on aliases you have in your config. Scripts are intended to be used by different people.
>> Git gives some very stupid hints “branhc is not a git command, did you mean branch?” it should also warn me when I defined an invalid alias.
This is the thing that really infuriates me - having a message from my tool that tells me there's an error is one thing, but offering a (frequently incorrect) correction forces me stop and try to figure out why this suggestion was offered, since it frequently has NOTHING to do with what I actually (mis)typed.
Lets put it this way - if this autocorrection feature is so great, then it should prompt you Y/N after giving the error message. Would it then be a better feature? Or would it make more people want to stab it in the eyeballs? Or if this suggestion feature is so great, then it should just DWIM in the first place.
Someone needs to take gits levenshtein algorithm away.
> Or if this suggestion feature is so great, then it should just DWIM in the first place.
It can do this if you tell it do. The following tells git to wait 10 tenths of a second before executing the corrected command, giving you time to cancel if it is incorrect.
If your solution to "get the current branch" is "cat out this magic data file that happens to exist in the data directory" then we have completely incompatible definitions of "user interface" and are never going to agree on this.
The question was not how to just get the current branch, thought, right? As pointed out already, `git branch` does that.
The question was how to do it programmatically, and there are multiple ways to do it. You might still call that user interface, but it's that's a stretch; fancy shell magic like branch-in-prompt is typically write-once.
>I think the concept of named branches in mercurial are hugely flawed and were the final nail in the coffin of why I migrated over to git. And I never looked back.
How does having more information available ever hurt you?
Now, fair enough, the fact that mercurial expects branches to be unique might be annoying if you have the tendency to call all branches 'bugfix', but you seem to imply that simply having the branch name in which the commit was originally created as metadata on it would be somehow bad.
>git checkout does what it says on the tin: it checks out a branch or paths to the working tree.
'git checkout is good because checkout means the thing that git checkout does'
riiiiight
`And how should I create a branch?. Use git checkout.` No. You create a branch with git branch. If you want to create a branch and immediately switch to it, use git branch (to create it) + git checkout (to switch to it). git checkout -b is just a shorthand for the two, it's there just for convenience. Unix has a lot of these occurrences. rm -rf as a shorthand for rm+rmdir, the open() syscall with O_CREAT shorthand for creat()+open() etc.
By the way, rm -rf does a lot more than rm+rmdir. You will probably learn this the hard way soon enough, unless you already know this and are just oversimplifying to show a point.
It's interesting to watch people here defend git's interface as if it was all a very deliberate, intricate design that is well justified and thought out, when it very clearly grew organically. Not that there was "no" thought, but it's not like there is a huge poster on a wall in Junio's office (I checked) or the surrounding whiteboards, that have git UI principles that are being enforced on new commands/patches.
Basically, people are wrapping stuff up in justifications that didn't even exist when the commands came to be.
That doesn't make it bad, of course, but uh, retroactively justifying things is always a bit odd to watch.
I actually could care less about either git or mercurial; having helped write subversion, the majority of us were/are glad that we were replaced by not just one, but two things that people like more. The fact that it "destroyed" our software is irrelevant, Subversion made what we set out to make, something better than CVS, succeeded, and then the world got even better through git and hg.
The fact that git and hg proponents seem to simply enjoy fighting about which VCS is better/worse, rather than concentrating on making actual progress, is fascinating to me.
> It's interesting to watch people here defend git's interface as if it was all a very deliberate, intricate design that is well justified and thought out, when it very clearly grew organically.
I don't see people doing too often, to be honest.
I do see git proponents saying that commands like git-checkout are misunderstood, but that isn't contradictory to "organic growth". From the beginning Linus was claiming that git was more of a content-addressable filesystem than an actual designed VCS.
If anything defenses like "git checkout -b is just shorthand for git branch && git checkout" are nothing but an acknowledgment of that organic growth. There's no reason to add a shorthand for a command sequence that no one uses.
Retroactively justifying things is what we always do. Here's an excerpt from Dan Ariely's "The (Honest) Truth About Dishonesty":
> The experience taught me that sometimes (perhaps often) we don't make choices based on our explicit preferences. Instead, we have a gut feeling about what we want, and we go through a process of mental gymnastics, applying all kinds of justifications to manipulate the criteria. That ways, we can get what we really want, but at the same time keep up the appearance -- to ourselves and to others -- that we are acting in accordance with our rational and well-reasoned preferences.
Simply replace all occurrence of "we" with "mercurial/git fanboys" based on your own preferences ;)
Thank you for Subversion :) Not only was it better than CVS but it was a damn site better than Visual SourceSafe too, which was often the alternative in Microsoft-land.
Your critique seems to be based off comparing git to mercurial and declaring that git is designed better. I don't think you're going to see a ton of disagreement with that in this community. You make good points too, with context a lot of those commands make more sense.
However well designed software shouldn't need the context. Well designed software is intuitive. We shouldn't need to know the intricacies in the git data model to able to understand the major commands and tools. As we begin to do more complex operations then more knowledge can be a prerequisite.
The fact that these commands all made sense with a detailed explanation is only a symptom of the problem, not an excuse.
Normally I would agree with you that software should be intuitive with a minimum of learning. However git is only used by expert users, people who use it all day every day. There's no doubt that git has a bit of a steep learning curve but the abstraction you're forced to learn is really powerful. Having come from SVN the intuitive abstraction I was used to now seems woefully inadequate.
I am an expert user. I've been using git almost every day for months. I've read about it's underlying data model and could write a tool that used it if I really needed to.
I _still_ have to use Google whenever I try to do anything with git that falls outside my normal workflow.
What do you mean that git is used by expert users? Expert in what, git? If that is true, then I agree with you that you need to be an expert in git to be able to use it efficiently and not second guess yourself constantly when you come across a problem.
I agree that git's internal design is very well thought out. Its interface is very inconsistent though.
Ah, nothing like pointing out someone else's flaws to feel better about your own. Git is powerful but hard to learn and is often not intuitive. Knowing Hg also has issues doesn't help me when I'm trying to use git.
I just find it unfair criticism because hg has just as many UI kinks or just lack of UI to begin with. `hg diff` or how it's randomly split into different commands with a `-p` flag is just one example.
And, in addition to that, I find it very hard to believe that anyone who used both can sincerely say Hg's CLI interface is 'just as bad' as git's. I've used both and switched to git eventually, because from a technical point of view it's simply the better tool. Git is faster, more flexible, easier to deploy. The things it is not: more intuitive, easier, more consistent.
I don't understand why many git apologists continue to try to paper over the atrocious CLI that comes with git by blaming the user for not knowing his tools well enough. The tool is supposed to serve the user, not the other way around. Personally I think everything about the git CLI sucks, plain and simple. The command structure and naming, the switches, the error messages, the documentation, all of it. Compared to git, the hg CLI is a thing of beauty. Yet I still prefer git over Hg. No need to sugarcoat it and pretend git is flawless though.
Either you or I have misunderstood the article. These koans aren't criticism. They are stories meant to enlighten readers to lessons regarding git.
If you see each of these stories as criticism's about git, then you are the novice. Once you receive enlightenment on each story you will realize why each story is not criticism--you will see the error of the reasoning of the novice in the story and you will understand why Master Git's actions make sense.
These really DO seem like criticisms. Particularly the hobgoblin. Given the context of the author[1] pointed out by the parent post, I think the thrust of these koans is obvious.
Personally It feels a bit dirty. I started out enjoying them, now they feel petty.
Yeah, about a third of the way through "Git Koans" I got the strong suspicion that the author was a Mercurial fan. It had potential and started out kind of amusing, but definitely felt like a bullied kid's fantasy near the end. Shame, too, as many of the author's other posts seem very well written and insightful. Eh, nobody's perfect.
Branches in mercurial are called Named Branches. And they have the benefit that commits made in the branch will always know where they were created.
They have Bookmarks which really are a better name than "git branches" anyway because you can't really understand branching in Git until you understand that a branch name is just a pointer--a bookmark if you will--to a specific HEAD commit.
> There is just a convenience operation on `git checkout` that also changes branches.
Isn't that violating 'doing one thing'? (Your other examples in this point are valid, and serve to show how powerful building concepts on top of a few primitives can be, if confusing at times)
Here's how I view the checkout command: It is used to check out stuff from your git database into your working tree. Stuff here means either the whole tree or some files in it. You can check out a whole tree (branch) by specifying its sha1, or you can check out a single file by specifying the tree and the filename. The fact that checkout can be used to create a branch is just a convenience because most of the time it's easier to give the tree sha1 a name to refer to for further git commands.
This is exactly what I tell students in my Git courses during the first hour.
Git checkout is a command to read something from your .git-folder to your working directory.
Especially for people coming from ClearCase, the concept of Git checkout needs to be clarified as fast as possible. (In CC checkout means you make an element (file) editable for you, and possibly reserve it to you too)
Yes. But that violation would also be there if `git branch` would checkout. And in that sense, what's more odd "git checkout -b new-branch" or "git branch --checkout new-branch"?
One Thing Well: The command of all these seemingly different action is the same, because all of these seemingly different actions are actually the same.
The Long and the Short of It: -h is not a valid argument. Passing -h therefore results in providing usage information.
Has anyone made an alternative interface for git that tidies this up without drastically changing how you use git? Or would this condemn someone's sanity to the graveyard?
I'm using hg even with github. The hg-git extension from the github guys works surprisingly well for me. The only downsides I've found that push/pull of very large repos tends to be a bit slow and the conversion to/from git will clobber file move histories in hg, since git doesn't track file moves.
> More people use git; I'm becoming increasingly convinced that few of them actually know git.
And yet I have noticed that people find merging and branching much easier than in mercurial. Few people understand the implications of named branches in hg.
> And yet I have noticed that people find merging and branching much easier than in mercurial.
That's the first time I've heard this opinion - do you know what kind of complaints people have?
In my experience, it's much easier to say 'When you start work on an issue, start a branch called 'issue-XXX', then once you're done we will merge it into the default branch'. I've never seen much confusion in response to that.
Not as weird as hijacking the term "branch" to mean a symlink to a ref that is not part of the history. Then again why make it easier by naming it to something more intuitive, like say, "bookmark".
This may seem like a joke, but having used Hg a little bit, I do think it has a superior interface. The error messages are clear, the commands are intuitive. The command addremove is genius.
But I still use git because I already grokked it before I learned hg.
I learned hg before git and the experience of switching to git was shockingly bad. I'm comfortable in both now and the big thing I'd miss if I went back to hg would be git stash (and I'm sure there's a hg plugin for it).
That's interesting. I learned hg first and moved to git after being frustrated with hg's limitations. Yeah, git could use an overhaul on some of it's command line interfaces (using the index on different git commands: --cached, --stage, --index), but it's so much better overall that those things become minor quibbles.
> I learned hg before git and the experience of switching to git was shockingly bad. I'm comfortable in both now and the big thing I'd miss if I went back to hg would be git stash (and I'm sure there's a hg plugin for it).
What about the index? Or the branching (bookmarks kinda work now I think), or the better remote management?
Let's say I modified an existing file (not staged) and then git add a new file. It is a work in progress, and the two changes are not supposed to be in the same commit.
Now, what can I do to see all changes I have made so far?
I have done this many times with subversion (svn diff) and mercurial (hg diff). Git's index just get in the way.
I have found branching in Git to be a pleasure to teach, even to large groups. There is nothing I have to hide about them, implementation or otherwise. "This is how it works and this is what you can do thanks to that"
Switching to git was very easy for me after hg screwed my repo. And git had much saner branching back then, not sure if anything changed in mercurial land.
Why? I find that in the middle of a big project I have lots of stupid files strewn about that I absolutely don't want checked in (TODO lists, patches I'm considering, copies of large log files I'm investigating, etc). I always add things explicitly and delete explicitly, otherwise I'd get random junk checked into my repo. How do you keep your repo clean enough to actually use automatic commands like that?
Isn't that checked in like .gitignore? I'd rather not put stupid local stuff into a file that the rest of the team shares. And if I don't commit it then I'm stuck with a local file that always has changes, which is also begging for trouble. It also implies that I name things in some consistent way or that I add a lot of files into the ignore file.
I guess other people just aren't as messy as I am?
You can keep multiple ignore files. Then .hgignore can be shared by the whole team (if you're working in C, you're going to ignore .o files, for instance, and so will everyone else). Look up the ui.ignore hgrc option to add more files (presumably one of which doesn't sync).
-a, --all
Tell the command to automatically stage files
that have been modified and deleted, but new
files you have not told Git about are not affected.
I don't use a complicated .gitconfig (just user.name and user.email), so this is all new to me.
Something that leaps out to me as horribly wrong is that aliasing a builtin doesn't cause the config file to be "invalid". Doing anything else is just doing spooky things that are explained only several hundred lines into the man page by a single sentence.
Maybe better would be to choke on config lines that alias a builtin, and to print error messages before or after the output of each git command while the offending line(s) are present.
Further,
git config alias.pull "pull --ff-only"
should definitely be an error. Instead, git gladly makes the (no-op) confusing edit to your config file.
'git' alone tells me to 'git help <command>' for help, which I've done dutifully ever since. That is, until I too was enlightened a few minutes ago with 'git <command> -h'.
git branch -h throws those short commandline help summaries.
Interestingly, -h does not mention -h itself or --help, so the only way to know that --help exists, is someone else telling you, you will never find on your own.
Actually, "-h" is not a valid option on "git", but it is on "git branch" (somewhat confusingly). Try "git branch -j" to see what happens when you pass a bad option. In particular, try that when your cwd isn't a git repository.
> Interestingly, -h does not mention -h itself or --help, so the only way to know that --help exists, is someone else telling you, you will never find on your own.
I've found the --help option on probably a dozen commands without ever being specifically told that --help was a valid option.
If you have trouble discovering it you might want to look inward, at least for that particular well-known option.
I believe lots of people do not understand that you should treat remote branches differently than local branches. More precisely, you treat remote/global history differently than local history.
Using rebase to clean you local history is ok. Maybe even recommended or mandatory depending on the project. Changing history, which is already in other people's repos, will lead confusion and should really be avoided.
Git is conceptually straightforward[1]. It is difficult if not impossible to discern those concepts from its baroque CLI. So learning git from its built-in help and/or man pages is suboptimal if not a complete waste of time. Either read the Pro Git book[2] or pop the hood[3]. Then the inconsistent CLI is no longer a big deal. If it were, someone would have come up with a new porcelain (high-level CLI) that would have taken hold by now. And no one really has. There is Easy Git[4] but I don't know anyone who uses it.
And really, I see git as not much different than Unix in this regard. Unix commands vary widely in their usage. Does knowing awk help with sed? cpio and tar? wget vs curl? Eventually you understand concepts such as pipes, regular expressions, file descriptors, etc and you become familiar with the commands where you are able to apply these concepts, at which point does strict interface consistency between them matter? I dunno, but I started with both git and hg around the same time and ended up happier with git. Mercurial had the "better" CLI, but I found git was better at doing what I wanted. Before that I used clearcase, which has a very consistent CLI and yet I was constantly cursing at it.
Or maybe I just have stockholm syndrome and I'd feel differently if I'd ever used plan 9. :)
1. http://tom.preston-werner.com/2009/05/19/the-git-parable.htm...
2. http://git-scm.com/book
3. http://newartisans.com/2008/04/git-from-the-bottom-up/
4. http://people.gnome.org/~newren/eg/