Repository: audreyr/cookiecutter (python)

Issue:
gh: prefix doesn't work anymore
* Cookiecutter version: 1.5.1
* Template project url: `gh:*`
* Python version: 2.7.13
* Operating System: Linux

### Description:

cookiecutter does not honor prefixes anymore.

### What I've run:

Simply testing the example from the README doesn't work as expected:

``` bash
$ cookiecutter gh:audreyr/cookiecutter-pypackage
A valid repository for "gh:audreyr/cookiecutter-pypackage" could not be found in the following locations:
gh:audreyr/cookiecutter-pypackage
/home/me/.cookiecutters/gh:audreyr/cookiecutter-pypackage
```
The same commands using the full repository path works as expected:

```bash
$ cookiecutter https://github.com/audreyr/cookiecutter-pypackage
```


Pull request description:
Fix missing builtin abbreviations
Resolve #966 

Patch:
diff --git a/cookiecutter/config.py b/cookiecutter/config.py
index f2e7d24..42214b8 100644
--- a/cookiecutter/config.py
+++ b/cookiecutter/config.py
@@ -39,6 +39,25 @@ def _expand_path(path):
     return path
 
 
+def merge_configs(default, overwrite):
+    """Recursively update a dict with the key/value pair of another.
+
+    Dict values that are dictionaries themselves will be updated, whilst
+    preserving existing keys.
+    """
+    new_config = copy.deepcopy(default)
+
+    for k, v in overwrite.items():
+        # Make sure to preserve existing items in
+        # nested dicts, for example `abbreviations`
+        if isinstance(v, dict):
+            new_config[k] = merge_configs(default[k], v)
+        else:
+            new_config[k] = v
+
+    return new_config
+
+
 def get_config(config_path):
     """Retrieve the config from the specified path, returning a config dict."""
     if not os.path.exists(config_path):
@@ -54,8 +73,7 @@ def get_config(config_path):
                 ''.format(config_path, e)
             )
 
-    config_dict = copy.copy(DEFAULT_CONFIG)
-    config_dict.update(yaml_dict)
+    config_dict = merge_configs(DEFAULT_CONFIG, yaml_dict)
 
     raw_replay_dir = config_dict['replay_dir']
     config_dict['replay_dir'] = _expand_path(raw_replay_dir)


Test patch:
diff --git a/tests/test-config/valid-config.yaml b/tests/test-config/valid-config.yaml
index 95150d9..1a10bf5 100644
--- a/tests/test-config/valid-config.yaml
+++ b/tests/test-config/valid-config.yaml
@@ -4,3 +4,5 @@ default_context:
     github_username: "example"
 cookiecutters_dir: "/home/example/some-path-to-templates"
 replay_dir: "/home/example/some-path-to-replay-files"
+abbreviations:
+    helloworld: "https://github.com/hackebrot/helloworld"
diff --git a/tests/test_get_config.py b/tests/test_get_config.py
index 441c173..ff5bb1f 100644
--- a/tests/test_get_config.py
+++ b/tests/test_get_config.py
@@ -8,6 +8,47 @@ from cookiecutter.exceptions import (
 )
 
 
+def test_merge_configs():
+    default = {
+        'cookiecutters_dir': '/home/example/some-path-to-templates',
+        'replay_dir': '/home/example/some-path-to-replay-files',
+        'default_context': {},
+        'abbreviations': {
+            'gh': 'https://github.com/{0}.git',
+            'gl': 'https://gitlab.com/{0}.git',
+            'bb': 'https://bitbucket.org/{0}',
+        }
+    }
+
+    user_config = {
+        'default_context': {
+            'full_name': 'Raphael Pierzina',
+            'github_username': 'hackebrot',
+        },
+        'abbreviations': {
+            'gl': 'https://gitlab.com/hackebrot/{0}.git',
+            'pytest-plugin': 'https://github.com/pytest-dev/pytest-plugin.git',
+        }
+    }
+
+    expected_config = {
+        'cookiecutters_dir': '/home/example/some-path-to-templates',
+        'replay_dir': '/home/example/some-path-to-replay-files',
+        'default_context': {
+            'full_name': 'Raphael Pierzina',
+            'github_username': 'hackebrot',
+        },
+        'abbreviations': {
+            'gh': 'https://github.com/{0}.git',
+            'gl': 'https://gitlab.com/hackebrot/{0}.git',
+            'bb': 'https://bitbucket.org/{0}',
+            'pytest-plugin': 'https://github.com/pytest-dev/pytest-plugin.git',
+        }
+    }
+
+    assert config.merge_configs(default, user_config) == expected_config
+
+
 def test_get_config():
     """
     Opening and reading config file
@@ -25,6 +66,7 @@ def test_get_config():
             'gh': 'https://github.com/{0}.git',
             'gl': 'https://gitlab.com/{0}.git',
             'bb': 'https://bitbucket.org/{0}',
+            'helloworld': 'https://github.com/hackebrot/helloworld'
         }
     }
     assert conf == expected_conf
diff --git a/tests/test_get_user_config.py b/tests/test_get_user_config.py
index 21d8886..8a541f8 100644
--- a/tests/test_get_user_config.py
+++ b/tests/test_get_user_config.py
@@ -71,6 +71,7 @@ def custom_config():
             'gh': 'https://github.com/{0}.git',
             'gl': 'https://gitlab.com/{0}.git',
             'bb': 'https://bitbucket.org/{0}',
+            'helloworld': 'https://github.com/hackebrot/helloworld',
         }
     }