Test of the hg evolve extension for easier upstreaming * Rationale Currently I rework my code extensively before I push it into upstream SVN. Some of that is inconvenient and it would be nicer to have easy to use refactoring tools. [[http://hg-lab.logilab.org/doc/mutable-history/html/index.html][hg evolve]] might offer that. This test uses the mutable-hg extension in revision c70a1091e0d8 (24 changesets after 2.1.0). * Tests :PROPERTIES: :tangle: runtests.sh :noweb: yes :END: #+BEGIN_SRC sh :shebang #!/bin/sh :exports both :results output verbatim # Tests for refactoring history with the evolve extension #+END_SRC ** Init /Initialize the repos I need for the test./ We have one public repo and 2 nonpublishing repos. #+BEGIN_SRC sh :exports both :results output verbatim # Initialize the test repo hg init testpublic # a public repo hg init testmy # my repo hg init testother # other repo # make the two private repos nonpublishing for i in my other do echo "[ui] username = $i [phases] publish = False" > test${i}/.hg/hgrc done #+END_SRC /note: it would be nice if we could just specify nonpublishing with the init command./ ** Prepare /Prepare the content of the repos./ #+BEGIN_SRC sh :exports both :results output verbatim cd testmy echo "Hello World" > hello.txt hg ci -Am "Hello World" hg log -G cd .. #+END_SRC ** Amend /Add a bad change and amend it./ #+BEGIN_SRC sh :exports both :results output verbatim cd testmy sed -i s/World/Evoluton/ hello.txt hg ci -m "Hello Evolution" hg log -G cat hello.txt echo FIX this up sed -i s/Evoluton/Evolution/ hello.txt hg amend -m "Hello Evolution" # pass the message explicitely again to avoid having the editor pop up hg log -G cd .. #+END_SRC ** …together /Add a bad change. Followed by a good change. Pull both into another repo and amend it. Do a good change in the other repo. Then amend the bad change in the original repo, pull it into the other and evolve./ *** Setup Now we change the format to planning a roleplaying session to have a more complex task. We want to present this as coherent story on how to plan a story, so we want clean history. First I do my own change. #+BEGIN_SRC sh :exports both :results output verbatim cd testmy # Now we add the bad change echo "Wishes: - The Solek wants Action - The Judicator wants Action " >> plan.txt hg ci -Am "What the players want" # show what we did hg log -G -r tip # and the good change echo "Places: - The village - The researchers cave " >> plan.txt hg ci -m "The places" hg log -G cd .. #+END_SRC No my file contains the wishes of the players as well as the places. Now we pull the changes into the repo of another game master with whom we plan this game. He adds the important people: #+BEGIN_SRC sh :exports both :results output verbatim hg -R testother pull -u testmy cd testother echo "People: - The Lost - The Specter " >> plan.txt hg ci -m "The people" hg log -G cd .. #+END_SRC *** Fix my side And I realize too late, that my estimate of the wishes of the players was wrong. So I simply amend it. #+BEGIN_SRC sh :exports both :results output verbatim cd testmy hg up -r -2 sed -i "s/The Solek wants Action/The Solek wants emotionally intense situations/" plan.txt hg amend -m "The wishes of the players" hg log -G cd .. #+END_SRC Now I amended my commit, but my history does not look good, yet. Actually it looks evil, since I have 2 heads, which is not so nice. The changeset under which we just pulled away the bad change has become unstable, because its ancestor has been obsoleted, so it has no stable foothold anymore. In other DVCSs, this means that we as users have to find out what was changed and fix it ourselves. Changeset evolution allows us to evolve our repository to get rid of dependencies on obsolete changes. #+BEGIN_SRC sh :exports both :results output verbatim cd testmy hg evolve hg log -G cd .. #+END_SRC Now I have nice looking history without any hassle - and without having to resort to low-level commands. *** Be a nice neighbor But I rewrote history. What happens if my collegue pulls this? #+BEGIN_SRC sh :exports both :results output verbatim hg -R testother pull testmy hg -R testother log -G #+END_SRC As you can see, he is told that his changes became unstable, since they depend on obsolete history. No need to panic: He can just evolve his repo to be state of the art again. #+BEGIN_SRC sh :exports both :results output verbatim hg -R testother evolve #+END_SRC But the unstable change is the current working directory, so evolve does not change it. Instead it tells us, that we might want to call it with `--any`. And as it is the case with most hints in hg, that is actually the case. /note: that message might be a candidate for cleanup./ #+BEGIN_SRC sh :exports both :results output verbatim hg -R testother evolve --any hg -R testother log -G #+END_SRC And as you can see, everything looks nice again. ** Fold /Do multiple commits to create a patch, then fold them into one commit./ Now I go into a bit of a planning spree. #+BEGIN_SRC sh :exports both :results output verbatim cd testmy echo "Scenes:" >> plan.txt hg ci -m "we need scenes" echo "- Lost appears" >> plan.txt hg ci -m "scene" echo "- People vanish" >> plan.txt hg ci -m "scene" echo "- Portals during dreamtime" >> plan.txt hg ci -m "scene" hg log -G cd .. #+END_SRC Yes, I tend to do that… But we actually only need one change, so make it one by folding the last 4 changes changes into a single commit. Since fold does not take -m, yet, we will stop here for now. #+BEGIN_SRC sh :tangle no :exports both :results output verbatim cd testmy # hg fold -r "-1:-4" # hg log -G cd .. #+END_SRC ** Split /Do one big commit, then split it into two atomic commits./ Now I apply the scenes to wishes, places and people. Which is not useful: First I should apply them to the wishes and check if all wishes are fullfilled. But while writing I forgot that, and anxious to show my co-gamemaster, I just did one big commit. #+BEGIN_SRC sh :exports both :results output verbatim cd testmy sed -i "s/The Judicator wants Action/The Judicator wants Action - portals/" plan.txt sed -i "s/The village/The village - lost, vanish, portals/" plan.txt hg ci -m "Apply Scenes to people and places." hg log -G cd .. #+END_SRC Let’s fix that: uncommit it and Normally I would just use `hg record` to interactively select changes to record. Since this is a non-interactive test, I manually undo and redo changes instead. #+BEGIN_SRC sh :exports both :results output verbatim cd testmy hg uncommit --all # to undo all changes, not just those for specified files hg diff sed -i "s/The village - lost, vanish, portals/The village/" plan.txt hg amend -m "Apply scenes to wishes" sed -i "s/The village/The village - lost, vanish, portals/" plan.txt hg commit -m "Apply scenes to places" hg log -G cd .. #+END_SRC ** …as afterthought /Do one big commit, add an atomic commit. Then split the big commit./ Let’s get the changes from our co-gamemaster and apply people to wishes, places and scenes. Then add a scene we need to fullfill the wishes and clean the commits afterwards. First get the changes: #+BEGIN_SRC sh :exports both :results output verbatim cd testmy hg pull ../testother hg merge --tool internal:merge tip # the new head from our co-gamemaster # fix the conflicts sed -i "s/<<<.*local//" plan.txt sed -i "s/====.*/\n/" plan.txt sed -i "s/>>>.*other//" plan.txt hg resolve -m hg commit -m "merge people" hg log -G cd .. #+END_SRC Now we have all changes in our repo. We begin to apply people to wishes, places and scenes. #+BEGIN_SRC sh :exports both :results output verbatim cd testmy sed -i "s/The Solek wants emotionally intense situations/The Solek wants emotionally intense situations | specter, Lost/" plan.txt sed -i "s/Lost appears/Lost appears | Lost/" plan.txt sed -i "s/People vanish/People vanish | Specter/" plan.txt hg commit -m "apply people to wishes, places and scenes" hg log -G cat plan.txt cd .. #+END_SRC As you can see, the specter only applies to the wishes, and we miss a person for the action. Let’s fix that. #+BEGIN_SRC sh :exports both :results output verbatim cd testmy sed -i "s/- The Specter/- The Specter\n- Wild Memories/" plan.txt sed -i "s/- Portals during dreamtime/- Portals during dreamtime\n- Unconnected Memories/" plan.txt hg ci -m "Added wild memories to fullfill the wish for action" hg log -G cat plan.txt cd .. #+END_SRC Now split the big change into applying people first to wishes, then to places and scenes. #+BEGIN_SRC sh :exports both :results output verbatim cd testmy # go back to the big change hg up -r -2 # uncommit it hg uncommit --all # Now rework it into two commits sed -i "s/- Lost appears | Lost/- Lost appears/" plan.txt sed -i "s/- People vanish | Specter/- People vanish/" plan.txt hg amend -m "Apply people to wishes" sed -i "s/- Lost appears/- Lost appears | Lost/" plan.txt sed -i "s/- People vanish/- People vanish | Specter/" plan.txt hg commit -m "Apply people to scenes" # let’s mark this for later use hg book splitchanges # and evolve to get rid of the obsoletes hg evolve --any hg log -G cd .. #+END_SRC You can see the additional commit sticking out. We want to get the history easy to follow, so we just graft the last last change atop the split changes. /note: We seem to have the workdir on the new changeset instead of on the one we did before the evolve. I assume that’s a bug to fix./ #+BEGIN_SRC sh :exports both :results output verbatim cd testmy hg up splitchanges hg graft -O tip hg log -G cd .. #+END_SRC /note: We use graft here, because using a second amend would just change the changeset in between but not add another change. If there had been more changes after the single followup commit, we would simply have called evolve to fix them. Which would have just worked, because graft -O left an obsolete marker on the grafted changeset, so evolve would have seen how to change all its children./ That’s it. All that’s left is fixing the plan, but I’ll rather do that outside this guide :) * Conclusion Evolve does a pretty good job at making it convenient and safe to rework history. If you’re an early adopter, I can advise testing it yourself. Otherwise, it might be better to wait until more early adopters tested it and polished its rough edges. /note: hg amend was subsumed into hg commit --amend, so the dedicated command will likely disappear./