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

git, cygwin and WSL


7 - 9 minutes read, 1848 words
Categories: version control systems virtualization windows
Keywords: POSIX WSL cygwin git msys powershell version control systems virtualization windows

As WSL is not a replacements for cygwin, and neither a subset nor superset, it was prevedibile 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 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 own 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 an 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.

Ho 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 script as much as possible

it is important to reduce those a 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 no need any translation when working with different systems. ../foo works out-of-the-box correctly in WSL, Cygwin, git-bash, and with 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 to handle files on Windows drives uniformly, and without calling environment specific tools like cygpath.

touch /c/user/fekir/file.txt

works in all (cygwin, MSYS2, WSL, GNU/Linux on VirtualBox, git-bash) environments, 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 to, for those programs the path needs to get translated.

$HOME directory

One of the first configurations I change is making Cygwin $HOME point to my Windows user directory.

This also avoids possible clashes, as Cygwin $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, .basrc, .ssh, .vimrc, …​). Unfortunately, some tools might need a different configuration between MSYS, the native Windows version, and Cygwin, but those cases are rare.

I did not try (yet) changing the $HOME directory of WSL.

Terminal emulator

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

mintty does not support tabs, which might be useful, but as I use tmux on my host, so I see little disadvantage in not having them.

Notice that opening git-bash 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 from git-bash, and 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" is invoked, especially if it expects paths in a different format or needs to redrawn 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 install git only once.

How to handle programs from different environments

With aliases, functions, and scripts.

#!/bin/sh
set -o errexit
set -o nounset
exec 'C:/Program Files/Vim/Vim82/gvim.exe' "$@";

This gives me the correct vim, and the native gvim in all windows environments: win32, MSYS, Cygwin, and WSL. On all environments except cmd and PowerShell, gvim takes precedence over gvim.exe. On cmd and Powershell gvim is ignored, as it does not have the "correct" file extension, and gvim.exe get’s 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 also 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 for working 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).

#!/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' "$@";

If git is already in PATH, then maybe alias git="XDG_CONFIG_HOME='C:/Users/fekir/.config' git" If you have XDG_CONFIG_HOME already defined and is giving you issues.

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.

Without bringing a lot of directories in my PATH. Notice that some programs might not work, even if I did not encounter 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, as it is generally faster to startup.

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.