This is a low-resolution version of the logo of Windows XP by Microsoft, protected by copyright

git, cygwin and WSL

As WSL is not a replacements for cygwin, and neither a subset nor superset, it was foreseeable that someday I would have come to the situation that I needed to work with both of them.

As I also use git as the main source control system, I’ve added git-bash to the mix, which, as the name implies, brings another shell.

Environments

Cygwin

cygwin is like a Swiss army knife. Full of useful tools that integrate pretty well with Windows, and most important a good shell (bash, zsh, …​) and a terminal emulator (mintty).

As I use those tools daily, it is hard to replace them with WSL, as it does not integrate that well with Windows. Also, the availability of Cygwin tools does not depend on the version of Windows and what Windows features are enabled. In practice, it has a higher availability, as it can be installed in more constrained environments.

git for windows

I use git often, as I do not use it only for managing source code. It is handy, for example, also for synchronizing personal notes, or as backup systems, as long as not backing up big binary blobs.

Unfortunately, the Cygwin git package is noticeably slower than git for windows. Probably git for Windows is more optimized for Windows, and thus can be faster than what the abstraction layer provided by Cygwin can provide.

But git for Windows is not an executable like svn.exe for subversion. It is an adapted MSYS2 environment. Thus it brings its bash shell, and also some inconsistencies in how paths, permissions, shell interaction, and some other things are handled.

WSL

Last, but not least, I’ve begun to use WSL. Before that, I used a virtual machine and opened from Cygwin a ssh-connection to it. It provided a relatively good integration but felt like a bloated solution.

It occurred to me that I could use WSL nearly the same way.

The primary use case is cross-compiling and testing the cross-compiled executable.

How to reduce discrepancies

So, to sum it up, I use

  • git-bash for executing git

  • WSL for cross-compiling and testing cross-compiled executables (an alternative is a virtual machine)

  • Cygwin for the shell, and many other tools

There are also other two "environments", those are cmd.exe and PowerShell, called from other tools, or invoked directly for executing native programs or PowerShell scripts.

There are many discrepancies between those environments, and to

  • avoid dumb errors

  • being able to reuse programs and scripts as much as possible

it is important to reduce those as much as possible.

For most comparisons, I left cmd and PowerShell out, as they are not (yet) my daily drivers.

Managing paths

Prefer "/" to "\"

"/" works in most environments, is easier to type (also depending on keyboard layout), and many Windows programs accept them without issues. AFAIK only cmd.exe has issues with some (not all) commands.

Also, notice that relative paths might not need any translation when working with different systems. ../foo works out-of-the-box correctly in WSL, Cygwin, git-bash, and many other Windows Programs too.

Also "\" is used as escape characters in a lot of environments, so it’s easy to get wrong and hard to read paths.

Absolute paths

I’ve decided to use the MSYS2 prefixes. They are shorter than all the prefixes used in all other environments:

  • Cygwin uses /cygdrive/X

  • VirtualBox uses /media/sf_X_Drive

  • WSL uses /mnt/x

Every system can be adapted as they all support symlinks

  • on cygwin (as administrator): ln -s /cygdrive/c /c

  • on VirtualBox: sudo ln -s /media/sf_C_DRIVE/ /c

  • on WSL: sudo ln -s /mnt/c/ /c

On WSL1 shared drives are not available by default, to add them use mount:

sudo mkdir /mnt/j
sudo mount -t drvfs J:\\ /mnt/j

These changes allow handling files on Windows drives uniformly, and without calling environment-specific tools like cygpath.

touch /c/user/fekir/file.txt

works in all Unix-like environments (Cygwin, MSYS2, WSL, GNU/Linux on VirtualBox, git-bash), and /c/user/fekir/file.txt always points to the same file. Of course, Windows programs do not know where /c/User/fekir/file.txt points, for those programs the path needs to be translated.

$HOME directory

One of the first configurations I change in my environments is making $HOME point to my Windows user directory (unless it contains spaces).

This also avoids possible clashes, as Cygwin default $HOME ignores the domain.

The biggest advantage of this approach is that it removes the need to synchronize configuration directories between MSYS, Cygwin, and other Windows tools (.config, .bashrc, .ssh, .vimrc, …​). Unfortunately, some tools might need a different configuration between MSYS, the native Windows version, and Cygwin, but those cases are rare.

I have not tried changing the $HOME directory of WSL, according to the documentation it is a bad idea.

Terminal emulator

I just use mintty from Cygwin. From there it’s possible to open a console to WSL with wslt, ssh in VirtualBox, or even open a git-bash shell.

mintty does not support tabs, which might be useful. Since I use tmux on my host, I do not have any disadvantage in not having tabs.

Notice that opening git-bash, cmd, or PowerShell might cause some issues, as the underlying process inherits, for example, all environment variables from the Cygwin environment.

Handle programs that are present in more than one environment

I have Vim installed as a "native" Windows application, the Vim version git-bash, Vim inside Cygwin, and of course Vim inside WSL.

Normally I would like to have some tools like git, vim (or at least gvim), always available in my path. This might cause issues when working from git-bash or Cygwin when the "wrong" tool is invoked, especially if it expects paths in a different format or needs to redraw the terminal.

Fortunately from bash, zsh, and other shells, compared to cmd, it is easy to personalize the environment:

path_remove() {
  PATH=${PATH//":$1:"/":"} # delete any instances in the middle
  PATH=${PATH/#"$1:"/} # delete any instance at the beginning
  PATH=${PATH/%":$1"/} # delete any instance in the at the end
}

path_remove '/c/Program Files/Neovim/bin'
path_remove '/c/Program Files/Vim/vim82'

git is another example, but I concluded that if I have git-bash available, then it’s best to remove git from Cygwin altogether because as they use different defaults for determining changes, permissions, and other properties, it changes the repositories in …​ interesting ways. To remove the error altogether it’s easier to have only one version of git available.

How to handle programs from different environments

With aliases, functions, and scripts.

gvim
#!/bin/sh
set -o errexit
set -o nounset

exec '/c/Program Files/Vim/Vim82/gvim.exe' "$@"

This gives me the native gvim in all Windows environments: win32, MSYS, Cygwin, and WSL. On all environments except cmd and PowerShell, gvim (without extension) takes precedence over gvim.exe. On cmd and Powershell gvim is ignored, as it does not have the "correct" file extension, and gvim.exe is picked.

Using '/c/Program Files/Vim/vim82/gvim.exe' instead of 'C:/Program Files/Vim/Vim82/gvim.exe' permits to use of the same file from WSL, which contrary to MSYS and Cygwin does never recognize "C:/" as a valid path. Thus the only environment left out is GNU/Linux from VirtualBox.

Of course, file paths need to be translated for gvim. It might be possible to create a more complete wrapper to handle such cases automatically, but in most cases using relative paths helps to avoid such inconsistencies.

Similarly, I have one Python environment that I use from PowerShell, Cygwin, and git-bash:

#!/bin/sh
set -o errexit
set -o nounset

'/c/Program Files/Python38/python.exe' "$@";

Thanks to conhost mintty can handle Python and other interactive programs. The alternative would be to install Python and its packages/dependencies multiple times.

WSL has Python by default and needs it to work correctly, so adding it to the PATH as python is not a wise idea. Adding python-win32 to all environments ensures I can use the same command in all environments (again, VirtualBox excluded).

Other scripts added in my path:

#!/bin/sh
set -o errexit
set -o nounset

'/c/path/to/android/toolchain/adb.exe' "$@"
#!/bin/sh
set -o errexit
set -o nounset

'/c/Program Files/Git/bin/git.exe' "$@";

As we use the same prefix or the Windows notation, those scripts can be used both in Cygwin and MSYS (no reason to use cygpath). As files without extensions are also ignored by cmd and PowerShell (which would not be able to execute them), it’s not a problem for those environments too.

This reduces the amount of logic for adding or not a program in your PATH. Notice that some programs might not work, even if I have not encountered one yet.

Using symlinks was more error-prone, as all environments use different tools for providing them, and thus were incompatible.

Opening a subshell on Windows is not as fast as on GNU/Linux systems (especially on a machine already doing a lot of other tasks like building in parallel), thus aliases are the fastest option, but have some drawbacks:

  • it is harder to update them compared to a file, as you need to resource every shell.

  • they cannot be invoked from a script

The second reason is most of the time the reason I prefer to have a ~/bin folder with a lot of little functions. Also prefer sh over bash, on a machine with a high workload it can make a big difference.

Handling multiple shells

As already mentioned, I use mintty as the main terminal emulator and one of Cygwin shells (bash or zsh) as the main shell.

Shells to a virtual machine are made through ssh:

ssht () {
  ssh -t "$@" "tmux new-session -A -s ssh_session\; set-option -g prefix2 C-a"
}

By using tmux with a different prefix, I can use multiple shells in the virtual machine without making multiple connections and benefit from other advantages provided by tmux.

Similarly, it is possible to create a connection to the WSL shell:

wslt () {
  wsl --distribution Debian --exec tmux new-session -A -s wsl_session\; set-option -g prefix2 C-a
}

The second prefix is set during connection as I generally want to use exactly the same configuration on all environments: fewer differences mean fewer conflicts during merging my configuration files.

Generally from bash/sh/dash in mintty it is possible to execute cmd and PowerShell. I do not do this that often but mintty handles it without issues.

From PowerShell, I also set

Set-PSReadLineOption -EditMode Emacs

This way it is possible, for example, to use Ctrl+D to close Powershell too.

Powershell file equivalent to .bashrc is stored in the variable $profile, so this setting can be automatically added with

if(!(Test-Path $profile)) { New-Item -path $profile -type file -force };
Add-Content $profile "Set-PSReadLineOption -EditMode Emacs;";

For cmd.exe I fear there is no possibility (without external programs that add a non-trivial overhead to the shell creation) to change any keybinding.


If you have questions, comments, or found typos, the notes are not clear, or there are some errors; then just contact me.