Git shouldn't take months to master if you can understand it conceptually rather than by use-cases. If instead of trying to learn commands to affect the working directory, staged files, or history, you learn how git organizes historical tree of commit hashes, then base what each operation does, it becomes much clearer. It's similar to trying to learn strings of shell pipe commands to get things done rather than realizing that there's pieces of work that each command can do. One tip is to use (even very temporary) branches rather than `stash`es unless it's absolutely short lived. And if you ever get lost, `reflog` is your friend.
For me, I had trouble first using `git` after experience with `cvs` and `subversion`. At some point, everything clicked because I inferred its internal model.
git’s problem to newcomers - even people without any prior experience with diff-paradigm systems like SVN and TFS - is the idea of a commit representing a snapshot of a file system is difficult to comprehend because it feels so crazy and impractical (especially as CS101 makes a huge deal about computational complexity). The fact that git internally is actually quite efficient is buried in heavy articles about git’s plumbing.
Oh, and the fact that a ‘git checkout’ is an unrelated concept to ‘svn checkout’ - and how SVN branches are “spatial” compared to git’s “parallel-universes” model is also a source of confusion.
If I could go back in time to 2005 (in addition to getting in super early on bitcoin) I’d beg Linus to not reuse SVN terminology. Personally I’d go with “git switch” instead of checkout, and “timeline” or “line” for short instead of “branch”. The fact that git branches are not 1-connected graph routes is enough reason not to call them “branches”.
I’d also convince him to include a “gitd” mode that would automatically fetch from upstream and automatically add commits when it detects file-move/rename changes. (I understand why git doesn’t have an exact “rename” op, but tooling today still isn’t good enough to detect rename-then-edit or rename-then-split operations without an intermediate rename-only commit. My dream “gitd” would also make auto-commits every 30 seconds just to give me a powerful filesystem undo feature. If you had hours of work that resulted in new files before you staged them and you accidentally did a hard reset then you’re sol - and this has happened to me already :/
In practice, git isn't immutable - people rebase their commits after pushing, or go back in time to remove a file that shouldn't have been there and reindex, and so on.
These actions are not modification, but creation of an alternate reality (commit tree). This gives trust that if we are not happy with this alternate reality, we have some time to revert to the original one (untill garbage collector remove it).
It makes complete sense for programmers learning git to also learn some relevant underpinnings of what git is doing (not necessarily how it achieves it so efficiently). That is a much quicker route than having users guess at similarities with things they've used or imagine while using it and being surprised with it veers off expectations.
It only takes a couple of hours to start being productive while using it though. All the commands you need to know are add, commit, push, pull, branch and checkout. Perhaps add log and reset to that list if you make errors.
That's what everyone says, but for me, coming from a rebase heavy git workflow, I've found Mercurial far more difficult to learn.
For example, with Mercurial, there's at least four different ways to do a rebase-ish thing: transplant, graft, rebase, and rebase (w/ evolve enabled). It's not obvious which a newbie should pick (rebase+evolve... I think?). Likewise, Mercurial has purge and strip which both delete commits in different ways. Git has multiple ways to do the same thing, but at least it's simple and consistent when you lift the hood.
Undoing any sort of rebase-ish operation in Mercurial also seems difficult and janky. It seems to take multiple steps, and involves unbundling some sort of patch file stored underneath your home directory. Whereas in git, you just update a pointer: `git reset --hard $BRANCH@{1}`. Git's reflog is such a fantastic safety net. Doing any sort of history rewriting in Mercurial feels very dangerous, in comparison.
For example, with Mercurial, there's at least four different ways to do a rebase-ish thing: transplant, graft, rebase, and rebase (w/ evolve enabled). It's not obvious which a newbie should pick (rebase+evolve... I think?).
You're cherrypicking--no pun intended.
By default, Mercurial doesn't do any of these operations; you have to activate extensions.
If I were a newbie coming from Git and I wanted a similar workflow using Mercurial, I would probably start with rebase.
I find it kinda ironic when in this message thread, people are praising a chart like this--http://justinhileman.info/article/git-pretty/. It feels like the DVCS version of Helsinki Syndrome…
By design, Mercurial makes it much harder to shoot yourself in the foot.
In contrast, there's an entire cottage industry (https://ohshitgit.com and the like) to help when you--inevitably--get into a bad situation with Git.
> By default, Mercurial doesn't do any of these operations; you have to activate extensions.
graft is actually built-in, but that's beside the point. Coming from a rebase heavy workflow in git, things like updating a ref or abandoning a commit feel like fundamental operations, and when moving to Mercurial it wasn't obvious which of the built-ins or bundled extensions I needed to be reading about to do these things.
I did eventually find Evolve, as you suggest, but it's not one of the bundled extensions, and it's not something you'll find in the official tutorial, or "The Definitive Guide", or even in most of the SO answers explaining how to do git-like things.
> By design, Mercurial makes it much harder to shoot yourself in the foot.
This is true, but git makes it so easy to recover from those mistakes.
Fortunately "transplant" is just an old extension, so you can just forget entirely about it.
"Graft" copies; "Rebase" moves.
Alternatively just forget about graft also, and use "rebase --keep" to copy.
"Rebase with evolve" is conceptually still rebase.
Mercurial has strip (removes changesets from repository, by default stores a backup).
Mercurial does not have purge.
Evolve has ~purge~ prune (marks changesets as obsolete).
Evolve is similar to a git reflog.
Evolve stores more contextual information than the reflog, so Mercurial+Evolve it is safer and easier to undo things than in Git+reflog. Mercurial also pushes some of this contextual information, so even collaborative history rewriting is possible in a safe and easy way, unlike git.
For me, I had trouble first using `git` after experience with `cvs` and `subversion`. At some point, everything clicked because I inferred its internal model.