In `bash`, without the `dotglob` option, hidden files are ignored unless the glob _explicitly_ (that is with a literal leading `.`) asks for them. And `.` and `..` are not ignored (contrary to what more sensible shells like `pdksh`, `zsh` or `fish` do). With `dotglob`, only `.` and `..` are ignored unless the glob starts with `.`. With `extglob`, `bash` adds support for some of `ksh` extended glob operators. However, here with a bug/misfeature when it comes to the `!(...)` one. In both `ksh` and `bash`, `@(.*)` is one case where you explicitly request dotfiles. But in `!(.git)` you're not requesting dotfiles. ``ksh` and `zsh` in `ksh` emulation handle it correctly, but `bash` presumes you're requesting dotfiles. And that includes `.` and `..` even with `dotglob`. So `.git` will be copied as part of the copying of `.`. To work around that, you can use `!([.]git)` (here `[.]` makes the `.` not explicit) in combination with `dotglob`, or exclude `.` and `..` explicitly with `!(.git|.|..)`: $ bash -O extglob -c 'echo !(.git)' . .. foo .foo $ bash -O extglob -O dotglob -c 'echo !(.git)' . .. foo .foo $ bash -O extglob -O dotglob -c 'echo !([.]git)' foo .foo $ bash -O extglob -c 'echo !(.git|.|..)' foo .foo In the latter case, I would still add the `dotglob` option because `bash` in a future version may be fixed to stop including dotfiles here like in other shells.