cksource/mgit2

View on GitHub
lib/utils/gitstatusparser.js

Summary

Maintainability
A
1 hr
Test Coverage
/**
 * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md.
 */

'use strict';

const UNMERGED_SYMBOLS = [
    'DD', // Deleted by both.
    'UU', // Modified by both.
    'AA', // Added by both.
    'DU', // Deleted by us.
    'AU', // Added by us.
    'UD', // Deleted by them.
    'UA' // Added by them.
];

const DELETE_STAGED_SYMBOL = 'D ';
const DELETE_NOT_STAGED_SYMBOL = ' D';
const MODIFIED_STAGED_SYMBOL = 'M ';
const MODIFIED_NOT_STAGED_SYMBOL = ' M';
const MODIFIED_STAGED_AND_NOT_STAGED_SYMBOL = 'MM';
const RENAMED_STAGED_SYMBOL = 'R ';
const ADDED_STAGED_SYMBOL = 'A ';
const UNTRACKED_SYMBOL = '??';

/**
 * @param {String} branchStatus An output returned by `git status --branch --porcelain` command.
 * @param {String} currentTag An output returned by `git describe --abbrev=0 --tags` command.
 * @returns {Object} data
 * @returns {Boolean} data.anythingToCommit Returns true if any changed file could be committed using command `git commit -a`.
 * @returns {String} data.branchOrTag Current branch or tag.
 * @returns {Number|null} data.behind Number of commits that branch is behind the remote upstream.
 * @returns {Number|null} data.ahead Number of commits that branch is ahead the remote upstream.
 * @returns {Array.<String>} data.added List of files created files (untracked files are tracked now).
 * @returns {Array.<String>} data.modified List of tracked files that have changed.
 * @returns {Array.<String>} data.deleted List of tracked files that have deleted.
 * @returns {Array.<String>} data.renamed List of tracked files that have moved (or renamed).
 * @returns {Array.<String>} data.unmerged List of tracked files that contain (unresolved) conflicts.
 * @returns {Array.<String>} data.untracked List of untracked files which won't be committed using command `git commit -a`.
 * @returns {Array.<String>} data.staged List of files that their changes are ready to commit.
 */
module.exports = function gitStatusParser( branchStatus, currentTag ) {
    const responseAsArray = branchStatus.split( '\n' );
    const branchData = responseAsArray.shift();

    const branch = branchData.split( '...' )[ 0 ].match( /## (.*)$/ )[ 1 ];
    const tag = currentTag;
    const detachedHead = branch === 'HEAD (no branch)';
    const added = filterFiles( [ ADDED_STAGED_SYMBOL ] );
    const modified = filterFiles( [ MODIFIED_NOT_STAGED_SYMBOL, MODIFIED_STAGED_AND_NOT_STAGED_SYMBOL, DELETE_NOT_STAGED_SYMBOL ] );
    const deleted = filterFiles( [ DELETE_STAGED_SYMBOL, DELETE_NOT_STAGED_SYMBOL ] );
    const renamed = filterFiles( [ RENAMED_STAGED_SYMBOL ] );
    const unmerged = filterFiles( UNMERGED_SYMBOLS );
    const untracked = filterFiles( [ UNTRACKED_SYMBOL ] );
    const staged = filterFiles( [
        ADDED_STAGED_SYMBOL,
        DELETE_STAGED_SYMBOL,
        MODIFIED_STAGED_SYMBOL,
        MODIFIED_STAGED_AND_NOT_STAGED_SYMBOL,
        RENAMED_STAGED_SYMBOL
    ] );

    let behind = branchData.match( /behind (\d+)/ );
    let ahead = branchData.match( /ahead (\d+)/ );

    if ( behind ) {
        behind = parseInt( behind[ 1 ], 10 );
    }

    if ( ahead ) {
        ahead = parseInt( ahead[ 1 ], 10 );
    }

    return {
        get anythingToCommit() {
            return [ added, modified, deleted, renamed, unmerged, staged ].some( collection => collection.length );
        },

        branch,
        tag,
        detachedHead,
        behind,
        ahead,
        added,
        modified,
        deleted,
        renamed,
        unmerged,
        untracked,
        staged
    };

    function filterFiles( prefixes ) {
        return responseAsArray
            .filter( line => prefixes.some( prefix => prefix === line.substring( 0, 2 ) ) )
            .map( line => line.slice( 2 ).trim() );
    }
};