Setting up Git Bash / MINGW / MSYS2 on Windows
Tags: mingw, windows, setup
Published: 2023-04-15 12:25:38

An installation instruction for Git Bash / MINGW / MSYS2 on Windows with some notes on solving common problems

<p>This article appeared first on <a href='https://www.pascallandau.com/'>https://www.pascallandau.com/</a> at <a href='https://www.pascallandau.com/blog/setting-up-git-bash-mingw-msys2-on-windows/'>Setting up Git Bash / MINGW / MSYS2 on Windows</a></p>
<hr />

In this article I'll document my process for **setting up Git Bash / MINGW /  MSYS2 on Windows** including some additional configuration (e.g. installing `make` and apply  some customizations via `.bashrc`).

<!-- generated -->
<a id='table-of-contents'> </a>
<!-- /generated -->

## Table of contents

<!-- toc -->

- [Introduction](#introduction)
- [How to install and update Git Bash / MINGW / MSYS2 via Git for Windows](#how-to-install-and-update-git-bash-mingw-msys2-via-git-for-windows)
    - [Update MINGW](#update-mingw)
    - [How to install `make`](#how-to-install-make)
    - [Configuration via `.bashrc`](#configuration-via-bashrc)
- [Common issues](#common-issues)
    - [The role of `winpty`: Fixing "The input device is not a TTY" ](#the-role-of-winpty-fixing-the-input-device-is-not-a-tty)
    - [The path conversion issue](#the-path-conversion-issue)
        - [Fixing the path conversion issue for MINGW / MSYS2](#fixing-the-path-conversion-issue-for-mingw-msys2)
        - [Fixing the path conversion issue for `winpty`](#fixing-the-path-conversion-issue-for-winpty)
- [Miscellaneous](#miscellaneous)
    - [Change the `bash` custom prompt to a `$`](#change-the-bash-custom-prompt-to-a)

<!-- /toc -->

<!-- generated -->
<a id='introduction'> </a>
<!-- /generated -->

## Introduction

When I was learning `git` I started with the fantastic  [Git for Windows](https://gitforwindows.org/) package, that is maintained in the  [`git-for-windows/git` Github repository](https://github.com/git-for-windows/) and comes with  [Git Bash](https://www.atlassian.com/git/tutorials/git-bash), a shell that offers a  Unix-terminal like experience. It uses  [MINGW and MSYS2 under the hood](https://github.com/git-for-windows/git/wiki/The-difference-between-MINGW-and-MSYS2) and does not only provide `git` but also a bunch of other common Linux utilities like

```text
bash
sed
awk
ls
cp
rm
...
```

I believe the main "shell" is actually powered by [MINGW64](https://www.mingw-w64.org/) as  that's what will be shown by default:

[![Git Bash / MINGW shell](https://www.pascallandau.com/img/setting-up-git-bash-mingw-msys2-on-windows/mingw.PNG)](https://www.pascallandau.com/img/setting-up-git-bash-mingw-msys2-on-windows/mingw.PNG)

Thus, I will refer to the tool as MINGW shell or Git Bash throughout this article.

I have been using MINGW for almost 10 years now, and it is still my go-to shell for Windows. I  could just never warm up to WSL, because the file sharing performance between WSL and native  Windows  files was (is?) horrible - but that's a different story.

<!-- generated -->
<a id='how-to-install-and-update-git-bash-mingw-msys2-via-git-for-windows'> </a>
<!-- /generated -->

## How to install and update Git Bash / MINGW / MSYS2 via Git for Windows

You can find the latest Git for Windows installation package directly at the homepage of [https://gitforwindows.org/](https://gitforwindows.org/). Older releases can be found on  Github in the  [Releases section of the `git-for-windows/git` repository](https://github.com/git-for-windows/git/releases)

Follow the instructions in the  [How to Install Git Bash on Windows article on git-tower.com](https://www.git-tower.com/blog/git-bash/#how-to-install-git-bash-on-windows) to get a guided tour through the setup process. 

After the installation is finished, I usually create a desktop icon and assign the shortcut  `CTRL + ALT + B` (for "**b**ash") so that I can open a new shell session conveniently via keyboard.

[![Git Bash desktop icon and shortcut](https://www.pascallandau.com/img/setting-up-git-bash-mingw-msys2-on-windows/git-bash-desktop-shortcut.PNG)](https://www.pascallandau.com/img/setting-up-git-bash-mingw-msys2-on-windows/git-bash-desktop-shortcut.PNG)

<!-- generated -->
<a id='update-mingw'> </a>
<!-- /generated -->

### Update MINGW

To update Git for Windows, you can simply run

```bash
git update-git-for-windows
```

See also the [Git for Windows FAQ under "How do I update Git for Windows upon new releases?"](https://github.com/git-for-windows/git/wiki/FAQ#how-do-i-update-git-for-windows-upon-new-releases)

> Git for Windows comes with a tool to check for updates and offer to install them. Whether or 
> not you enabled auto-updates during installation, you can manually run 
> `git update-git-for-windows`.

You can check the current version via `git version`

```text
$ git --version
git version 2.37.2.windows.2
```


<video controls>
  <source src="https://www.pascallandau.com/img/setting-up-git-bash-mingw-msys2-on-windows/update-git-bash.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>

<!-- generated -->
<a id='how-to-install-make'> </a>
<!-- /generated -->

### How to install `make`

As per [How to add more to Git Bash on Windows: `make`](https://gist.github.com/evanwill/0207876c3243bbb6863e65ec5dc3f058#make):

- Go to [ezwinports](https://sourceforge.net/projects/ezwinports/files/)
- Download file [`make-4.3-without-guile-w32-bin.zip`](https://sourceforge.net/projects/ezwinports/files/make-4.3-without-guile-w32-bin.zip/download)
  (get the version without guile)
- Extract zip
- Copy the contents to your `Git/mingw64/` directory, merging the folders, but do NOT
  overwrite/replace any existing files
  - navigate to the `Git/mingw64/` directory via

    ```
    $(cd /; explorer .)
    ```

Test via `make version`

```text
$ make --version
GNU Make 4.3.1
Built for Windows32
Copyright (C) 1988-2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
```


<video controls>
  <source src="https://www.pascallandau.com/img/setting-up-git-bash-mingw-msys2-on-windows/install-make-git-bash.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>

PS: There's also an alternative way that I've outlined in [Install `make` on Windows (MinGW)](https://www.pascallandau.com/blog/structuring-the-docker-setup-for-php-projects/#install-make-on-windows-mingw), though the one explained here is easier/faster.

<!-- generated -->
<a id='configuration-via-bashrc'> </a>
<!-- /generated -->

### Configuration via `.bashrc`

The MINGW shell is a `bash` shell and can thus be  [configured via a `.bashrc` file](https://www.digitalocean.com/community/tutorials/bashrc-file-in-linux) located at  the home directory of the user. The shell supports the `~` character as an alias for the home  directory, i.e. **you can use `~/.bashrc` to refer to the full path of the file**. This means you can  also edit it easily via `vi ~/.bashrc` - though I prefer an actual GUI editor like [Notepad++](https://notepad-plus-plus.org/). A common workflow for me to open the file is  running the following commands in a MINGW shell session

```text
# navigate to to the home directory
cd ~

# open the file explorer
explorer .
```

My `.bashrc` file usually includes the following setup:

```bash
# Get bash completion for make targets by parsing make files in the current directory at 
#  the file "Makefile"
#  all files with a ".mk" suffix in the folders ".make" and ".makefile"
# see https://stackoverflow.com/questions/4188324/bash-completion-of-makefile-target
# Notes:
#  -h hides filenames
#  -s hides error messages
complete -W "\`grep -shoE '^[a-zA-Z0-9_.-]+:([^=]|$)' Makefile .make/*.mk .makefile/*.mk | sed 's/[^a-zA-Z0-9_.-]*$//' | grep -v PHONY\`" make

# Docker login helper
# see https://www.pascallandau.com/blog/structuring-the-docker-setup-for-php-projects/#easy-container-access-via-din-bashrc-helper
function din() {
  filter=$1

  user=""
  if [[ -n "$2" ]];
  then
    user="--user $2"
  fi

  shell="bash"
  if [[ -n "$3" ]];
  then
    shell=$3
  fi

  prefix=""
  if [[ "$(expr substr $(uname -s) 1 5)" == "MINGW" ]]; then
    prefix="winpty"
  fi
  ${prefix} docker exec -it ${user} $(docker ps --filter name=${filter} -q | head -1) ${shell}
}
``` Links:

- [SO: bash completion of makefile target](https://stackoverflow.com/questions/4188324/bash-completion-of-makefile-target)
- [Easy container access via din .bashrc helper](https://www.pascallandau.com/blog/structuring-the-docker-setup-for-php-projects/#easy-container-access-via-din-bashrc-helper)

<!-- generated -->
<a id='common-issues'> </a>
<!-- /generated -->

## Common issues

The [Git for Windows Known Issues page](https://github.com/git-for-windows/build-extra/blob/main/ReleaseNotes.md#known-issues) lists common problems with Git Bash and I want to provide some more context (and solutions)  to the things that I have encountered.

<!-- generated -->
<a id='the-role-of-winpty-fixing-the-input-device-is-not-a-tty'> </a>
<!-- /generated -->

### The role of `winpty`: Fixing "The input device is not a TTY" 

I encountered the `The input device is not a TTY` error while using `docker`. To log into a  running `docker` container or starting a container with a login session, the [`-i` (Keep STDIN open even if not attached) and `-t` (Allocate a pseudo-tty) options must be given:](https://docs.docker.com/engine/reference/run/#foreground)

> For interactive processes (like a shell), you must use `-i` `-t` together in order to allocate a
> tty for the container process. `-i` `-t`  is often written `-it`.

But attempting to do so via

```bash
docker run --rm -it busybox sh
```

yields the following error:

```text
$ docker run --rm -it busybox sh
the input device is not a TTY.  If you are using mintty, try prefixing the command with 'winpty'
```

Fortunately, the fix is included in the message: Prefix the command with `winpty`. Doing so works as expected:

```text
$ winpty docker run --rm -it busybox sh
/ #
```

[`winpty`](https://github.com/rprichard/winpty) is according to it's readme

> [...] a Windows software package providing an interface similar to a Unix pty-master for
> communicating with Windows console programs. The package consists of a library (libwinpty) and
> a tool for Cygwin and MSYS for running Windows console programs in a Cygwin/MSYS pty.

So kind of a translator between your "Windows input" and the "command input" to create input  that is  [compatible with a Unix pty (pty=pseudoterminal interface), e.g. for `docker`](https://iximiuz.com/en/posts/linux-pty-what-powers-docker-attach-functionality/).

According to the [Git for Windows Known Issues page](https://github.com/git-for-windows/build-extra/blob/main/ReleaseNotes.md#known-issues), there are a number of other cases where `winpty` is required (though I personally didn't encounter them yet):

> Some console programs, most notably non-MSYS2 Python, PHP, Node and OpenSSL, interact
> correctly with MinTTY only when called through `winpty` (e.g. the Python console needs to be
> started as `winpty python` instead of just `python`).

**CAUTION**: I've seen people put an alias in their `.bashrc` file to _always_ prefix `docker`  commands automatically with `winpty` like so:

```text
alias docker="winpty docker"
```

However, **[`winpty` seems to break piping](https://superuser.com/q/1011597/434918) and can lead to unexpected results** like the error `stdout is not a tty`. See the following  example:

```text
$ docker run --rm busybox echo "foo" | cat
foo
```

```text
$ winpty docker run --rm busybox echo "foo" | cat
stdout is not a tty
```

You might work around this by adding the [(undocumented) `-Xallow-non-tty`](https://github.com/rprichard/winpty/issues/103) flag like so ```text
$ winpty -Xallow-non-tty docker run --rm busybox echo "foo" | cat
foo
```

But this [doesn't seem to be a catch-all solution](https://github.com/rprichard/winpty/issues/103#issuecomment-285987050) and I would recommend against using it as a default - or if you do, only use it when the `-it`  flag is used [as proposed in this answer](https://stackoverflow.com/a/61580520/413531).

<!-- generated -->
<a id='the-path-conversion-issue'> </a>
<!-- /generated -->

### The path conversion issue

Ah. This one has given me _lots_ of headaches over the years. MINGW, MSYS2 and `winpty` use  automatic conversion of Unix paths to Windows paths, e.g. `/foo` gets translated to something like `C:/Program Files/Git/foo` where `C:/Program Files/Git/` is the installation directory of the  Git for Windows installation.

<!-- generated -->
<a id='fixing-the-path-conversion-issue-for-mingw-msys2'> </a>
<!-- /generated -->

#### Fixing the path conversion issue for MINGW / MSYS2

First, the behavior is mentioned on the [Git for Windows Known Issues page](https://github.com/git-for-windows/build-extra/blob/main/ReleaseNotes.md#known-issues)

> If you specify command-line options starting with a slash, POSIX-to-Windows path conversion 
> will kick in converting e.g. "`/usr/bin/bash.exe`" to "`C:\Program Files\Git\usr\bin\bash.exe`". 
> When that is not desired -- e.g. "`--upload-pack=/opt/git/bin/git-upload-pack`" or "`-L/regex/`" 
> -- you need to set the environment variable `MSYS_NO_PATHCONV` temporarily, like so:
>
> `MSYS_NO_PATHCONV=1 git blame -L/pathconv/ msys2_path_conv.cc`
> 
> Alternatively, you can double the first slash to avoid POSIX-to-Windows path conversion, e.g. 
> "`//usr/bin/bash.exe`".

and also documented for [MINGW at "Posix path conversion"](https://web.archive.org/web/20201112005258/http://www.mingw.org/wiki/Posix_path_conversion), but it's still brought up regularly, see e.g. [GH #3619: "/" is replaced with the directory path of Git installation when using MinGW64 Bash](https://github.com/git-for-windows/git/issues/3619). or [SO: How to stop MinGW and MSYS from mangling path names given at the command line](https://stackoverflow.com/a/34386471)

**Example**

```bash
$ docker run --rm busybox ls /foo
ls: C:/Program Files/Git/foo: No such file or directory
```

As quoted above, it can be solved by either

- adding an **additional `/`** to the path

  ```text
  $ docker run --rm busybox ls //foo
  ls: /foo: No such file or directory
  ```
- **prefixing** the command with `MSYS_NO_PATHCONV=1`  

  ```text
  $ MSYS_NO_PATHCONV=1 docker run --rm busybox ls /foo
  ls: /foo: No such file or directory
  ```
- or exporting the `MSYS_NO_PATHCONV=1` variable as an **environment variable** to disable the 
  behavior completely

  ```text
  $ export MSYS_NO_PATHCONV=1
  $ docker run --rm busybox ls /foo
  ls: /foo: No such file or directory
  ```
  
**CAUTION:** The value of the `MSYS_NO_PATHCONV` variable does not matter - we can also set it  to `0`, `false` or an empty string. It only matters that the variable is defined!

```text
$ MSYS_NO_PATHCONV=0 docker run --rm busybox ls /foo
ls: /foo: No such file or directory
```

This is particularly important when using the **environment variable** approach. In order to  selectively enable the path conversion again, you must  [unset the `MSYS_NO_PATHCONV` first](https://stackoverflow.com/a/41749660) via  `env -u MSYS_NO_PATHCONV ...`, e.g.

```text
$ env -u MSYS_NO_PATHCONV docker run --rm busybox ls /foo
ls: C:/Program Files/Git/foo: No such file or directory
```

<video controls>
  <source src="https://www.pascallandau.com/img/setting-up-git-bash-mingw-msys2-on-windows/mingw-git-bash-posix-path-conversion.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>

**CAUTION**: I've seen people adding `MSYS_NO_PATHCONV=1` permanently to their environment in their `.bashrc` file to _always_ disable path conversion via

```bash
export MSYS_NO_PATHCONV=1
```

However, this can have some unintended side effects. When I tried it out, [my local installation of the `gcloud` cli](https://www.pascallandau.com/blog/gcp-compute-instance-vm-docker/#set-up-the-gcloud-cli-tool) stopped working with the error

```text
$ MSYS_NO_PATHCONV=1 gcloud version
C:\Users\Pascal\AppData\Local\Programs\Python\Python39\python.exe: can't open file 'C:\c\Users\Pascal\AppData\Local\Google\Cloud SDK\google-cloud-sdk\lib\gcloud.py': [Errno 2] No such file or directory
```

So instead I recommend setting `MSYS_NO_PATHCONV=1` either selectively per command or scope it to the use case. I do this for example in my Makefiles by only exporting it for the scope of `make` (and all scripts `make` invokes) by putting the following code in the beginning of the  Makefile:

```makefile
# OS is a defined variable for WIN systems, so "uname" will not be executed
OS?=$(shell uname)
# Values of OS:
#   Windows => Windows_NT 
#   Mac     => Darwin 
#   Linux   => Linux 
ifeq ($(OS),Windows_NT)
	export MSYS_NO_PATHCONV=1
endif
```

The path conversion is also documented for [MSYS2 at "Filesystem Paths: Automatic Unix ⟶ Windows Path Conversion"](https://www.msys2.org/docs/filesystem-paths/#automatic-unix-windows-path-conversion) and can be disabled via the `MSYS2_ARG_CONV_EXCL` environment variable:

> [...] For these cases you can exclude certain arguments via the `MSYS2_ARG_CONV_EXCL` environment 
> variable:
> [...]
> `MSYS2_ARG_CONV_EXCL` can either be * to mean exclude everything, or a list of one ore more 
> arguments prefixes separated by ;, like `MSYS2_ARG_CONV_EXCL=--dir=;--bla=;/test`. It matches 
> the prefix against the whole argument string.

I.e. setting the variable as `MSYS2_ARG_CONV_EXCL="*"` should disable the path conversion  completely. I myself have never had to use this, though. Using `MSYS_NO_PATHCONV` was always  sufficient.

<!-- generated -->
<a id='fixing-the-path-conversion-issue-for-winpty'> </a>
<!-- /generated -->

#### Fixing the path conversion issue for `winpty`

Unfortunately, `winpty` suffers from this path conversion issue as well. In the standard  installation of Git for Windows we can even see this by simply using `echo`:

```text
$ winpty echo /
C:/Program Files/Git
```

The behavior is known and flagged as a bug e.g. in [GH issue #411: Path conversion with and without winpty differs](https://github.com/msys2/MSYS2-packages/issues/411).

Remember the example I gave in section [The role of `winpty` e.g. when using `docker`](#the-role-of-winpty-fixing-the-input-device-is-not-a-tty)?

```text
$ winpty docker run --rm -it busybox sh
/ #
```

Now let's extend this and throw a volume into the mix:

```text
$ winpty docker run --rm -v foo:/foo -it busybox sh
docker: Error response from daemon: create foo;C: "foo;C" includes invalid characters for a local volume name, only "[a-zA-Z0-9][a-zA-Z0-9_.-]" are allowed. If you intended to pass a host directory, use absolute path.
```

`winpty` converts `/foo` to `C:/Program Files/Git/foo` so that the volume definition becomes `-v foo:C:/Program Files/Git/foo` - which is of course invalid.

Using an additional `/` as a prefix does work here as well:

```text
$ winpty docker run --rm -v foo://foo -it busybox sh
/ #
```

But **there is no environment variable that we could use**. The only way to "fix" the path  conversion is using a  [newer release of `winpty`](https://github.com/rprichard/winpty/releases) and replace the one  that is shipped together with Git for Windows  [as proposed by the maintainer of `winpty`](https://github.com/msys2/MSYS2-packages/issues/411#issuecomment-372585320).

This [comment outlines the full process](https://github.com/rprichard/winpty/issues/127#issuecomment-817356202) to replace `winpty` and is (slightly adapted) as follows:

```bash
# create temporary directory
mkdir temp
cd temp
# download a newer release
curl -L https://github.com/rprichard/winpty/releases/download/0.4.3/winpty-0.4.3-msys2-2.7.0-x64.tar.gz --output winpty.tar.gz
# extract the archive
tar -xvf winpty.tar.gz
# copy the content of the bin/ folder to `/usr/bin` 
# (which resolves to e.g `C:/Program Files/Git/usr/bin`; replaces any existing files)
cp winpty-0.4.3-msys2-2.7.0-x64/bin/* /usr/bin
# delete the temporary directory
cd ..
rm -rf temp/
```

<video controls>
  <source src="https://www.pascallandau.com/img/setting-up-git-bash-mingw-msys2-on-windows/winpty-update-posix-path-conversion.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>

Once the new version is installed, the path conversion does not happen any longer (even without  specifying any environment variables).

Related comments:

- [https://github.com/docker/for-win/issues/1588#issuecomment-594938988](https://github.com/docker/for-win/issues/1588#issuecomment-594938988)
- [https://github.com/docker/for-win/issues/1588#issuecomment-698080757](https://github.com/docker/for-win/issues/1588#issuecomment-698080757)

**Caution**
After [updating MinGW](#update-mingw), the fix for `winpty` is "gone"! 

<video controls>
  <source src="https://www.pascallandau.com/img/setting-up-git-bash-mingw-msys2-on-windows/winpty-path-fix-gone-after-update.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>

I.e. you need to re-run the steps above every time you run an update.

<!-- generated -->
<a id='miscellaneous'> </a>
<!-- /generated -->

## Miscellaneous

Some stuff that I need from time to time - not necessarily only relevant for Git Bash.

<!-- generated -->
<a id='change-the-bash-custom-prompt-to-a'> </a>
<!-- /generated -->

### Change the `bash` custom prompt to a `$`

Via `PS1=" $"`:

```text
Pascal@LAPTOP-0DNL2Q02 MINGW64 ~
$ PS1="$ "
$
```

See [How to Change / Set up bash custom prompt (PS1) in Linux](https://www.cyberciti.biz/tips/howto-linux-unix-bash-shell-setup-prompt.html)