When I first learned git, I thought it's pretty neat. It solves merge, branch, rewind problems. Git is one of the things in life that doesn't work like the way we think. But it turns out to be a better way.
That’s not all it has going for it either. Mercurial has a concept of commit stages to make history rewriting safer. It has a commit model that enables you to work on and manipulate branches of commits seamlessly, without needing named branches. It has not just a tree of commits but also each commit tracks its own history through rewrites. You get cool commands like absorb and evolve. It’s easier to extend than Git.
The only downside to modern Mercurial I can think of is it’s still slower than Git, by at least a bit. But it can scale incredibly far with its extensibility. For example, what Facebook did:
https://code.fb.com/core-data/scaling-mercurial-at-facebook/
So why does it never seem to get consideration? I guess it’s because of the insane proliferation of Github and Linux, which is definitely more a blessing than a curse. But it’s weird. Back in the CVS and SVN days, it didn’t seem like there was ever going to be a ‘network effect’ for source control like there is today.
I kind of wish Gitlab would implement Mercurial support. I bet it would help Mercurial gain more adoption within teams working on closed source projects. I know Bitbucket does, but to be honest that doesn’t really appeal to me much.
I think that's true if you're coming from something like Perforce or Subversion and have some notion of what the expectations are around those environments. It's much easier to translate that model into Mercurial commands.
But in the end it's deceptive; git, ugly as it is, reflects the underlying data model. Once you understand that data model, and how the basic commands manipulate that model, then it becomes a very natural language for manipulating the commit chain.
If there's a downside, it's that git doesn't let you get away for too long without learning the data model, while Mercurial lets you live in blessed ignorance until the first time it doesn't work like svn does, and then you're stuck because its data model is not as cleanly exposed, so you need to gently massage it into a good state rather than just surgically moving it to where you want it to be.
Fossil is another matter. Fossil defies pithy car analogies. Integrating the bug tracker into the version control alone is a game changer, and that's not even all that Fossil does.
Keith Packard's "Repository Formats Matter" post nicely captures how meaningless it is to focus on this sort of thing in the long term: https://keithp.com/blogs/Repository_Formats_Matter/
I.e. yes Git has some UI issues, but those are fixable, whereas e.g. Subversion's UI was way better than Git in the early days, but its repository format limitations inherently weren't fixable.
> Mercurial has a concept of commit stages to make history rewriting safer.
Yeah that's a really neat feature. For what it's worth some people at Google seem to be working on trying to get an equivalent feature into Git.
> For example, what Facebook did[...]
Much of what drove Facebook to Mercurial has since been addressed, e.g. "status" times being slow due to lack of inotify-like integration. That's now a feature of core git. Some of the rest is being worked on and actively upstreamed, e.g. from the GVFS effort: https://vfsforgit.org
I found git easier to learn and use. Git has just a few basic concepts underlying how it works. Once I learned blobs, trees, commits, the index, and references (branches, tags, reflog, etc), I understood the model. It wasn't hard to grok a local vs remote repo, how history was a dag, and so forth. From there, I realized all the crazy commands, the baroque UI, these were all just ways of manipulating those objects in various ways. I could always figure out how to make git do what I wanted. It never fought me or tried to tell me I couldn't do something.
hg, by contrast, I felt was always trying to hold my hand. Its model was harder to understand. It required me to adapt to its workflow, instead of allowing me to adapt it to mine. It was generally much less flexible.
I found everything about git more Unix-philosophy like.
I understand completely if you don't want to think about your VCS and just want it to record your changes and otherwise get out of your way, but I wanted a more flexible and powerful tool.
There is some work being done on it, including a prototype: https://gitlab.com/gitlab-org/gitlab-ce/issues/31600#note_15...
It’s a really nice tool that I’ve been using for several years to push to git repositories.
I think the way forward for Mercurial would be to become a Git frontend, or "porcelain" as they call it. That way we would get the best of both worlds: the ubiquitous git format and hosting infrastructure; and the superior UI and workflow of Mercurial. I'm sure it would be a difficult undertaking, and might require difficult trade offs between compatibility, performance and usability. But I think it would be worth it. For one, Mercurial would become relevant again, and probably get a lot of new users. A lot of users would probably come back. And Git users would also benefit from the renewed competition between competing frontends.
Hi! GitLab employee here. Mercurial support has been a hot-ish topic internally. If you want to weigh in then please comment on this Issue, since Issue interaction plays a big part of how we prioritize https://gitlab.com/gitlab-org/gitlab-ce/issues/31600
(edit: I'm an idiot I should've read the full thread first since someone already linked it. gonna leave it up anyway)
Part of it is not wanted to learn multiple VCS's. Part of it is Github. Part of it is performance.
I honestly believe that if Github supported Mercurial, or if Bitbucket had "won" over Github, then Git:Mercurial would be something like 4:1.
Git won because it was a) unopinionated and b) powerful enough to support arbitrary workflows for any enterprise.
For the enterprise, being 'uniform and predictable' is way, way, way down lower on the list of important criteria in version control. (And in fact may even be a negative, due to the weird legacy workflows many enterprises have.)
What I will NEVER be able to understand is WHY the commands are so fucked-up, inconsistent and counter-intuitive. It doesn't have to be that way.
If git feels comfortable, it's only because you've crashed and burned it so many times or suffered though countless google searches to eventually remember the commands that are appropriate for your workflow.
Git has a shitty UI when you start with, and it still has a shitty UI years down the line.
The underlying model is neat and interesting, but that you have to know it to find any usability or elegance is an indictment.
This, exactly. Git was written by Linus Torvalds. You could literally replace the word "Git" in that sentence with any other software written by Linus Torvalds and it would remain just as valid. The guy writes software that has a learning curve, but does does its job elegantly. He's not writing software for the social-media-no-attention-span crowd.
And I feel it in my bones that there is a revolutionary GUI waiting to be invented. Why can't I drag a commit or set of commits from one branch to another? With safe, easy undo (reflog doesn't count) and super smooth conflict resolution? Etc etc.
And of course there is the interesting rabbit hole of semanitc / language aware diff. Line diffs suck in many ways.
It's one of the hundreds of of problems that I'd love to work on one day, but probably won't get a chance to. Sigh... :)
It is indeed being invented. It’s called Pijul: http://pijul.org/
This tool is based on strong mathematical theory of patches, instead of snapshot/commit-based. It seems simpler to reason with, but we’d have to unlearn a lot from Git.
It’s not suitable for big projects yet, but it’s already used by Pijul itself and other Rust components. And it already have its own „Github” called the Nest (because pijul is a bird). Pretty promising imho.
Is this a good thing? What practical problems does a strong mathematical theory of patches solve that git doesn’t? And what’s the difference between a commit and a patch? Aren’t git commits stored as patches?
I’m a math lover, but my gut reaction to that idea is that it sounds off-putting. I don’t mean that as a judgement or insult; I’m admitting my own assumption and bias here, jumping to unwarranted conclusion, not saying anything is wrong with pijul. But when the elevator sales pitch is “strong math”, it immediately makes me assume it’s too technical for a normal programmer and focused on academic ideals rather than getting practical work done as easily as possible.
The FAQ even says, “Pijul is trivial for whoever knows category theory.” Is that question really asked frequently? Words like that might convince me to never try it. ;)
darcs was nice, up until you hit the exponential merge problem.
It is so much better than Git CLI, which probably is too low level for daily usage.
I sometimes turn to magit when I want to navigate a file's history through git-blame, though.
So I get this vague feeling that someone could build a better human-oriented suite of CLI tools on top of Git's internals―with the internals better mapped to people's more casual understanding of the concepts and their aims.
But that someone isn't me, yet.
Because the hardest part of conflicts is already the conflicting changes themselves and not the source control.
For example say I change one line of code in a big function. I rebase and that function has moved. Conflict!
I want a tool that says "here's what you changed, and here's the current state of the source". None of them do that though - they all just show the conflicts that git writes to disk - the code after you changed it, and the current code.
You basically get two copies of the function, one with your change and one without and you have to manually (visually) diff them to work out what you changed (or go back and look at your commit) and then reapply that change to the moved function.
It's really awkward and could definitely be better.
The model of git feels very nice and intuitive once you grasp the central architecture -- an immutable append-only content-addressed file system. The changes mentioned here in particular just don't really fit the model. You can certainly argue that we need a better model, but in order to move commits between branches in anything other than a mechanical manner, you would have to deal with the fact that often in source code repositories there are semantic connections between unrelated changes -- one file has a change to update its interface, the other file changes to use the old interface, and now you have something that no general-purpose version control software can reasonable reconcile or even identify as an issue.
Arguable there's a space for merging tools that build on the git model and contain language semantic information, so it can actually highlight conflicts that are deeper than patch-based, but until then I'd prefer that the version control system not try to be too smart and let me repair things after it does its mechanical actions.
> The CLI in particular ... Line diffs suck in many ways
These are changes that I think are in line with what I see in the future of git -- because git just contains a series of snapshots of a hierarchical tree, diffs are a secondary thing that an external tool can be brought to bear on very easily -- rather than trying to merge patches to create a diff, you actually have two fully realized files, together, potentially, with their common ancestor, which is all the information necessary to create a fully semantics-aware diff between files or trees.
I would almost want git to double-down on the grammar of tree changes; commands like "checkout" and "reset" are very basic manipulations with fairly clear idioms. But since those words are also English words that carry a whole bunch of connotations, it makes it confusing when you use "reset" to do something that does not involve returning something to its original state.
Having a "worse" command line interface, with some commands like "git make-my-working-tree-look-like-this-ref" and "git point-this-ref-to-this-other-ref" would break the connection with traditional version control systems while allowing us to use the full power of the git model.
GitKraken might be for you, there is a nice drag-and-drop feature demonstration at https://www.gitkraken.com/git-client.
I don't think you can drag commits because a commit itself is a snapshot that describes changes to a branch so it is the branch itself that can be merged, rebased, pushed, etc. by drag-and-drop.
- [SemanticMerge](https://www.semanticmerge.com/)
Regarding language-aware AST-based diffs, I know of one serious full-time effort. Even for Java, it turned way too complex, so guys gave up.
It was disappointing to me that web interfaces do not offer something like this
I think that's fair to say. I think that's always fair to say.
Honestly, it faded into the background of code from the beginning. I mean, I know "Forward-port local commits to the updated upstream head" means nothing to anyone not already familiar with `git rebase` but a practical mastery of the tool is very easy to achieve.
I honestly think this is a pedagogical lack. We tell everyone it's this complex thing and that they should be scared of rebase and the reflog and they believe it. Maybe if we didn't, it'd be easier.
Like I get it, we all enjoy making fun of vi but imagine every thread about vi only filled with people harping about ":wq". It gets tiring real soon.
The truth is, it's not so easy a task to get versioning right and git does, people are not humble enough.
Nothing shameful in someone sharing that knowledge to help a newcomer become a super-user faster.
Reason I posted top-level is that it isn’t an indictment of any specific “this is hard” poster but of the general view. I don’t know when it started becoming this common belief but it’s not true.
* Bart turnstiles: the green lit-up arrow doesn’t mean you can use the turnstile with your card. Only if that arrow is present and the Clipper reader has a green light on it can you use it. Not hard, really.
* Doors that you should push but have a handle: the presence of the handle implies a pull and yet you have to push. Hard? No.
* Applications which use a floppy disk icon for save. Not hard at all.
Honestly, I think the UI is like any tool. You learn it in a day and through use it gets so familiar you don’t even think about it. But many have complained about this and UIs are harder to evaluate, so I can easily admit I’m wrong here. The difficulty, though, that’s overstated. The absolute ease with which it came to me and to nearly everyone I know puts the lie to that.
If people _did_ learn those concepts, they wouldn't find it "impossible to grasp" and then it would be more appropriate to compare one's knowledge of git to their programming knowledge.
So many things wrong with your premises...
How do you know every git user is writing code? Is git only for code now?
And in the code case... were you born knowing how to use git? Or were you forced to learn it before you wrote a single line of code? Or were you forced not to learn it until you were a pro coder? Is it difficult to fathom something landing between these extremes?
It's a tool. Any tool can be confusing if the person isn't taught how to use it. Git requires teaching so there's a lot of room for misunderstanding.
You never have to do this, because it's virtually impossible to screw up a git repo with git commands, to the point it can't be easily fixed. And I'll tell you why and how.
As you no doubt know, all commits in git are hashed. This means that you can't change a commit in any way, only ever add a new one. Neither can you insert a commit into an existing chain: you have to rewrite the whole chain with new commits.
As a consequence, if your `master` branch, for example, is pointing to commit `aa33bf`, there will be a unique chain of commits leading back to the original commit which can't be altered.
It doesn't matter if you cherry-pick, merge, rebase or dance the fandango...if you point `master` back to `aa33bf` it will look exactly the same as it did before.
You can usually find what the branch was pointing before with `git reflog`. But you were going to clone it from github again, right? So, `origin/master` is pointing to the right commit, and you can just reset it back to that (remember to first commit any random stuff in your working dir that you want to keep):
git checkout master
git reset --hard origin/master
And, obviously, you can do exactly the same with any other branches that have been messed up.There you go. Now you too are a "git hot shot" and can wow your friends with your amazing skills. ;)
And confusing can be and imho in this case is, something else than difficult.
I think I'd be completely unsurprised to see an intern successfully use `rerere` on a longer project of theirs.
This is a horrible article. You should not bookmark it or use it. If you're not a programmer, you shouldn't use git. If you are a programmer, do yourself a favour and spend a day going through something like this: https://wyag.thb.lt/
It will make you better at git and better at programming. Git is a powerful tool and you need to learn how to use it. Imagine if people read articles like this one instead of learning how to drive.
It's probably helpful to know some basics, but do I need to know intimate details of my filesystem to use cp, mv, shell redirection? No. For most basic actions it Just Works™.
The problems in git are purely user-interface based. Other distributed systems have proven you can make a dcvs with a reasonably friendly UI.
Git is not providing you with something you understand. It is providing you with a DAG and you neither understand what that is or why you need it. The DAG is not the "internals of git". This is the big mistake. It is git. Everything about git is about building that DAG.
I'm decent at got now for the work I do so I'm cool with it. It really is an awesome tool. But for some reason its just collectively taught like shit.
But git isn't just for programmers, and cheat sheets can be good for people who just want to dip their toes in.
Graph = graph, a structure composed of a set of objects (nodes or vertices) with links between them (edges).
Directed = the edges have an orientation / a direction.
Acyclic = there's no cycle, you can't come back to a node (in a directed graph you have to follow edge direction).
In Git, the commit objects are nodes, the "parent" link(s) is a directed edge, and because commit objects are immutable you can't create a commit which refers to one of its "descendants" and thus the graph is acyclic.
I think it might be nice to add a disclaimer saying that this is not advisable if you've already pushed the code. Suck it up and make a new commit–don't rewrite public Git history.
Or, someone might have created another branch off your feature branch, because they depend on your work. Now you've creaed a time bomb for them when they try to merge their work after you've merged your alternative-history version of it. (and the failure mode is just weird, it takes experience to identify that all those seemingly nonsensical merge conflicts are result of this situation)
Etc. It just breaks a lot of things. The Git model and bad UI are already taxing enough to work with in your head, concurrently with your actual programming and domain cognitive load, that adding the uncertainty and multiplied complexity from having history rewritten around you is just a bad tradeoff.
(This may be different if the scenario is not a team, of course...)
I push my code up to my origin as soon as I can and as I go I’ll fix up my commits and force push.
There are some advantages for me anyway. Pushing to origin kicks off some smoke tests and end to end tests that are fairy slow and cumbersome to run on my dev machine. That helps me catch bugs earlier, especially since I’m working on a Microservices architecture. Also it acts as a backup for if my dev machine dies on me. I prefer to fix up and force push to create a clean logical story from my code rather than leave in spurious commits which exist only to fix linting for example.
So it would change your advice to: Don’t rewrite public Git history unless you can assume it’s read only.
Patches for Linux get rewritten all the time till they are finally merged.
You're most likely making things worse. Not only will you have the mess-up commit(s), but also the un-mess-up revert-commit. It gets super hairy when reverting merges (I assume you're not rebasing + fast-forwarding if you lobby against rewriting).
There is no big deal in rebasing to a rewritten master branch. It's just like any other rebase.
IMHO, rebasing signals that someone wants to apply a patch, and knows exactly where. Merging/reverting/etc. signals someone wants to "upload his latest stuff", like to a dropbox.
I can lose something for you in 2 seconds in git. Have fun e.g. recovering from this:
$ git init
$ mkdir -p widget && echo Introduction > widget/readme.txt
$ git add widget
$ git commit -m "Initial commit"
$ echo Conclusion > widget/readme.txt
$ git checkout widget
$ cat widget/readme.txt # No "Conclusion"??You just demonstrated losing something that was not in git... All you've demonstrated is that neglecting to put something into git is a good way of losing it.
Then like a year on I suddenly realized Ohhhhh it's ref-log, that makes a lot more sense!
Ideally your code review system should be the only one to have merge rights to master. Then nobody can singularly break master at least.
Recently I discovered that it is found under "git help revisions".
It's worth scrounging through git(1) anyway; it mentions several other manpages for things like recommended workflows, the basic structure of the .git/ directory, not to mention gittutorial(7).
And it's remarkably hard to find using web searches, since most git documentation uses the term "commit" for these things, not "revision".[0] I found it after I discovered "git help -g", which lists "some concept guides" according to "git help".
Of course, n=1 and stuff, but there's a lot about git that is not obviously documented. Something like this should be linked to in multiple places, so even if you skim over one you'll catch it relatively early.
[0] The name of the man page was chosen to avoid conflict with the "git commit" command, I guess?
Seems fairly magical compared to other stuff here. To me at least. Can anyone briefly explain what it does?
git reset means 'point the current branch to this commit instead of wherver it's pointing now' (branches in git are just pointers to commits)
git reset --hard means 'also reset the state of the checkout and staging area to be in sync with the commit'
Thus, the entire spell means 'reset the current branch to make it point the the seecond-to-last-commit, also ensure my current checkout and staging area are in sync to that', or, in other words, fully forget and drop the latest commit.
This seems like mistake or a non-standard usage of the english phrase "second to last". Given a git log of:
$ git log --oneline
15e0437 - (HEAD -> master) this is the third commit
f82d1fd - this is the second commit
9180c17 - initial commit
I would call "f82d1fd" the "next to last commit" and it can be referred to as HEAD~
I would call "9180c17" the "second to last commit" and it can be referred to as HEAD~2In the mean time, 'thanks' to Git, the source change history became a maintenance line-item. The expectations of a clean history were raised almost to the level of expectations for bug-free code.
I can see a utility of clean feature history, but asking developers to craft the history is shifting their focus away from the actual code. As long as the source state has been saved, the source control has done its main job.
So for the most of the listed 'shits', the developer should just be able to revert, cherry-pick, and re-commit, and keep going. Nothing esoteric and hard to remember, also fairly common commands across different VCS tools. Shit happens and will happen again, no biggie, no need to blame and shame, source annotation will show the right change/comment anyway.
Like no good program, ever, to use git you have to understand all the compromises and all the internals of its data structures. What a joke.
If you need to reset with reflog a lot you're probably using git wrong.
Sure it can be useful but I don't see why it should be in a workflow.
The way it worked well was that the "--staged" flag would be implied if you had already staged some files to be committed.
But on this day I noticed that nothing bad happened from that behavior. So I time traveled back and whispered to the git devs that the interface should be made more pedantic to keep users from relying too much on git to do the right thing for them.
Now it's great because users suffer and I have plausible deniability from this now being on par with the rest of git's interface.
Oh shit someone has fixed an old commit message that was already pushed to other repositories.
Oh shit, this commit ough to be a merge commit. The tree is good, but not the parents.