Search code examples
node.jsgitcommand-line-interfacechild-processspawn

Node child_process spawn hangs when calling git shortlog -sn


My Scenario

In my node application I'm using child_process.spawn to query information from the current repository

I've built a small function to return a promise which resolves with the response from the command:

const spawn = require('child_process').spawn;

const gitExec = command => (
  new Promise((resolve, reject) => {
    const thread = spawn('git', command);
    const stdOut = [];
    const stdErr = [];

    thread.stdout.on('data', (data) => {
      stdOut.push(data.toString('utf8'));
    });

    thread.stderr.on('data', (data) => {
      stdErr.push(data.toString('utf8'));
    });

    thread.on('close', () => {
      if (stdErr.length) {
        reject(stdErr.join(''));
        return;
      }
      resolve(stdOut.join());
    });
  })
);

module.exports = gitExec;

Calling git branchworks just as expected:

gitExec(['branch'])
.then((branchInfo) => {
  console.log(branchInfo);
})

(as expected) results in

    * develop
      feature/forever
      feature/sourceconfig
      feature/testing
      master

From my understanding this proves the method I'm using to actually be working.

When calling git shortlog -sn the spawned process "hangs" and does not resolve in anything

gitExec(['shortlog', '-sn'])
.then((shortlogInfo) => {
  console.log(shortlogInfo);
})

Calling git shortlog -sn via commandline i geht the expected result:

   154  Andreas Gack
    89  Some other dude
     6  Whoever else

My (so far unsuccessfull) tries

using spawnSync (while changing my gitExec function to acommodate the syncronous approach) returns an object as documented - so the process seems to actually exit - but the relevant props of the object output stdout and stderr are all empty.
The status of the object is 0 which indicates the command to execute successfully

I've read about having to redefine the maxBuffer in the spawn options, but neither setting it to a (ridiculously) high value nor a very small value does make a difference in the syncronous or asynchronous approach.

Setting the shell option to true also does not make a difference in all of the above scenarios.

The issue occurrs on my Win10x64 as well as on MacOs running node v6.9.x or 7.x

Also calling the alias git log --pretty=short does not provide a result

My Actual Question

  • Has anyone yet managed to successfully query git shortlog -sn via child_process.spawn?
  • Does anyone know a module for Node which allows querying the current local git-repository?

I somehow think the two commands git branch and git shortlog internally handle their output in a different way.

I'd be happy to create an issue on their github page, but I actually don't know how to identify the actual root-cause of that problem.

Any further input is much appreciated!


Solution

  • git shortlog thinks it needs to read something from stdin thats why the whole process hangs. To work around that, you can pass stdin from the main process in as an option and pipe everything else as usual. Then it should run.

    const spawn = require('child_process').spawn;
    
    const gitExec = command => (
      new Promise((resolve, reject) => {
        const thread = spawn('git', command, { stdio: ['inherit', 'pipe', 'pipe'] });
        const stdOut = [];
        const stdErr = [];
    
        thread.stdout.on('data', (data) => {
          stdOut.push(data.toString('utf8'));
        });
    
        thread.stderr.on('data', (data) => {
          stdErr.push(data.toString('utf8'));
        });
    
        thread.on('close', () => {
          if (stdErr.length) {
            reject(stdErr.join(''));
            return;
          }
          resolve(stdOut.join());
        });
      })
    );
    
    module.exports = gitExec;
    

    Perhaps some more context from the git documentation:

    If no revisions are passed on the command line and either standard input is not a terminal or there is no current branch, git shortlog will output a summary of the log read from standard input, without reference to the current repository.

    Which is the case when spawning a child process. So it expects something gets passed in via stdin. By setting stdin to the main process git shortlog is aware of the terminal and therefor will run.