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 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 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.
edit: formatting with the * .
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.
git reset --hardIt'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.
`git branch [branchname]` creates a branch without switching to it.
`git checkout -b [branchname]` creates a branch and checks it out.
And `git reset --hard` will also discard changes. (Arguably, this is better than `git reset` discarding local changes, as it is more explicit.)
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.
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.
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.
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.
Built another tool similar but not same, using the same storage method underneath.
Have you ever worked with Rational ClearCase? It's a true horror show.
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.
Does it happen often at your workplace? What kind of issues are we talking about?
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.
I started in the late 90's when cvs was popular. Then we moved to svn. You had productivity issues of all sorts, mainly with branching and merging.
git show <commitish>
will show the log message and the diff from the parent commit tree.
While there are a metric ton of things which are confusing about git, this was perhaps not the greatest example.
So perhaps it's a great example if you've gotten it wrong?
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.
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.
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.
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.
These are problems that every single person learning git has to figure out and then come up with their own solutions for.
So much of this drama seems propped up on things that just aren't that difficult.
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.
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?