Hacker Read top | best | new | newcomments | leaders | about | bookmarklet login

Git gives me everything I needed MQ for, but with the complete safety of the reflog. There is no such thing as a change I can't undo. The fact that patch management is "special" in Mercurial is the problem!

With respect to history modification:

First, I rebase a couple dozen times per day. I'm on a team that doesn't use merges unless we have a reason to (this makes it easier to bisect and think about history). I also create, destroy, and rebase many of my own branches every day.

Second, I amend commits a lot. I'll often spike some little piece of code I don't understand, then start amending the commit as I rewrite it with TDD, until the commit no longer contains any traces of the original spiked version. For more complex spikes and TDD rewrites, I'll do it over many commits, rebasing the spike over the rewritten version until the spike commit is empty and gets skipped by the rebase. Doing that in Mercurial would be... arduous. I can easily do multiple history rewrites per minute while doing this.

Third, I amend commit messages a lot, usually with "git rebase -i". Maybe I forgot the ticket number, or maybe the meaning of the commit changed (see the next point).

Fourth, I sometimes do drastic commit rearranging. This is harder to explain, but it usually involves splitting commits (in the simple case) or moving sets of related changes from one commit to another (in the complex case). These are sometimes at the file level, sometimes at the hunk level, and sometimes within a hunk. This is rarer than the others; I probably do it once or twice per week.

Fifth, I "git reset <ref>" a lot. It took me longer to start doing this, but it's useful in a lot of situations. For example, "oops, I accidentally created a merge bubble."



sort by: page size:

How is this different from the information stored in `git reflog`, which allows you to rollback to commits before their rebase? You can just as easily revert back to the old version, since it never goes anywhere.

Besides, the need to perfectly preserve history in most cases is totally overblown AFAICS, especially local history nobody else sees. I don't care if a person who submitted patches to me made 20 separate minor commits to fix minor things in some case like a code review (e.g. "fix spelling", "fix 80 column violations", "rename this thing", "clean up code a bit and make it shorter re: code review"); those are superfluous and add no meaning to the actual work itself and can be rebased/squashed away in almost all cases. If they submit 20 minor commits that are each independent of one another and isolated, that's another story.

The alternative seems to just be 'have an ugly history littered with these commits' if "rewriting history" is so incredibly dangerous/terrible like it is always implied (which it is not, because you can always recover from it with the reflog until you push). But I'd rather keep my project history clean and clear; a tidy history is just as important as tidy code IMO. FWIW, I think the OP's set of patches are clear and do not constitute an ugly history.

The actual way to 'stop rewriting history' is to disable --force pushes, which does unilaterally rewrite history for all downstream consumers. This is also true for Mercurial. Rebase does not do this, or anything close to it.

As someone who reads and writes a lot of patches, this is an exceedingly common workflow. How is Mercurial any better in this situation where I don't want all that useless information?


I gave up on rebasing or any form of history rewrite after going through the screw up phase, then watching every dev that came after me do the same at least once.

I realised that treating a git repo like an immutable collection, where existing commits are NEVER mutated makes it far easier to reason about history and nearly impossible to do serious damage.

Devs can do whatever they please with their own local pulls (such as squashing 50 rambling commits during dev), but once a commit is on remote, it never changes.


True. And what makes it easy is it's immutability. When you change history in Git, you rewrite it (rebase). But re-writing is only possible if you know every bit of the journey up to now. When you have actual updates, history is lost.

Like histedit, mq and rebase in hg does you mean?

And it's not an error that the user tells git (s)he wants to remove or squash or whatever some commits. Git gives you that flexibility, which alot of people like, and at the same time gives users an undo-history in case they mess up.

On the other hand, I've used the reflog once in the last four months. For me, the use of reflog really has been exceptional.


I sometimes use --amend, but it's good to get yourself into the habit of just making a new commit, then squashing it with git rebase -i.

I use magit.el for Emacs which makes this really easy, and if you screw up getting your data back from the reflog is a lot easier than if you just had a bunch of --amend fixups.


git is FUCKING HARD due to its tons of poorly designed misfeatures. The index/cache/staging area is the worst of them. If git wants to make any progress towards actual usability, this mess needs to be untangled with prejudice.

Other tools have none of this overly stateful bullshit. When I want a file to be included in the next commit, I don't want a silent, implicit copy to be whisked away into some interal storage the moment I flag it. There is NO reason why merges need to destroy the contents of that same storage area. The totally confusing semantics of the reset command with the three nonsensically named modes soft, mixed and hard are also created solely by this particular misfeature of a magical hidden storage area.

There is also no reason to even support destructive history editing. Immutable history is the correct choice. The mercurial evolve extension, for example, supports rebasing without destroying history.

Also, the combination of not being able to close branches instead of deleting them and not storing branch names in commits makes git history completely undecipherable when you have to go back more than a few merges. You might just as well throw it into the garbage bin.

Just take a look at competing tools (free and commercial) to see what's being innovated in this space and how this widespread obsession with git is in fact preventing much needed progress.


That's one diff between git and Hg. Hg likes to consider the history "sacred" so there's no "cleaning up your commit" via squashing/cherry picking.

This is actually superior in most cases. Being able to force rewrite history so easily in git was a design mistake imo.


There absolutely is version control for rewriting commit history, no fancy tools needed.

Start another branch at the point where you want to rewrite history; don't switch to it: `git branch original-history-branch`.

Now `git rebase` your branch to your heart's content. This branch will have the new, rewritten history.

The original-history-branch still has your old history, refers to your old commits and prevents them from being garbage-collected, just in case you'd like to reset your target branch to that state.


My main issue with Git, other than the terrible UX of the CLI, is just how common it is for one to want to rewrite the commit history - an operation for which there's no version control.

You better get it right, or otherwise you get to nuke the whole repository.


It's crazy how people forget the past. Back in the day, git was "rewrite, rebases modify past to make beautiful commit" vs hg "rewriting past is bad, beautiful commits are lies about history". Turns out people don't care about truthful history.

(Nowadays mercurial can do rebase/amend just fine.. But it is too late)


> Undoing a commit/push means rebasing or resetting, and that's where git drives me insane.

To look on the bright side first, Git is the only(+) DVCS in which you can clean up these kinds of mistakes without cluttering the revision history with "revert" and "oops sorry" changesets. :)

> I have used subversion, CVS, and even Visual SourceSafe, but only in git have I lost previous commits. Again with the misleading terminology. Why call them commits if you can destroy them with a single command?

The changesets are not really destroyed; you have just redeclared the official revision history not to include them, so they aren't visible. The changesets are still alive within the database until the garbage collector picks them up at a later date (then they will be destroyed), and you can access them via looking up the revision ID in the revision log (or sometimes just by scrolling up the terminal window). Once you have the revision ID, you can tag it (or declare it a new branch) so you don't lose it until you're done with the cleanup. Then you can cherry-pick, rebase, merge or do whatever you want to fix the erroneous commit.

This bit of Git causes a lot of headache among new Git users for the first few weeks (me included), since you need to understand the underlying database and really toy around with it a lot to understand how to do stuff like this properly. Though once I finally got it, Git replaced HG as my favourite VCS.

(+) That I know of at least. :) HG supports a "rollback" command, but that only covers one changeset, and you can't really use it if you have pushed your changeset or if it has been pulled by somebody else.


Git has "reset" in addition to "revert" (the former mutates history but the latter does not). What Mercurial has for removing the wrong commit?

For amend option - well, we switched to Git at time of Mercurial 2.1 (I said it was long time ago), did not notice they added this feature, sorry.


That was not my experience at all.

Consider just the simple task of "forget about the last commit I made." Mercurial has like five different ways to do this (hg rollback, hg uncommit, hg strip, hg prune, hg histedit), which work in three totally different ways under the hood (obsolescence markers, backup bundles, or just YOLOing it in the case of Rollback), and which may change their behavior depending on whether you're using `evolve`. As a novice, it's not clear which you should be using (hint: use the evolve extension) or how to recover from mistakes.

Git also has several ways to do it, but all operating on the same principles. Whether you `git reset` or `git branch -m`, or `echo $(git rev-parse $BRANCH~) > .git/refs/heads/$BRANCH`, you're just moving pointers around, and the reflog makes recovery simple.


Oh, I see. I created this great patch and commited it. Then I wanted to push it, but it reported some strange error.

I checked out master but now my commit is no longer there. HELP!

Can I somehow push my changes to you so you can fix it?

Ah let's intuitively try:

git log --graph --decorate $(git rev-list -g --all)

For comparisson:

- mercurial would show your commit all the time, no matter what currently is checked out.

- Only when you push, you have to force it, to create a new head in the remote repository because most workflows expect you to merge heads locally.


> Git history is intended to be immutable when working with others.

That's another pet peeve (though maybe less git's fault): immutable, yes, but then rebase is pushed quite liberally. Arguably not by git itself, but by many online learning resources.

It's convenient, and mostly works, but then occasionally really stings you.

...yet undoing the last commit, arguably the least aggressive of history-rewriting commands, remains awkward.


A very basic issue with git is that if you're editing a stack of commits, you can't go back to an earlier commit without completing the rebase -i and restarting it.

I wish git had a “metahistory” feature to allow everyone to undo anything. A `git revert` isn’t of any help when you’ve already merged and pushed.

Bah, that's bullshit. These are Git features and meant to be used, and these are part of the reason Git rocks so great.

It's not lying, it's cleaning up. History rewriting is assembling all the intermediate crap into a comprehensible patch that as an added bonus also comes with a hindsight, or keeping sense in maintaining your work-in-progress on top of some other branch by continuous rebasing.

It's not lying, it's making sense. It's the same thing why you don't save your Emacs editing history to have someone else replay it to produce the source file. You just save the source file because you've spent time doing work to eventually produce something that makes sense. You don't want to bother other people with your errors. It's of no value to them.

However, the time you do not want to do this with public commits. Anything that you have pushed or someone has pulled is public. Anything that flies out of your local nest shall become immutable.


Git allows rewriting history though.
next

Legal | privacy