Because some functions are patched twice (like `os.rename()`),
stopping the patches in the same order they were started
can result in restoration of one of the patches.
For example:
```
fn = os.rename
# Patch nesting order: 1 then 2
os.rename = patch_1(fn)
os.rename = patch_2(patch_1(fn))
# Unpatch 1 then 2: A still-patched function is restored
os.rename = fn
os.rename = patch_1(fn)
# Unpatch 2 then 1: The original function is restored
os.rename = patch_1(fn)
os.rename = fn
```
Fixes#365
This used to be the behavior, and then
b5499c7dc5 changed this, breaking some
plugins (some of which subsequently implemented workarounds). This patch
restores the behavior, so that if a Dispatcher is constructed without
explicitly passing in plugins, it has access to all plugins.
This change seems safe, in that it's unlikely that any plugins were
relying on the behavior between b5499c7dc5
and this patch, where a Dispatcher without an explicit plugin list
behaved as if there were no plugins---because such a Dispatcher is not
very useful!
This is useful for plugins like dotbot-if [1] that want to instantiate
their own Dispatcher. Previously, the Dispatcher found the set of
available plugins on its own, but as of
b5499c7dc5, this was changed so that
plugins are passed in.
Given that it has been over two years since this behavior has been
broken/changed, reverting to the previous behavior of having the
Dispatcher auto-load plugins might not be ideal, which is why this patch
instead makes the set of plugins available via the Context for plugins
to use.
This was reported in the Dotbot repository [2], and earlier in dotbot-if
[3]. dotbot-if is currently using a workaround [4] that was originally
implemented in dotbot-ifplatform [5].
[1]: https://github.com/wonderbeyond/dotbot-if
[2]: https://github.com/anishathalye/dotbot/issues/339
[3]: https://github.com/wonderbeyond/dotbot-if/issues/1
[4]: https://github.com/wonderbeyond/dotbot-if/pull/2
[5]: e35b5c0d71
See https://github.com/anishathalye/dotbot/issues/282 and
https://github.com/anishathalye/dotbot/issues/315.
This patch simplifies the implementation, removing special-case handling
for the cases of zero matches and one match. Instead, any situation
where `glob: true` is specified and the path contains a glob character
(any of "?", "*", or "[") is treated as a glob case. The reason we check
both `use_glob` and `_has_glob_chars()` is to more gracefully handle the
case where the user has enabled globs by default, but most links do not
contain glob characters and should not be treated as globs.
Note that this does NOT port the following command over:
```shell
git config --global protocol.file.allow always
```
Doing so would change the git configuration of users running
the unit tests locally, and this is not an acceptable outcome.
Instead, the git configuration is modified at the CLI using
the `-c protocol.file.allow=always` argument to accomplish
the same thing without side effects.