I wrote a command-line tool to execute git pull
for multiple git repos using python asyncio. It works fine if all repos have ssh password-less login setup. It also
General speaking, the recommended way to feed password to git is through "credential helpers" or GIT_ASKPASS
, as pointed out by the answer of Martijn, but for Git+SSH, the situation is complicated (more discussion below). So it'd be difficult to set this up correctly across OS.
If you just want a quick patch to your script, here is the code that works in both Linux and Windows:
async def run_async(...):
...
process = await asyncio.create_subprocess_exec( *cmds,
stdin=asyncio.subprocess.PIPE,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
start_new_session=True, cwd=path)
stdout, stderr = await process.communicate(password + b'\n')
The parameter start_new_session=True
will set a new SID to the child process so that it got assigned a new session which have no controlling TTY by default.
Then SSH will be forced to read the password from the stdin
pipe.
On Windows, start_new_session
seems to have no effect (there is no concept of SID on Windows AFAIK).
Unless you plan to implement a Git-credential-manager (GCM) in your project "gita", I won't recommend to feed any password to Git at all (the unix philosophy). Simply set stdin=asyncio.subprocess.DEVNULL
and pass None
to process.communicate()
. This will force Git and SSH to use the existing CM or abort (you can handle the error later).
Moreover, I think "gita" doesn't want to mess up with the configuration of other CMs, such as GCM for windows. Thus, do not bother to touch the GIT_ASKPASS
or SSH_ASKPASS
variables, or any credential.*
configuration. It's the user's responsibility (and freedom) to setup a proper GCM for each repo. Usually the Git distribution includes a GCM or an ASKPASS implementation already.
There is a common misunderstanding to the problem: Git doesn't open the TTY for password input, SSH does! Actually, other ssh-related utilities, such as rsync
and scp
, share the same behavior (I figured this out the hard way when debugging a SELinux related problem a few months ago). See the appendix for verification.
Because Git calls SSH as a sub-process, it cannot know whether SSH will open TTY or not. The Git configurables, such as core.askpass
or GIT_ASKPASS
, will not prevent SSH from opening /dev/tty
, at least not for me when testing with Git 1.8.3 on CentOS 7 (detail in the appendix). There are two common cases that you should expect a password prompt:
~/.ssh/id_rsa
or PKCS11 chip) is password protected.In these cases, ASKPASS or GCM won't help you on the deadlock problem. You have to disable the TTY.
You may also want to read about the environment variable SSH_ASKPASS. It points to an executable that will be called when the following conditions are met:
DISPLAY
is set.On Windows, for example, it defaults to SSH_ASKPASS=/mingw64/libexec/git-core/git-gui--askpass
. This program comes with the main-stream distribution and the official Git-GUI package.
Therefore, on both Windows and Linux desktop environments, if you disable TTY by start_new_session=True
and leave the other configurables unchanged, SSH will automatically popup a separate UI window for password prompt.
To verify which process opens the TTY, you can run ps -fo pid,tty,cmd
when a Git process is waiting for password.
$ ps -fo pid,tty,cmd
3839452 pts/0 \_ git clone ssh://username@hostname/path/to/repo ./repo
3839453 pts/0 \_ ssh username@hostname git-upload-pack '/path/to/repo'
$ ls -l /proc/3839453/fd /proc/3839452/fd
/proc/3839452/fd:
total 0
lrwx------. 1 xxx xxx 64 Apr 4 21:45 0 -> /dev/pts/0
lrwx------. 1 xxx xxx 64 Apr 4 21:45 1 -> /dev/pts/0
lrwx------. 1 xxx xxx 64 Apr 4 21:43 2 -> /dev/pts/0
l-wx------. 1 xxx xxx 64 Apr 4 21:45 4 -> pipe:[49095162]
lr-x------. 1 xxx xxx 64 Apr 4 21:45 5 -> pipe:[49095163]
/proc/3839453/fd:
total 0
lr-x------. 1 xxx xxx 64 Apr 4 21:42 0 -> pipe:[49095162]
l-wx------. 1 xxx xxx 64 Apr 4 21:42 1 -> pipe:[49095163]
lrwx------. 1 xxx xxx 64 Apr 4 21:42 2 -> /dev/pts/0
lrwx------. 1 xxx xxx 64 Apr 4 21:42 3 -> socket:[49091282]
lrwx------. 1 xxx xxx 64 Apr 4 21:45 4 -> /dev/tty