We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Slightly broken links in output
`pre-commit autoupdate` outputs repository links:

```
Updating https://github.com/psf/black...already up to date.
Updating https://github.com/prettier/prettier...already up to date.
```

In iTerm2 on a Mac using Fish Shell—and probably lots of other setups as well—you can click the repository links (by holding down the _Command_ key):

<img width="668" alt="Screenshot 2020-01-01 at 15 21 32" src="https://user-images.githubusercontent.com/8469540/71642362-6fcd2800-2caa-11ea-9e00-d463dcdf9682.png">

But the link is slightly broken because there is no space after it—we're getting https://github.com/asottile/seed-isort-config...already instead of https://github.com/asottile/seed-isort-config.

This is a tiny issue, but it would be nice if we could fix it. I'll try to make a pull request to show what I mean.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `pre_commit/commands/autoupdate.py`
Content:
```
1 from __future__ import print_function
2 from __future__ import unicode_literals
3 
4 import collections
5 import os.path
6 import re
7 
8 import six
9 from aspy.yaml import ordered_dump
10 from aspy.yaml import ordered_load
11 
12 import pre_commit.constants as C
13 from pre_commit import git
14 from pre_commit import output
15 from pre_commit.clientlib import InvalidManifestError
16 from pre_commit.clientlib import load_config
17 from pre_commit.clientlib import load_manifest
18 from pre_commit.clientlib import LOCAL
19 from pre_commit.clientlib import META
20 from pre_commit.commands.migrate_config import migrate_config
21 from pre_commit.util import CalledProcessError
22 from pre_commit.util import cmd_output
23 from pre_commit.util import cmd_output_b
24 from pre_commit.util import tmpdir
25 
26 
27 class RevInfo(collections.namedtuple('RevInfo', ('repo', 'rev', 'frozen'))):
28     __slots__ = ()
29 
30     @classmethod
31     def from_config(cls, config):
32         return cls(config['repo'], config['rev'], None)
33 
34     def update(self, tags_only, freeze):
35         if tags_only:
36             tag_cmd = ('git', 'describe', 'FETCH_HEAD', '--tags', '--abbrev=0')
37         else:
38             tag_cmd = ('git', 'describe', 'FETCH_HEAD', '--tags', '--exact')
39 
40         with tmpdir() as tmp:
41             git.init_repo(tmp, self.repo)
42             cmd_output_b('git', 'fetch', 'origin', 'HEAD', '--tags', cwd=tmp)
43 
44             try:
45                 rev = cmd_output(*tag_cmd, cwd=tmp)[1].strip()
46             except CalledProcessError:
47                 cmd = ('git', 'rev-parse', 'FETCH_HEAD')
48                 rev = cmd_output(*cmd, cwd=tmp)[1].strip()
49 
50             frozen = None
51             if freeze:
52                 exact = cmd_output('git', 'rev-parse', rev, cwd=tmp)[1].strip()
53                 if exact != rev:
54                     rev, frozen = exact, rev
55         return self._replace(rev=rev, frozen=frozen)
56 
57 
58 class RepositoryCannotBeUpdatedError(RuntimeError):
59     pass
60 
61 
62 def _check_hooks_still_exist_at_rev(repo_config, info, store):
63     try:
64         path = store.clone(repo_config['repo'], info.rev)
65         manifest = load_manifest(os.path.join(path, C.MANIFEST_FILE))
66     except InvalidManifestError as e:
67         raise RepositoryCannotBeUpdatedError(six.text_type(e))
68 
69     # See if any of our hooks were deleted with the new commits
70     hooks = {hook['id'] for hook in repo_config['hooks']}
71     hooks_missing = hooks - {hook['id'] for hook in manifest}
72     if hooks_missing:
73         raise RepositoryCannotBeUpdatedError(
74             'Cannot update because the tip of master is missing these hooks:\n'
75             '{}'.format(', '.join(sorted(hooks_missing))),
76         )
77 
78 
79 REV_LINE_RE = re.compile(r'^(\s+)rev:(\s*)([^\s#]+)(.*)(\r?\n)$', re.DOTALL)
80 REV_LINE_FMT = '{}rev:{}{}{}{}'
81 
82 
83 def _original_lines(path, rev_infos, retry=False):
84     """detect `rev:` lines or reformat the file"""
85     with open(path) as f:
86         original = f.read()
87 
88     lines = original.splitlines(True)
89     idxs = [i for i, line in enumerate(lines) if REV_LINE_RE.match(line)]
90     if len(idxs) == len(rev_infos):
91         return lines, idxs
92     elif retry:
93         raise AssertionError('could not find rev lines')
94     else:
95         with open(path, 'w') as f:
96             f.write(ordered_dump(ordered_load(original), **C.YAML_DUMP_KWARGS))
97         return _original_lines(path, rev_infos, retry=True)
98 
99 
100 def _write_new_config(path, rev_infos):
101     lines, idxs = _original_lines(path, rev_infos)
102 
103     for idx, rev_info in zip(idxs, rev_infos):
104         if rev_info is None:
105             continue
106         match = REV_LINE_RE.match(lines[idx])
107         assert match is not None
108         new_rev_s = ordered_dump({'rev': rev_info.rev}, **C.YAML_DUMP_KWARGS)
109         new_rev = new_rev_s.split(':', 1)[1].strip()
110         if rev_info.frozen is not None:
111             comment = '  # {}'.format(rev_info.frozen)
112         else:
113             comment = match.group(4)
114         lines[idx] = REV_LINE_FMT.format(
115             match.group(1), match.group(2), new_rev, comment, match.group(5),
116         )
117 
118     with open(path, 'w') as f:
119         f.write(''.join(lines))
120 
121 
122 def autoupdate(config_file, store, tags_only, freeze, repos=()):
123     """Auto-update the pre-commit config to the latest versions of repos."""
124     migrate_config(config_file, quiet=True)
125     retv = 0
126     rev_infos = []
127     changed = False
128 
129     config = load_config(config_file)
130     for repo_config in config['repos']:
131         if repo_config['repo'] in {LOCAL, META}:
132             continue
133 
134         info = RevInfo.from_config(repo_config)
135         if repos and info.repo not in repos:
136             rev_infos.append(None)
137             continue
138 
139         output.write('Updating {}...'.format(info.repo))
140         new_info = info.update(tags_only=tags_only, freeze=freeze)
141         try:
142             _check_hooks_still_exist_at_rev(repo_config, new_info, store)
143         except RepositoryCannotBeUpdatedError as error:
144             output.write_line(error.args[0])
145             rev_infos.append(None)
146             retv = 1
147             continue
148 
149         if new_info.rev != info.rev:
150             changed = True
151             if new_info.frozen:
152                 updated_to = '{} (frozen)'.format(new_info.frozen)
153             else:
154                 updated_to = new_info.rev
155             msg = 'updating {} -> {}.'.format(info.rev, updated_to)
156             output.write_line(msg)
157             rev_infos.append(new_info)
158         else:
159             output.write_line('already up to date.')
160             rev_infos.append(None)
161 
162     if changed:
163         _write_new_config(config_file, rev_infos)
164 
165     return retv
166
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/pre_commit/commands/autoupdate.py b/pre_commit/commands/autoupdate.py
--- a/pre_commit/commands/autoupdate.py
+++ b/pre_commit/commands/autoupdate.py
@@ -136,7 +136,7 @@
             rev_infos.append(None)
             continue
 
-        output.write('Updating {}...'.format(info.repo))
+        output.write('Updating {} ... '.format(info.repo))
         new_info = info.update(tags_only=tags_only, freeze=freeze)
         try:
             _check_hooks_still_exist_at_rev(repo_config, new_info, store)

{"golden_diff": "diff --git a/pre_commit/commands/autoupdate.py b/pre_commit/commands/autoupdate.py\n--- a/pre_commit/commands/autoupdate.py\n+++ b/pre_commit/commands/autoupdate.py\n@@ -136,7 +136,7 @@\n             rev_infos.append(None)\n             continue\n \n-        output.write('Updating {}...'.format(info.repo))\n+        output.write('Updating {} ... '.format(info.repo))\n         new_info = info.update(tags_only=tags_only, freeze=freeze)\n         try:\n             _check_hooks_still_exist_at_rev(repo_config, new_info, store)\n", "issue": "Slightly broken links in output\n`pre-commit autoupdate` outputs repository links:\r\n\r\n```\r\nUpdating https://github.com/psf/black...already up to date.\r\nUpdating https://github.com/prettier/prettier...already up to date.\r\n```\r\n\r\nIn iTerm2 on a Mac using Fish Shell\u2014and probably lots of other setups as well\u2014you can click the repository links (by holding down the _Command_ key):\r\n\r\n<img width=\"668\" alt=\"Screenshot 2020-01-01 at 15 21 32\" src=\"https://user-images.githubusercontent.com/8469540/71642362-6fcd2800-2caa-11ea-9e00-d463dcdf9682.png\">\r\n\r\nBut the link is slightly broken because there is no space after it\u2014we're getting https://github.com/asottile/seed-isort-config...already instead of https://github.com/asottile/seed-isort-config.\r\n\r\nThis is a tiny issue, but it would be nice if we could fix it. I'll try to make a pull request to show what I mean.\n", "before_files": [{"content": "from __future__ import print_function\nfrom __future__ import unicode_literals\n\nimport collections\nimport os.path\nimport re\n\nimport six\nfrom aspy.yaml import ordered_dump\nfrom aspy.yaml import ordered_load\n\nimport pre_commit.constants as C\nfrom pre_commit import git\nfrom pre_commit import output\nfrom pre_commit.clientlib import InvalidManifestError\nfrom pre_commit.clientlib import load_config\nfrom pre_commit.clientlib import load_manifest\nfrom pre_commit.clientlib import LOCAL\nfrom pre_commit.clientlib import META\nfrom pre_commit.commands.migrate_config import migrate_config\nfrom pre_commit.util import CalledProcessError\nfrom pre_commit.util import cmd_output\nfrom pre_commit.util import cmd_output_b\nfrom pre_commit.util import tmpdir\n\n\nclass RevInfo(collections.namedtuple('RevInfo', ('repo', 'rev', 'frozen'))):\n    __slots__ = ()\n\n    @classmethod\n    def from_config(cls, config):\n        return cls(config['repo'], config['rev'], None)\n\n    def update(self, tags_only, freeze):\n        if tags_only:\n            tag_cmd = ('git', 'describe', 'FETCH_HEAD', '--tags', '--abbrev=0')\n        else:\n            tag_cmd = ('git', 'describe', 'FETCH_HEAD', '--tags', '--exact')\n\n        with tmpdir() as tmp:\n            git.init_repo(tmp, self.repo)\n            cmd_output_b('git', 'fetch', 'origin', 'HEAD', '--tags', cwd=tmp)\n\n            try:\n                rev = cmd_output(*tag_cmd, cwd=tmp)[1].strip()\n            except CalledProcessError:\n                cmd = ('git', 'rev-parse', 'FETCH_HEAD')\n                rev = cmd_output(*cmd, cwd=tmp)[1].strip()\n\n            frozen = None\n            if freeze:\n                exact = cmd_output('git', 'rev-parse', rev, cwd=tmp)[1].strip()\n                if exact != rev:\n                    rev, frozen = exact, rev\n        return self._replace(rev=rev, frozen=frozen)\n\n\nclass RepositoryCannotBeUpdatedError(RuntimeError):\n    pass\n\n\ndef _check_hooks_still_exist_at_rev(repo_config, info, store):\n    try:\n        path = store.clone(repo_config['repo'], info.rev)\n        manifest = load_manifest(os.path.join(path, C.MANIFEST_FILE))\n    except InvalidManifestError as e:\n        raise RepositoryCannotBeUpdatedError(six.text_type(e))\n\n    # See if any of our hooks were deleted with the new commits\n    hooks = {hook['id'] for hook in repo_config['hooks']}\n    hooks_missing = hooks - {hook['id'] for hook in manifest}\n    if hooks_missing:\n        raise RepositoryCannotBeUpdatedError(\n            'Cannot update because the tip of master is missing these hooks:\\n'\n            '{}'.format(', '.join(sorted(hooks_missing))),\n        )\n\n\nREV_LINE_RE = re.compile(r'^(\\s+)rev:(\\s*)([^\\s#]+)(.*)(\\r?\\n)$', re.DOTALL)\nREV_LINE_FMT = '{}rev:{}{}{}{}'\n\n\ndef _original_lines(path, rev_infos, retry=False):\n    \"\"\"detect `rev:` lines or reformat the file\"\"\"\n    with open(path) as f:\n        original = f.read()\n\n    lines = original.splitlines(True)\n    idxs = [i for i, line in enumerate(lines) if REV_LINE_RE.match(line)]\n    if len(idxs) == len(rev_infos):\n        return lines, idxs\n    elif retry:\n        raise AssertionError('could not find rev lines')\n    else:\n        with open(path, 'w') as f:\n            f.write(ordered_dump(ordered_load(original), **C.YAML_DUMP_KWARGS))\n        return _original_lines(path, rev_infos, retry=True)\n\n\ndef _write_new_config(path, rev_infos):\n    lines, idxs = _original_lines(path, rev_infos)\n\n    for idx, rev_info in zip(idxs, rev_infos):\n        if rev_info is None:\n            continue\n        match = REV_LINE_RE.match(lines[idx])\n        assert match is not None\n        new_rev_s = ordered_dump({'rev': rev_info.rev}, **C.YAML_DUMP_KWARGS)\n        new_rev = new_rev_s.split(':', 1)[1].strip()\n        if rev_info.frozen is not None:\n            comment = '  # {}'.format(rev_info.frozen)\n        else:\n            comment = match.group(4)\n        lines[idx] = REV_LINE_FMT.format(\n            match.group(1), match.group(2), new_rev, comment, match.group(5),\n        )\n\n    with open(path, 'w') as f:\n        f.write(''.join(lines))\n\n\ndef autoupdate(config_file, store, tags_only, freeze, repos=()):\n    \"\"\"Auto-update the pre-commit config to the latest versions of repos.\"\"\"\n    migrate_config(config_file, quiet=True)\n    retv = 0\n    rev_infos = []\n    changed = False\n\n    config = load_config(config_file)\n    for repo_config in config['repos']:\n        if repo_config['repo'] in {LOCAL, META}:\n            continue\n\n        info = RevInfo.from_config(repo_config)\n        if repos and info.repo not in repos:\n            rev_infos.append(None)\n            continue\n\n        output.write('Updating {}...'.format(info.repo))\n        new_info = info.update(tags_only=tags_only, freeze=freeze)\n        try:\n            _check_hooks_still_exist_at_rev(repo_config, new_info, store)\n        except RepositoryCannotBeUpdatedError as error:\n            output.write_line(error.args[0])\n            rev_infos.append(None)\n            retv = 1\n            continue\n\n        if new_info.rev != info.rev:\n            changed = True\n            if new_info.frozen:\n                updated_to = '{} (frozen)'.format(new_info.frozen)\n            else:\n                updated_to = new_info.rev\n            msg = 'updating {} -> {}.'.format(info.rev, updated_to)\n            output.write_line(msg)\n            rev_infos.append(new_info)\n        else:\n            output.write_line('already up to date.')\n            rev_infos.append(None)\n\n    if changed:\n        _write_new_config(config_file, rev_infos)\n\n    return retv\n", "path": "pre_commit/commands/autoupdate.py"}], "after_files": [{"content": "from __future__ import print_function\nfrom __future__ import unicode_literals\n\nimport collections\nimport os.path\nimport re\n\nimport six\nfrom aspy.yaml import ordered_dump\nfrom aspy.yaml import ordered_load\n\nimport pre_commit.constants as C\nfrom pre_commit import git\nfrom pre_commit import output\nfrom pre_commit.clientlib import InvalidManifestError\nfrom pre_commit.clientlib import load_config\nfrom pre_commit.clientlib import load_manifest\nfrom pre_commit.clientlib import LOCAL\nfrom pre_commit.clientlib import META\nfrom pre_commit.commands.migrate_config import migrate_config\nfrom pre_commit.util import CalledProcessError\nfrom pre_commit.util import cmd_output\nfrom pre_commit.util import cmd_output_b\nfrom pre_commit.util import tmpdir\n\n\nclass RevInfo(collections.namedtuple('RevInfo', ('repo', 'rev', 'frozen'))):\n    __slots__ = ()\n\n    @classmethod\n    def from_config(cls, config):\n        return cls(config['repo'], config['rev'], None)\n\n    def update(self, tags_only, freeze):\n        if tags_only:\n            tag_cmd = ('git', 'describe', 'FETCH_HEAD', '--tags', '--abbrev=0')\n        else:\n            tag_cmd = ('git', 'describe', 'FETCH_HEAD', '--tags', '--exact')\n\n        with tmpdir() as tmp:\n            git.init_repo(tmp, self.repo)\n            cmd_output_b('git', 'fetch', 'origin', 'HEAD', '--tags', cwd=tmp)\n\n            try:\n                rev = cmd_output(*tag_cmd, cwd=tmp)[1].strip()\n            except CalledProcessError:\n                cmd = ('git', 'rev-parse', 'FETCH_HEAD')\n                rev = cmd_output(*cmd, cwd=tmp)[1].strip()\n\n            frozen = None\n            if freeze:\n                exact = cmd_output('git', 'rev-parse', rev, cwd=tmp)[1].strip()\n                if exact != rev:\n                    rev, frozen = exact, rev\n        return self._replace(rev=rev, frozen=frozen)\n\n\nclass RepositoryCannotBeUpdatedError(RuntimeError):\n    pass\n\n\ndef _check_hooks_still_exist_at_rev(repo_config, info, store):\n    try:\n        path = store.clone(repo_config['repo'], info.rev)\n        manifest = load_manifest(os.path.join(path, C.MANIFEST_FILE))\n    except InvalidManifestError as e:\n        raise RepositoryCannotBeUpdatedError(six.text_type(e))\n\n    # See if any of our hooks were deleted with the new commits\n    hooks = {hook['id'] for hook in repo_config['hooks']}\n    hooks_missing = hooks - {hook['id'] for hook in manifest}\n    if hooks_missing:\n        raise RepositoryCannotBeUpdatedError(\n            'Cannot update because the tip of master is missing these hooks:\\n'\n            '{}'.format(', '.join(sorted(hooks_missing))),\n        )\n\n\nREV_LINE_RE = re.compile(r'^(\\s+)rev:(\\s*)([^\\s#]+)(.*)(\\r?\\n)$', re.DOTALL)\nREV_LINE_FMT = '{}rev:{}{}{}{}'\n\n\ndef _original_lines(path, rev_infos, retry=False):\n    \"\"\"detect `rev:` lines or reformat the file\"\"\"\n    with open(path) as f:\n        original = f.read()\n\n    lines = original.splitlines(True)\n    idxs = [i for i, line in enumerate(lines) if REV_LINE_RE.match(line)]\n    if len(idxs) == len(rev_infos):\n        return lines, idxs\n    elif retry:\n        raise AssertionError('could not find rev lines')\n    else:\n        with open(path, 'w') as f:\n            f.write(ordered_dump(ordered_load(original), **C.YAML_DUMP_KWARGS))\n        return _original_lines(path, rev_infos, retry=True)\n\n\ndef _write_new_config(path, rev_infos):\n    lines, idxs = _original_lines(path, rev_infos)\n\n    for idx, rev_info in zip(idxs, rev_infos):\n        if rev_info is None:\n            continue\n        match = REV_LINE_RE.match(lines[idx])\n        assert match is not None\n        new_rev_s = ordered_dump({'rev': rev_info.rev}, **C.YAML_DUMP_KWARGS)\n        new_rev = new_rev_s.split(':', 1)[1].strip()\n        if rev_info.frozen is not None:\n            comment = '  # {}'.format(rev_info.frozen)\n        else:\n            comment = match.group(4)\n        lines[idx] = REV_LINE_FMT.format(\n            match.group(1), match.group(2), new_rev, comment, match.group(5),\n        )\n\n    with open(path, 'w') as f:\n        f.write(''.join(lines))\n\n\ndef autoupdate(config_file, store, tags_only, freeze, repos=()):\n    \"\"\"Auto-update the pre-commit config to the latest versions of repos.\"\"\"\n    migrate_config(config_file, quiet=True)\n    retv = 0\n    rev_infos = []\n    changed = False\n\n    config = load_config(config_file)\n    for repo_config in config['repos']:\n        if repo_config['repo'] in {LOCAL, META}:\n            continue\n\n        info = RevInfo.from_config(repo_config)\n        if repos and info.repo not in repos:\n            rev_infos.append(None)\n            continue\n\n        output.write('Updating {} ... '.format(info.repo))\n        new_info = info.update(tags_only=tags_only, freeze=freeze)\n        try:\n            _check_hooks_still_exist_at_rev(repo_config, new_info, store)\n        except RepositoryCannotBeUpdatedError as error:\n            output.write_line(error.args[0])\n            rev_infos.append(None)\n            retv = 1\n            continue\n\n        if new_info.rev != info.rev:\n            changed = True\n            if new_info.frozen:\n                updated_to = '{} (frozen)'.format(new_info.frozen)\n            else:\n                updated_to = new_info.rev\n            msg = 'updating {} -> {}.'.format(info.rev, updated_to)\n            output.write_line(msg)\n            rev_infos.append(new_info)\n        else:\n            output.write_line('already up to date.')\n            rev_infos.append(None)\n\n    if changed:\n        _write_new_config(config_file, rev_infos)\n\n    return retv\n", "path": "pre_commit/commands/autoupdate.py"}]}