ansible/ansible — pull request #9051 by natefoo (2014-09-17 23:07:24+00:00)
Title: Fix numerous bugs in the hg module

##### Issue Type:

Bugfix Pull Request

##### Ansible Version:

ansible 1.7.1

##### Environment:

N/A

##### Summary:

The `hg` module has a few problems:

1. It performs an `hg pull` every time, regardless of whether there is anything to update, e.g. in the case where `revision` is a changeset hash and `dest` is already updated to that changeset hash. This sucks for cases when you are running this module to maintain a clone at a certain exact revision but have a bunch of other tasks to run before and after, and the remote site is slow or down. If `dest` is already at `revision`, you don't need (and probably don't want) to pull and have this module report `changed=True`.
1. After the `hg pull` it performs an `hg update` without a revision, even if one is specified. This will update `dest` to the tip of its current branch. It then attempts an `hg update -r ` *but ignores the result of this command*. If it fails, which it will do if you specified a `revision` that does not exist, you now have a clone in `dest` that is almost certainly not in the state you wanted. What's worse, the module reports success and `changed=True` giving you the impression it did what you asked.
1. The default for `revision` is `default`, which is not consistent with how Mercurial works. The default revision when left unspecified for hg commands is either the tip of the current named branch or the current working directory parent.

Here's what I changed:

1. If the output of `hg --debug id -i` (current *full* changeset hash of the parent) starts with `revision`, the `hg pull` and `hg update` steps are not done. In the case that `revision` is a tag, numerical revision, or branch, it will still pull every time since those are not fixed revisions.
1. If `revision` is set, append `-r ` to `hg update -C` for the `force` option so forcing does not change the revision. `hg revert -a -C` would be even better since it never changes the parent, but it does not revert failed merges, I don't know if anyone cares about that.
1. Change the default for the `revision` param from `default` to null. This mimics the expected behavior of using `hg` on the command line. If I say `hg update` without `-r` I want to update to the tip of my current branch. I do not want to change branches.

This should also address the similar issue addressed in PR #8158, but goes a bit further.

##### Steps To Reproduce:

Start with this test clone:

```
% ansible localhost -i /dev/null -m hg -a "dest=/tmp/bbtest repo=https://bitbucket.org/natefoo/bbtest"
localhost | success >> {
"after": "ebee71b0d80e default tip",
"before": "",
"changed": true,
"cleaned": false
}

% cd /tmp/bbtest
```

**Always pulls (1.):**

```
% hg strip -r cdd2e9c9e218
0 files updated, 0 files merged, 6 files removed, 0 files unresolved
saved backup bundle to /private/tmp/bbtest/.hg/strip-backup/cdd2e9c9e218-backup.hg
% hg tip -R /tmp/bbtest
changeset: 40:01dadf689039
tag: tip
user: Nate Coraor
date: Fri May 28 14:25:22 2010 -0400
summary: added 41

% ansible localhost -i /dev/null -m hg -a "dest=/tmp/bbtest repo=https://bitbucket.org/natefoo/bbtest revision=01dadf689039"
localhost | success >> {
"after": "01dadf689039 default",
"before": "01dadf689039 default tip",
"changed": true,
"cleaned": false
}

```

**Unintended update (2.):**

```
% ansible localhost -i /dev/null -m hg -a "dest=/tmp/bbtest force=no purge=no repo=https://bitbucket.org/natefoo/bbtest revision=bork"
localhost | success >> {
"after": "ebee71b0d80e default tip",
"before": "01dadf689039 default",
"changed": true,
"cleaned": false
}

```

**Default revision crosses branches (3.):**

```
% hg branch stable
marked working directory as branch stable
(branches are permanent and global, did you want a bookmark?)
% hg ci -m 'stable branch'
% hg branch
stable
% ansible localhost -i /dev/null -m hg -a "dest=/tmp/bbtest force=no purge=no repo=https://bitbucket.org/natefoo/bbtest"
localhost | success >> {
"after": "ebee71b0d80e default",
"before": "5817a2fdd755 stable tip",
"changed": true,
"cleaned": false
}

```

##### Expected Results:

- **Always pulls:** tip should be `40:01dadf689039`
- **Unintended update:** The task should fail
- **Default revision crosses branches:** branch should be `stable`

##### Actual Results:

- **Always pulls:** tip is `47:5817a2fdd755`
- **Unintended update:** The task succeeds and `dest` is updated to the tip on the current branch
- **Default revision crosses branches:** branch is `default`