It's a shame how poor the command-line usability of the git client is. Commands are poorly named and often do the wrong thing. It's really hard to explain to new users why you need to run `git checkout HEAD *` instead of `git reset` as you'd expect, why `git branch [branchname]` just switches to a branch whereas `git checkout -b [branchname]` actually creates it, etc.
I really wish he'd collaborated with some more people in the early stages of writing git to come up with an interface that makes sense, because everyone is constantly paying the cost of those decisions, especially new git learners.
I don't even know what `git checkout HEAD * ` does lol. Does it just checkout the current branch to the HEAD of that branch?
I can never seem to guess what things do in Git, and I consider myself fairly comfortable with the core concepts of Git. Having written many types of append-only / immutable / content address data systems (they interest me), you'd think Git would be natural to me. While the core concepts are natural, Git's presentation of them is not.. at least, to me.
so, that says, copy all of the files out of the current branch at the current commit into the local dir. What this will do in practice is "discard current changes to tracked files". So If i had files foo, bar, baz, and I had made edits to two of them, and I just want to undo those changes, that's what checking out * from HEAD does. It doesn't however delete new files you have created. So it doesn't make the state exactly the same.
So why not just git checkout HEAD? Well, you already have HEAD checked out (you are on that branch), so there's nothing for git to do. You want to specify that you want to _also_ explicitly copy the tracked file objects out also. It's kind of like saying "give me a fresh copy of the tracked files".
The confusing thing is that in practice it is "reverting" the changes that were made to the tracked files. But `git revert` is the command you use to apply the inverse of a commit (undo a commit). One of the more confusing aspects of git is that many of the commands operate on "commits" as objects (the change set itself), and some other commands operate on files. But it's not obvious which is which.
That command would only discard changes to non-hidden files though, because * typically doesn't expand to hidden files. I think the command one really wants in these cases is
That throws away everything though, whereas `git checkout HEAD *` only throws away stuff in the current directory and below, or you can pass exact filepaths to be surgical about which changes exactly you're reverting. This is what I use it for most often -- reverting some, but all, edits.
It's been a long time since I used a GUI source control client. Maybe I should try one out again. Certainly it makes diffs nicer.
It's just that I've been using git CLI for so long, and know exactly which commands to use in any circumstance without having to look them up, that I don't benefit much from switching to something new, whereas someone who hasn't yet put in that time to really learn git would stand to benefit more.
I don't think so; I'm not much of a developer but I've used git productively for years and I've never had to run the commands CydeWeys listed.
The commands johnmaguire2013 listed are the ones usually recommended for beginners and I have found them easy to understand.
"git branch [name]" is for creating branches; it tells you if the branch already exists. Pretty easy to understand.
"git checkout [name]" is for checking out branches; it tells you if you're already on that branch.
You can run these sequentially and it works fine; there's no need for `git checkout -b [branchname]`.
I think there is sometimes some productivity porn involved in discussions of git, where people feel really strongly that everything should be doable in one line, and also be super intuitive. It's a bit like the difference between `mkdir foo && cd "$_"` on the command line, vs just doing mkdir and cd sequentially. IMO the latter is easier to understand, but some experienced folks seem to get upset that it requires typing the directory name twice.
Mm, I'm not sure I agree. The first correction shows that the command works as GP would have expected. The second command shows why checkout works too -- because it is checking out the branch (like the GP expected) in addition to creating a branch.
And I have already explained why `git reset --hard` makes more sense in my opinion.
I agree that Git can be hard to wrap your head around, and that the commands could be more intuitive. But Git is complex in large part because the underlying data structure can be tricky to reason about -- not because the UI on top of it is terrible.
There may be complexity under the hood (I don't know), but in my experience, even pretty advanced users employ a pretty straightforward mental model that's considerably simpler than the commands themselves are to use.
I disagree. I think the commands are intuitive and work perfectly for the system. It's very expressive, but it all makes sense once you've learned it.
git is a tool. Different tools take different amounts of time to master. People should probably spend some time formally learning git just as one would formally learn a programming language.
It's a good point: just because an entire product won out doesn't mean that every single one of its features was individually superior to its competitors. This is definitely not true for git.
That's roughly the same as saying that you won't eat the food prepared by a chef if they don't use your favorite brand of knife. You've diminished the value of your previous comment substantially with this one.
The git storage structure is not that difficult. People could implement their own compatible clients on top of it with a quite different UI. That there is nothing that overtook git as UI in popularity seems to be an indication that the interface is not as bad as many people claim.
People have added better UIs on top of git. The problem is they don't come installed by default out of the box, and unless everyone else you work with is using them too it becomes quite hard for you to communicate properly over git issues (especially in writing development workflow docs). hg has a better UI out of the box, and is notably easier for new users to pick up and become productive with.
You're underestimating how much inertia is created simply by being the out-of-the-box default, and how hard that inertia is to overcome even by better alternatives.
Not sure who "many people" are, but no one in this thread is claiming that git is unworkable, only that it is confusing. A collaboration tool like git is highly subject to network effects. The usability delta between git and a given alternative must be very, very high before people will leave git for the alternative. Ergo, git can be both awful and "good enough" to have a majority market share (although I don't think anyone is even saying git is awful in this thread).
I can still never remember the git equivalents for things like ‘hg log -r default:feature-branch’ or ‘hg diff -c <commit>’. People who haven’t used Mercurial really have no idea how pleasant a VCS interface can be.
The latter is 'git show <revision>', show being one of those fun commands that does a bazillion different things depending on what the argument is. My fun example of what's-the-git-command is 'hg cat -r <commit> <path>', whose git commit equivalent is another variant of 'git show'
ClearCase is wonderful if you fit the use case, which is a sizable team on a fast LAN.
It's great being able to change a ClearCase config file to choose a different branch of code for just a few files or directories, then instantly get that branch active for just those specific files.
Basically the usual ones that end up with copying the modified files to a temporary directory and doing a fresh clone followed by a manual merge and commit, because someone messed up their local repository while trying out some git command beyond clone/pull/push/checkout/commit, and now cannot push without messing everyone's else.
And there are alternatives that make better sense, too.
But the existence of something even worse doesn't excuse something that is merely bad. And git is so much more widely used that its total overall harm on developer productivity is worse.
Thing is most of these can simply be fixed using an alias, its not that hard to remember '-b' creates a new branch, but 'gcb' followed by the branch name I want is probably easier, however maybe someone wants 'gcob' or just 'cob', thats what Alias is for.
I don’t think those examples were meant to be an exhaustive list of git’s UI warts. There are many that are harder to remember and creating aliases and functions for each of them require building a full alternative UI. For example, how do you see all of the changes in a branch (IIRC the equivalent of ‘hg diff -b branch-name’)? How do you see the changes for just one commit (I.e., ‘hg diff -c $commit’). These things are all feasible in git, but I can never remember the incantation, so I have to Google every time. I haven’t used hg in 5 years and I still have an easier time remembering those commands.
`git diff <commit>' is the set of changes since that commit, not the changes of that commit (the distinction between hg diff -r and hg diff -c). Similarly, `git diff <branch>' is the diff between that branch and the current HEAD, not the diff of the branch itself.
So perhaps it's a great example if you've gotten it wrong?
/u/samatman is definitely mistaken, and I think you are correct (although I'm not sure about "the set of changes since that commit"). As far as I can tell, `git diff <commit>` and `git diff <branch>` both just diff your current workspace against the commit/branch respectively.
In the case of the branch, the correct result is something like "what has changed since my branch diverged from its parent"--basically what you see in a PR. I think this is unnecessarily obscure in Git because a "branch" isn't really a branch in the git data model; rather it's something like "the tip of the branch".
I don't think I've ever wanted to compare my workspace against a branch, but clearly diffing the branch is useful (as evidenced by PRs). Similarly, I'm much less inclined to diff my workspace against a particular commit, but I often want to see the contents of an individual commit (another common operation in the Github UI).
In essence, if Github is any indicator, Git's data model is a subpar fit for the VCS use case.
That's fair, insofar as I'm unfamiliar with the mercurial commands.
It's unfair insofar as it does what I expect it to, which is to diff between what I'm curious about, and where I am.
In other words, if you elide the second argument, it defaults to wherever HEAD is.
The point being, this is not something I personally need to look up. I'd venture a guess that your familiarity with hg is interfering because the conventions are different.
That brings up a deeper issue with git's philosophy. Git's UI is largely geared to introspecting the repository history only insofar as it exists to the currently existing checkout--commands that interact with history without concerning themselves with the current checkout are far more inscrutable, confusing, and difficult to find.
By contrast, Mercurial's UI makes the repository history a more first-class citizen, and it is very easy to answer basic questions about the history of the repository itself. If you're doing any sort of source code archaeology, that functionality is far more valuable than comparing it to the current state: I don't want to know what changed since this 5-year-old patch, I want to know what this 5-year-old patch itself changed to fix an issue.
> I'd venture a guess that your familiarity with hg is interfering because the conventions are different.
Git users also need to answer questions like "What changes are in my feature branch?" (e.g., a PR) and "What changed in this commit?" (e.g., GitHub's single-commit-diff view). These aren't Mercurial-specific questions, they're applicable to all VCSes including Git, as evidenced by the (widely-used) features in GitHub.
Even with Git, I've never wanted to know how my workspace compares to another branch, nor how a given commit compares to my workspace (except when that commit is a small offset off my workspace).
> In other words, if you elide the second argument, it defaults to wherever HEAD is.
Yeah, I get that, but that's not helpful because I still need to calculate the second argument. For example, `git diff master..feature-branch` is incorrect, I want something like `git diff $(git merge-base master feature-branch)..feature-branch` (because the diff is between feature-branch and feature-branch's common ancestor with master, not with HEAD of master).
One of the cool things about Mercurial is it has standard selectors for things. `hg log -b feature-branch` will return just the log entries of the range of commits in the feature-branch (not their ancestors in master, unlike `git log feature-branch`). Similarly, `-c <commit>` always returns a single-commit range (something like <commit>^1..<commit> in git). It's this consistency and sanity in the UI that makes Mercurial so nice to work with, and which allows me to recall with better accuracy the hg commands that I used >5 years ago than the git commands that I've used in the last month.
I use git with no aliases and have forever. I came from SVN, and myself and the entire team I worked with at the time enjoyed the transition and had very few issues.
So much of this drama seems propped up on things that just aren't that difficult.
I'd argue that naming and usability of git is actually very-very up to the point. Naming reflects not the what you want to do on high level, but what you want to do with underlying data structure and checked out files. This could be seen as weakness or unnecessary problem for newbies, but if you work with branches or in multiuser environment, you inevitably would run in some complex and problematic conflict, and then "magical tools" would leave you with a mess, while I've yet to see problem with git repository I couldn't solve.
I've actually taught how to use git to many teams, and I always start with merkle trees. They are actually easy to grasp even for designers and other nontechnical people, and could be explained in 10 minutes with a whiteboard. And then suddenly git starts to totally make sense, and I'd dare to say, become intuitive.
> It's a shame how poor the command-line usability of the git client is.
In comparison to an average CLI program's usability, I think git's got a very good one. It's not perfect, but I think saying it's "poor" is really exaggerating the problems.
In particular, I love how well it does subcommands. You can even add a script `git-foobar` in your $PATH and use it as `git foobar`. It even works with git options automatically like `git -C $repo_dir foobar`.
> It's really hard to explain to new users why you need to run `git checkout HEAD * ` instead of `git reset` as you'd expect
Why would you ever do `git checkout HEAD * ` instead of `git reset --hard`? The only difference is that your checkout command will still leave the changes you've done to hidden files, and I can't think that's ever any good.
> why `git branch [branchname]` just switches to a branch whereas `git checkout -b [branchname]` actually creates it
If you think those behaviors should be switched, good, because they are.
EDIT: How did you manage to add the asterisk to the checkout command in your post so that it's not interpreted as italics without adding a space after it?
I really wish he'd collaborated with some more people in the early stages of writing git to come up with an interface that makes sense, because everyone is constantly paying the cost of those decisions, especially new git learners.