Why use Mercurial? Why use command line tools?
Mercurial (Hg) is a powerful and sleek distributed version control system. It's quick and gives you the ability to do anything you'd ever want with source control. It can do anything you'd want from a DVCS, and easy and quick. It is an extendible platform because it provides a base command set while more advanced commands have to be explicitly enabled. It is made to be user-friendly and makes sure you know what you're doing. Some user-made extensions can even be downloaded online and can become a part of your standard workflow. With that said, which extensions to choose and where to use them becomes a user's preference. Over the years, it seems Hg has integrated a large part of best extensions into the core distribution and all you have to do is enable them. The installation of other extensions is really simple; you just place a Python file somewhere and point to it in a config file.
There are 2 main GUI clients for it on Windows, SourceTree, and TortoiseHg. First has been my personal favorite for a number of years but recently I have fallen out of love with it – the GUI changes made by Atlassian I personally don't find appealing and worse, some features had to be removed because of it (at the time of writing, they seem to still be catching up). TortoiseHg even seems more powerful, and I've found some features missing in SourceTree (like the ability to compact unpublished history between selected changesets), but the style of its GUI is not really appealing to me.
More importantly, to be able to actually use these options users have to first understand what these commands do and have them memorized to the point where it's instinctive to use them in everyday situations. This comes back to command line because these commands are best learned and explored where they have originally been conceived – in the command line. Additional options cannot be provided in the GUI, and while GUI seems quicker at first, the command line alternative, styled properly, can be just as appealing.
Proper installation:
This whole article is based on Windows usage. You have a couple of options:
1. Download only the executable from the Mercurial website: this is fine and will give you the latest version. However, there is no GUI with it.
2. Download SourceTree and use Hg from the terminal that comes with it. It does not add the path to hg executable to the Path system variable, so you have to do it manually. Also, you will not get the most recent version, depending on which version of ST you have installed. The best version of ST for windows, in my opinion, is 1.7.0.3, but it comes with an outdated version of hg. You can tell it to use the ‘System Mercurial’, whatever other installation you have, but then some things won’t work (like hgflow) without manual fiddling.
3. Download TortoiseHg. It is useful with a Visual Studio extension that I will explain below. I really haven’t used it much because of the archaic interface, but it has its following.
For beginners, I would actually recommend both (2) and (3), ST for everyday use and THg for integration with Visual Studio. To check which hg you have installed, type in: `where hg` and you will get a path to hg executable. If you don’t get anything you need to add it to your Path.
If you use Visual Studio in your development, you may want to have an extension for it which helps a bit with Hg repositories. There are a couple of options, but the best one is VisualHg2, as here: https://bitbucket.org/lmn/visualhg2. This version actually looks good. I would recommend opening a small window with status and docking it in a corner: it shows you which files are changed and provides some common options in the context menu. However, it relies on you having installed THg previously, thus the recommendation and all the operations chosen from VS menu will be diverted to it.
Besides graphical tools, for learning, it is best to use the command line. I am also not a fan of the default Windows cmd console, so I strongly recommend installing a terminal emulator such as ConEmu. That is one of the more useful free tools you can install. Highlights include: “knowing” if you want to select a line or a square space, automatic copy, allowing paste with Ctrl+V, multiple tabs split to sides and bottom, user themes. Just install it and select the default theme, with Consolas 11pt font, and it will look and work great. You also want it integrated into the Right-Click context menu, which it isn’t by default. You do that by going into Settings > Integration and clicking Register on the top option.
Here is how I use these tools. I open a VS project and Right-Click on the Solution “Open Folder in File Explorer”. This opens the folder where the project is. There I Right-Click and chose “ConEmu here” and I can command line from there really quickly. The same works from within Unity, with the "Show in Explorer" option. All the other external tools are optional, but I still use SourceTree sometimes.
Where to learn it:
It is best to learn from daily usage of these tools once you get the basic concepts, which you test on a test project. For basic concepts, there are 3 authoritative resources: http://hginit.com/ for complete beginners, this book: http://hgbook.red-bean.com/ which will tell you almost everything you need to know for the start, and you also need to understand branching. Once you have that, you’re not missing out, even if the book is pretty old. After the basic concepts, it’s pretty much StackOverflow and Googling when you need something, you know the drill.On to the actual content, what do I do next:
You set up all options in mercurial.ini, which is found in your %UserProfile% folder. Once you make changes, you don’t have to restart anything or load anything, they will be used on the next command run. The file is separated in sections, where each section is determined by a heading like this [extensions].So, let’s walk through all the extensions I use:
extdiff=
This is critical. It allows you to specify which GUI tool to use for merging conflicting code files. I would personally recommend CodeCompare, look around the site for tutorials about setting it up.
shelve=
Shelving takes files that status reports as not clean, saves the modifications to a bundle (a shelved change), and reverts the files so that their state in the working directory becomes clean.
To restore these changes to the working directory, use hg unshelve; this will work even if you switch to a different commit.
Let’s say you are working on a specific feature, but you don’t know exactly how to go about it. You try one solution, but you bump into a wall. Now the other solution seems much more appealing. Do you remove all the changes in your code, risking to have to redo them again? Do you commit and this becomes a permanent part of repo history, potentially confusing your teammates? No, you do a shelve operation. This feature for some reason wouldn’t work in SourceTree (at least for me at the time of writing!) and it is so nice that it is worth exploring the command line for.
color=
This colorizes the output of most commonly used commands, really handy with diff and status. Use this one, it makes things really readable.
purge=
This cleans up all the files in the repo that Hg doesn’t know about (haven’t been added to tracking yet). This tends to be really useful in everyday work.
churn=
This command will display a histogram representing the number of changed lines or revisions, grouped according to the given template. Statistics are based on the number of changed lines, or alternatively the number of matching revisions if the --changesets option is specified.
This is one is fancy but optional.
hgext.mq =
Mercurial Queues are critical when working with patches. They are explained in detail in the book I recommended above, and are a bit advanced but useful when you need them. You can’t do this with the GUI.
rebase =
This is a history editing operation, which means it’s advanced (and evil). You can certainly create a mess with it, but you can, for example, join a couple latest commits before pushing them, which is a common use case. It also serves as a base for some other commands such as hgflow and histedit, which automate a number of steps for specific use cases, so I would recommend them instead for these.
hgflow=C:\Program Files (x86)\Atlassian\SourceTree\extras\hgext\hgflow\hgflow.py
HgFlow is an extension that implements gitflow ( http://nvie.com/posts/a-successful-git-branching-model/ ) Let me first say that for GameDev this branching strategy is a bit of an overkill. In fact, there are multitudes of articles online about alternatives to gitflow. What I think works best for GameDev is a variation of GitHub Flow (http://scottchacon.com/2011/08/31/github-flow.html ), as explained in my article here.
If you do decide to use it, it works out of the box in SourceTree, and if you want to use ST with latest Hg installation from the command line, you supply the path to the latest hgflow.py file in your config file. You can see my path in the example code above.
Side note: if you want to use the latest Hg with SourceTree 1.7.0.3 you’ll have to get the latest hgflow.py to the path listed above and it will work with the GUI.
histedit=
What histedit does is:
- 'pick' to [re]order a changeset
- 'drop' to omit changeset
- 'mess' to reword the changeset commit message
- 'fold' to combine it with the preceding changeset
- 'roll' like fold, but discarding this commit's description
- 'edit' to edit this changeset
It is powerful, but you have to know working with editing history is no picnic. There are cases where you chose to do some of these things, and this is where knowledge of tools like this comes in handy. It is easy to do what you want to do: a default text editor will pop up and you will get a choice of what to do through it.
Aliases:
This is my favorite part, aliases make things so easy and quick, and allow for nice customization. Aliases are defined in their own header [alias], they start with the name of the alias followed by ‘=’ and then the parameters that you’d normally supply to hg. If you start an alias with ‘!’ then that alias is instead a System command, and what follows you’d normally type in the shell you’re executing the hg command from. This means the aliases you use may not be cross platform. You don’t even have to type the whole command/alias every time, just the distinguishable part.
clean = !hg revert --all --no-backup && hg purge
Note: It's signs for (and) and (and) above. The editor here seems to have converted it...
Hg already has an alias clean that is a synonym for purge, but I decided to steal it. This creates no problems and ‘just works’. This alias has 2 parts: first, it reverts all files to the state of the last commit; second, it erases all the files found not known to Hg. The commands are separated by ‘&&’ which means you’d normally do something like this in 2 steps. Also, note the no-backup option. This will really clean up the working directory, so you’ve been warned.
pushbranch = push --rev .
In Hg, as opposed to Git, you push all branches when you do a push. This may not be what you want (though it normally is). With this alias you just push the branch you’re currently on.
slog = log --template "\x1B[4m{pad('({rev})',6,' ',True)} \x1B[5m{node|short} \x1B[8;9m{word(0, '{date|isodate}')} {word(1, '{date|isodate}')} \x1B[6m=> {author|user} {ifcontains(rev, revset('.'), '\x1B[8;9m@', ':')} \x1B[7m{desc|firstline} \x1B[8;9m[{branch}]{if(tags, ' [{tags}]')}{if(bookmarks, ' [{bookmarks}]')}\n"
Note: the whole command is a one-liner.
slog is my personal beast! It’s a shorthand of short log. The original log command produces quite line-heavy output, so I prefer output like this:
Fancy, hmmm?
You would have to take a look at the templating mechanism to understand what this actually does, but the more confusing part are terminal codes starting with \x1B. This presumes you’ve enabled the color extension as explained before. After the ‘[‘ sign the code will determine the color. You can read more about terminal codes here: https://en.wikipedia.org/wiki/ANSI_escape_code
This is probably my #1 favorite hg command now, and you limit the output like this hg slog –l 5. I have limited the output already with the -l 15 you can see above, but if you specify it in your command it will take precedence.
glog = log -l 15 -G --template "\x1B[4m({rev}) \x1B[5m{node|short} \x1B[8;9m{word(0, '{date|isodate}')} {word(1, '{date|isodate}')} \x1B[6m({author|user}) {ifcontains(rev, revset('.'), '\x1B[8;9m@', ':')} \x1B[7m{desc|firstline} \x1B[8;9m[{branch}]{if(tags, ' [{tags}]')}{if(bookmarks, ' [{bookmarks}]')}\n"
Note: the whole command is a one-liner.
glog – the graphical log is even better:
You can also see ‘closed branches’ displayed differently on the tree. This arguably replaces any need for a visual tool in most cases (depending on the complexity of your workflow). Another critical alias.
blame = annotate --user -number
A small optional alias that shows who actually changed which line of the supplied file. SourceTree has this option, but I find this really quick, if not necessarily frequently used.
noorig = !del /S/P *.orig
When you have merge conflicts you may leave .orig files laying around after you do the merge. This a shortcut on system command del, and it will prompt you for each individual file and search subdirectories. You would call it like `hg noorig`.
here = log -r .
Just a quick alias to show you the current commit you’re working off of. You could also change it for `here= slog -r .` if you liked that compact output from before, but I don’t see much point in this case.
plog = log --style=compact --patch -r .
As in patch-log. It shows the ‘patch view’ of the last commit relative to the commit before it. The path view is very useful, it shows differences very colorfully.
qlist = !echo \033[34m Series: | cmdcolor && hg qseries && echo \033[95m Applied: | cmdcolor && hg qapplied
Note: It's signs for (and) and (and) above. The editor here seems to have converted it...
To get this to work you need to download the small utility from here (https://github.com/jeremejevs/cmdcolor) and place it in your Path. What it does is it allows for the same coloration we mentioned in the templating alias, but the cmd does not actually support that kind of coloration when output is printed to it. It does work, but the range of options is somewhat limited. Here is a list of colors with codes:
Beyond that, it simply joins the outputs of 2 commands used together commonly when working with patches.
upsafe = update -check
When changing the current working directory, unless the option –check is supplied it will try to carry over the unclean content into the new commit. This seems somewhat out of line with Hg’s “safety first” policy because in Gamedev environments it can create a bit of havoc. It’s better to just use `hg ups` to be safe and let Hg warn you if there are any outstanding changes.
fa = forget "set:added()"
As in forget-all. If you added a range of files by mistake, this is the quickest way to “un-add” them all.
stnm = status -X "**.meta"
Unity’s .meta files are a bit annoying when you have to look at the file system. They also create some visual clutter when reviewing changes either in SourceTree or from the command line. This is a quick shorthand to exclude the .meta files from the output of the status command.
gst = !hg status | PathToTree /s /c | cmdcolor
Now for the real kicker, graphical status. For this, you would need my little utility downloaded from here: https://bitbucket.org/sirgru/pathtotree/overview and also the cmdcolor program mentioned before (optional).
Instead of showing the linear list of files in the output of the status and building the tree in your head, why not let the computer display the tree? This is especially useful to us in GameDev because we tend to have somewhat deep hierarchies of folders. You can read the details about the utility on Bitbucket, so I won’t repeat it here.
gstnm = !hg status -X "**.meta" | PathToTree /s /c | cmdcolor
This combines the outputs of the last 2 aliases: it omits the .meta files and builds the tree.
close = commit -m "Closed branch" --close-branch
When you do feature branches, at some point you'll - of course - want to merge them back into the original. If you don't close feature branches first, they will show in the output of hg heads and hg branches, This may not be what you want, so you have to manually close the branch, preferably before merging it back, by creating another commit with --close-branch option. This option is a shorthand for such boilerplate commit.
Bonus tip:
When you rename a file tracked by Hg, it needs to know it’s the same file and not deletion + addition if you want to keep its history. This is determined by ‘similarity factor’: if it’s just the rename of the file it is 100% similar. However, when working with Unity it’s never just a simple rename: if you rename the source file you have to rename the class too because it would not work otherwise. SourceTree seems to be stuck at 100% similarity and I haven’t seen the way to change that. You need to do something like `hg addrem` and the system should automatically use some similarity lower than 100%. To achieve that, you put in this alias:
[defaults] addremove = --similarity 90Note: its '[defaults]'.
Bonus tip 2: Setting up pre-hook to not allow commits when there are unknown files:</h2>
By default, Hg will not warn you about the files created, but not yet added. This can result in a situation where you have created a commit, but that commit does not include newly created files. Everything seems fine, but the push is incomplete.... and you transfer to a new machine and you're missing files, or your colleagues are confused. This happened to me a couple of times, so I looked for ways to avoid it in the future. Admittedly, it's not hard to check for status every time before a commit, but you can still forget it sometimes. I usually dealt with this with a com --amend option after realizing I forgot to hg add, but there is a saying "if something bothers you more than 3 times within a week, make sure to fix it." So, here's how.
First, you will need my command line utility. It's a trivial thing that only returns a status code based on whether there was some standard input piped in. AnyStdIn.zip This will only work on 64-bit OS-es (Windows). Make sure it's on your Path system variable.
Second, you need to modify your .hg/hgrc file inside the repository that you want this hook to be relevant to. I prefer to have this for all repos, so I put it inside mercurial.ini file. What this does is creates a "hook" that will fire before the commit event, it will list all "untracked files", and if there are any they will be piped into AnyStdIn.exe, which will return status code 1 and abort the commit.
[hooks] precommit.No_Untracked_Files = hg st -u | AnyStdInNote: it's '[hooks]'
And... that's it. If there are untracked files, the message will be something like:
abort: precommit.No_Untracked_Files hook exited with status 1
If for any reason you'd want to avoid this check you'd have to temporarily comment this hook's line.
Conclusion:
I hope you have enjoyed this article and tried these commands. Reading about these things is really easy, but in heat of development, problems of this nature are really not welcome. That’s why it’s important to be properly prepared and practiced when it comes to these things, and have a standardized, reliable, understood workflow in your GameDev shop.
If you're finding this article helpful, stop by here where I have more simple gamedev tips and tutorials, and consider our asset Dialogical on the Unity Asset store for your game dialogues.</p>