1. 24
  1.  

  2. 12

    My favorite thing about Evolution, once I’ve gotten the hang of it, is how it makes editing history safe and easy, while also preserving the full “real” history.

    The other day, I realized my editor was being stupid, so I quickly grabbed a .editorconfig from another project on my machine, committed it, and kept hacking. It wasn’t until two or three commits later that I realized the .editorconfig I happened to grab trailed off mid-line, which Visual Studio Code was actually cool with, but IntelliJ was not—and which was at any rate invalid.

    The old way to fix this would be some combination of hg histedit or a dive through MQ on Mercurial, and a collection of commits and rebase commands in Git. But Evolution?

    $ hg up <commit with bad .editorconfig>
    $ <fix it>
    $ hg amend         # update the commit
    $ hg evolve --all  # automatically fix up all the children commits
    $ hg up            # go back to tip
    

    That is way easier—and it trivially preserves the whole, real history to boot, for later if I accidentally realize there was something in the original-yet-buggy .editorconfig I wanted or the like.

    You can see an example of this if you’d like: when viewing that changeset, off to the side, you’ll see a link marked « 791b15d. That’s the original version of that changeset. If you click it, you’ll see the obsolete commit, which both clearly identifies that it’s obsolete, and which links to the changeset obsoleted it. Git has no equivalent of this, and I’ve found it amazingly useful even in my own little one-person projects.

    1. 2

      Ah cool, I didn’t realize @smf added a UI to bitbucket to see the obsolete versions of successor commits.

    2. 10

      This is really cool, and the appropriate response to this.

      Rebasing can be made safe and distributed. Git can’t do it because there’s not enough metadata, but this is precisely the problem that hg evolve solves: a distributed meta-history of which commit replaces which commit.

      I wish the damn thing would just come out of the perpetual beta it’s been in for seven years or so. :-/

      1. 3

        I agree this is cool, but these are not things that should be used daily, changing history is stupid. Pretty much the only reason I can see using a tool like this is to immediately back out a password(or other secret) committed by mistake. But really the right answer is just change the password/secret, make a new commit removing it and note that this is not valid anymore, let the history stand as people making mistakes and move along with life.

        1. 13

          I disagree. One area where this is insanely useful on a daily workflow is code review, because it gives you the best of both worlds: the mainstream, published history can be the versions of the patches that were finally accepted, with all fixes and any squashing or splitting that needed to happen, but if I need to later ask questions like, “why was this particular solution done here, rather than the alternatives?”, then I stand a much higher chance of being able to trivially answer that within the SCM by tracking obsolete versions of that changeset. This is a huge improvement, in practice, from trying to discover what GitHub PRs existed for a given commit and track those histories entirely through the GitHub UI.

          1. 1

            hrm. for me, code review belongs in the other person’s branch.. if Tootie wants me to merge her stuff, then I’ll look at her code on her branch/repo and then merge into the main branch/repo. i.e. hg incoming -vp <Tootie's repo>

            But if you were doing code-review ala github or other methods, then I can see where this could be useful.

            1. 1

              Does that mean you’re not doing CI? You might want to consider setting up a CI server - it’s very useful.

              1. 1

                I’m not sure where you got no CI from what I said… but yes, I’m all for CI/CD. On push to the central VCS server (regardless of repo/branch), Jenkins will go forth and run some tests, etc. If it happens to be the main stable(or dev/test/etc) branch, it will also deploy. I definitely agree it’s useful.

                The nice thing about my method is it’s decentralized, and anyone can use whatever tools they want to do code review, much like the Linux Kernel. If Tootie wants 500 feature branches or repo’s, then she can have them, organized however she wants. When it’s time to merge into the main branch, the main server repo(s) require a gpg signature from 1 other developer (via the commitsigs extension) – it’s just a test that’s run as part of the CI run, which effectively makes our code reviews GPG signed.

                1. 1

                  Ah, I see, your central server has all the in-development branches. The nice thing about the evolve workflow is that you can have a publishing server that has a canonical linear history where all the tests pass on every commit. It takes some tooling and discipline to get there, but the benefit is that bisect is always useful and it’s easier to follow how the codebase evolved over periods when many developers were working simultaneously.

                  1. 1

                    For us, every push, regardless of branch/repo gets tested by Jenkins, a commit hook on our central repo calls into Jenkins to run. Can you explain more about your workflow and how it works? I think maybe I’m missing something. How does every commit get tested?

                    1. 1

                      After passing code review from one or two other developers, and after the test bot certifies that the tests pass on every commit in the series, a bot rebases the series onto the current default branch and then pushes to the publishing server. In principle one could also squash the series into a single commit but that means that each commit is no longer as easy to read. We like to keep commits atomic and manageable to review by a person without getting overwhelmed by a huge diff.

                      1. 1

                        Assuming I understand you correctly, this is how it works:

                        So developer A makes a small change, commits it, pushes it to Server A. Server A then runs tests, assuming pass - dev A begs 1-2 other devs to review their commit. After code review is signed off (via what mechanism?) a bot rebases it onto some new repo, and pushes to Server B. Server B then does the standard CI/CD stuff and deploys?

          2. 7

            changing history is stupid

            Mercurial keeps track of whether a changeset is public (because you pushed it/someone pulled it) or draft. By default, Evolve-related commands (rebase, prune, fold, evolve, and others), will only change unpublished history, that is the changesets that are still in draft mode: this prevents you from accidently editing history others may already depend on. As Gecko says, it’s a pleasure to be able to edit history without destroying the old history.

            You can also share a history that you mutate, e.g. with a close colleague or with yourself on another device. Create a so-called ‘non-publishing’ repo: changesets pushed to this repository remain in draft mode. Evolve now supports sharing your history mutations. It’s a rare use case, but it’s supported; and Evolve’s notion of ‘obsolesence’ and ‘successor changesets’ is what makes it possible to support it at all.

            1. 3

              Yes, Mercurial is awesome, I’m not disagreeing, it’s what I use. Like I said this is very cool, but I think as a daily driver it’s not a good idea to use. That it doesn’t destroy old history like Git does is indeed a breath of fresh air. I’m not at all disagreeing with how it’s implemented/what it does. It’s great the tool exists for when you need it, I just don’t think one should be using it all the time.

            2. 4

              In addition to code review, another reason to modify history on a daily basis is to avoid broken commits. It’s generally good practice to avoid landing any commits that lead to a broken state (in build or tests). Otherwise other contributors who happen to rebase onto the busted commit will be left wondering whether or not their changes are responsible for the failures.

              Rather than committing a “bustage” fix on top, much better to change history and make sure the broken commit never makes it into upstream in the first place.

              1. 1

                For large teams, or where you are not able to communicate in basically real-time, I can see this being useful. For smaller teams and/or where real-time communication(i.e. a chat channel/etc) I don’t see this as a big deal. But I can agree there are times where this would be useful, but if you are breaking builds on a daily basis.. maybe you are doing something wrong? :P

            3. 3

              Evolve is one of the best feat. of Mercurial. We (RhodeCode) are shipping evolve extensions with fully enable per-repository support of it from the previous release.

              What really surprised us is that lots of folks actually use it. We got lots of feedback and for sure we’ll update support for it.