Jump to content
  • Advertisement
  • 01/18/17 08:51 AM
    Sign in to follow this  

    Intermediate Advanced Mercurial DVCS (Hg) tips

    Engines and Middleware

    • Posted By Gru

    Why use Mercurial? Why use command line tools?

    Mercurial (Hg) is a powerful and sleek distributed version control system (DVCS). It's quick and gives you the ability to do anything you'd ever want with source control. 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 externally and can become a part of your standard workflow. 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. With that said, which extensions to choose and where to use them becomes a user's preference. This article is my compilation of best extensions, tools and tactics when using Unity as the engine and Hg as source control. There are 2 main GUI clients 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 that are 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 (or to most people from what I've seen). 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 - without the limitations of different GUIs. Additional options cannot be provided in the GUI, and while GUI seems quicker at first, the command line alternative, stylized properly, can be just as appealing and at least just as fast. This craetes a base, foundational understanding of how Hg works, and from there limitations and abilities of different GUI frontends can be explored.

    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 SourceTree you have installed. The best version of SourceTree for Windows at the time of writing, in my opinion, is, but it comes with an outdated version of Hg. You can tell it to use the 'System Mercurial' (whatever other Hg installation you have on your system), 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), SourceTree for everyday use and TortoiseHg 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 and you know it is installed, you need to add it to your Path system variable. 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 my personal favorite is VisualHg2, as here: https://bitbucket.org/lmn/visualhg2. 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 TortoiseHg previously, thus the recommendation I mentioned above, so all the operations chosen from Visual Studio menu will be diverted to TortoiseHg. 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 (another personal favorite of mine). Highlights include: "knowing" if you want to select a line or a rectangular 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 may 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. Update: In the meantime I have discovered another, faster way to open the console at the current project. There is a free Visual Studio extension called Open Command Line, by Mads Kristensen, which can be configured to work nicely with ConEmu. All I do now is press Alt+Space in Visual Studio and ConEmu is opened at the root of the current project, ready for Hg commands.

    Where to learn it:

    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. It is best to learn from daily usage of these tools once you get the basic concepts. I will not rehash these concepts later as they are quite well known and a part of beginner-intermediate learning curve. 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]. All you would have to do is paste the lines provided in that file. 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 my 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 of this one. 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 SourceTree 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 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 certain cases where you chose to do some of these things. This extension makes it 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. These options will be valid until you push these changesets anywhere. Once that is done, changes are considered "propagated" and messing with history is disabled. Frequent use case is editing the last commit (e.g. we have forgottent to add some file). The easiet way is to use --amend option with commit, which will backup the previous commit, and put the current one instead of it. Same restriction applies from above. mercurial_keyring= Install it by following the instructions here, and you will have to type the password for a repo only once. This is critical when you push/pull a lot! The password is stored in your Windows Credentials Vault, so they are saved securely.


    This is my favorite part, aliases make things so easy and quick, and allow for nice customization. They are personalized shorthands for certain commands with certain options. 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 second part comes in handy with Unity because of .meta files. 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: slog.png Fancy, hmmm? It will also show you the current commit you're on with the '@' sign. 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 above. 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: glog_short.png 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). 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 patch 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... hg_qlist.jpg 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: colors.png 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 (unconnected asset references, etc.). 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/downloads and also the cmdcolor program mentioned before (optional). path2tree.png 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. The output you get by using `hg gst` will be like in the lower part of the image. gstnm = !hg status -X "**.meta" | PathToTree /s /c | cmdcolor As in "graphical status no meta files". 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 is 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 90 Note: its '[defaults]'. Now both in SourceTree and Hg command line addrem command will look for similarity of "90% +". Bonus tip 2: Setting up pre-hook to not allow commits when there are unknown files: 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 | AnyStdIn Note: 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 out this hook's line.


    I hope you have enjoyed this article and tried these commands. Reading about these things is really easy, but in the heat of development, problems in this area 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.

    Update 1 (21.01.2017): Style updates, some new content since first publish.

      Report Article
    Sign in to follow this  

    User Feedback

    Create an account or sign in to comment

    You need to be a member in order to leave a comment

    Create an account

    Sign up for a new account in our community. It's easy!

    Register a new account

    Sign in

    Already have an account? Sign in here.

    Sign In Now

  • Advertisement

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!