Setting up Git Bash / MINGW / MSYS2 on Windows

and solving the most common issues

Posted by Pascal Landau on 2023-04-12 14:00:00

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).

Table of contents

Introduction

When I was learning git I started with the fantastic Git for Windows package, that is maintained in the git-for-windows/git Github repository and comes with Git Bash, a shell that offers a Unix-terminal like experience. It uses MINGW and MSYS2 under the hood and does not only provide git but also a bunch of other common Linux utilities like

bash
sed
awk
ls
cp
rm
...

I believe the main "shell" is actually powered by MINGW64 as that's what will be shown by default:

Git Bash / MINGW shell

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.

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/. Older releases can be found on Github in the Releases section of the git-for-windows/git repository

Follow the instructions in the How to Install Git Bash on Windows article on git-tower.com 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 "bash") so that I can open a new shell session conveniently via keyboard.

Git Bash desktop icon and shortcut

Update MINGW

To update Git for Windows, you can simply run

git update-git-for-windows

See also the Git for Windows FAQ under "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

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

How to install make

As per How to add more to Git Bash on Windows: make:

  • Go to ezwinports
  • Download file make-4.3-without-guile-w32-bin.zip (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

$ 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.

PS: There's also an alternative way that I've outlined in Install make on Windows (MinGW), though the one explained here is easier/faster.

Configuration via .bashrc

The MINGW shell is a bash shell and can thus be configured via a .bashrc file 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++. A common workflow for me to open the file is running the following commands in a MINGW shell session

# navigate to to the home directory
cd ~

# open the file explorer
explorer .

My .bashrc file usually includes the following setup:

# 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:

Common issues

The Git for Windows Known Issues page lists common problems with Git Bash and I want to provide some more context (and solutions) to the things that I have encountered.

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:

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

docker run --rm -it busybox sh

yields the following error:

$ 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:

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

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.

According to the Git for Windows Known Issues page, 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:

alias docker="winpty docker"

However, winpty seems to break piping and can lead to unexpected results like the error stdout is not a tty. See the following example:

$ docker run --rm busybox echo "foo" | cat
foo
$ 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 flag like so

$ winpty -Xallow-non-tty docker run --rm busybox echo "foo" | cat
foo

But this doesn't seem to be a catch-all solution 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.

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.

Fixing the path conversion issue for MINGW / MSYS2

First, the behavior is mentioned on the Git for Windows Known Issues page

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", 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. or SO: How to stop MinGW and MSYS from mangling path names given at the command line

Example

$ 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

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

    $ 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

    $ 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!

$ 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 via env -u MSYS_NO_PATHCONV ..., e.g.

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

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

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 stopped working with the error

$ 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:

# 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" 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.

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:

$ 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.

Remember the example I gave in section The role of winpty e.g. when using docker?

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

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

$ 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:

$ 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 and replace the one that is shipped together with Git for Windows as proposed by the maintainer of winpty.

This comment outlines the full process to replace winpty and is (slightly adapted) as follows:

# 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/

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

Related comments:

Caution After updating MinGW, the fix for winpty is "gone"!

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

Miscellaneous

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

Change the bash custom prompt to a $

Via PS1=" $":

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

See How to Change / Set up bash custom prompt (PS1) in Linux


Wanna stay in touch?

Since you ended up on this blog, chances are pretty high that you're into Software Development (probably PHP, Laravel, Docker or Google Big Query) and I'm a big fan of feedback and networking.

So - if you'd like to stay in touch, feel free to shoot me an email with a couple of words about yourself and/or connect with me on LinkedIn or Twitter or simply subscribe to my RSS feed or go the crazy route and subscribe via mail and don't forget to leave a comment :)

Subscribe to posts via mail

We use Mailchimp as our newsletter provider. By clicking subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp's privacy practices here.
Waving bear

Comments