HEX
Server: Apache/2.4.46 (Win64) OpenSSL/1.1.1j PHP/8.4.25
System: Windows NT DESKTOP-4TAV2RJ 10.0 build 19045 (Windows 10) AMD64
User: fred (0)
PHP: 8.4.25
Disabled: NONE
Upload Files
File: C:/Users/fred/.vscode/extensions/mhutchie.git-graph-1.30.0/out/dataSource.js
"use strict";
function requireWithFallback(electronModule, nodeModule) { try { return require(electronModule); } catch (err) {} return require(nodeModule); }
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DataSource = exports.GIT_CONFIG = void 0;
const cp = require("child_process");
const fs = requireWithFallback("original-fs", "fs");
const iconv_lite_1 = require("iconv-lite");
const path = require("path");
const vscode = require("vscode");
const askpassManager_1 = require("./askpass/askpassManager");
const config_1 = require("./config");
const utils_1 = require("./utils");
const disposable_1 = require("./utils/disposable");
const DRIVE_LETTER_PATH_REGEX = /^[a-z]:\//;
const EOL_REGEX = /\r\n|\r|\n/g;
const INVALID_BRANCH_REGEXP = /^\(.* .*\)$/;
const REMOTE_HEAD_BRANCH_REGEXP = /^remotes\/.*\/HEAD$/;
const GIT_LOG_SEPARATOR = 'XX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPb';
exports.GIT_CONFIG = {
    DIFF: {
        GUI_TOOL: 'diff.guitool',
        TOOL: 'diff.tool'
    },
    REMOTE: {
        PUSH_DEFAULT: 'remote.pushdefault'
    },
    USER: {
        EMAIL: 'user.email',
        NAME: 'user.name'
    }
};
class DataSource extends disposable_1.Disposable {
    constructor(gitExecutable, onDidChangeConfiguration, onDidChangeGitExecutable, logger) {
        super();
        this.logger = logger;
        this.setGitExecutable(gitExecutable);
        const askpassManager = new askpassManager_1.AskpassManager();
        this.askpassEnv = askpassManager.getEnv();
        this.registerDisposables(onDidChangeConfiguration((event) => {
            if (event.affectsConfiguration('git-graph.date.type') || event.affectsConfiguration('git-graph.dateType') ||
                event.affectsConfiguration('git-graph.repository.commits.showSignatureStatus') || event.affectsConfiguration('git-graph.showSignatureStatus') ||
                event.affectsConfiguration('git-graph.repository.useMailmap') || event.affectsConfiguration('git-graph.useMailmap')) {
                this.generateGitCommandFormats();
            }
        }), onDidChangeGitExecutable((gitExecutable) => {
            this.setGitExecutable(gitExecutable);
        }), askpassManager);
    }
    isGitExecutableUnknown() {
        return this.gitExecutable === null;
    }
    setGitExecutable(gitExecutable) {
        this.gitExecutable = gitExecutable;
        this.gitExecutableSupportsGpgInfo = gitExecutable !== null ? utils_1.doesVersionMeetRequirement(gitExecutable.version, '2.4.0') : false;
        this.generateGitCommandFormats();
    }
    generateGitCommandFormats() {
        const config = config_1.getConfig();
        const dateType = config.dateType === 0 ? '%at' : '%ct';
        const useMailmap = config.useMailmap;
        this.gitFormatCommitDetails = [
            '%H', '%P',
            useMailmap ? '%aN' : '%an', useMailmap ? '%aE' : '%ae', '%at', useMailmap ? '%cN' : '%cn', useMailmap ? '%cE' : '%ce', '%ct',
            ...(config.showSignatureStatus && this.gitExecutableSupportsGpgInfo ? ['%G?', '%GS', '%GK'] : ['', '', '']),
            '%B'
        ].join(GIT_LOG_SEPARATOR);
        this.gitFormatLog = [
            '%H', '%P',
            useMailmap ? '%aN' : '%an', useMailmap ? '%aE' : '%ae', dateType,
            '%s'
        ].join(GIT_LOG_SEPARATOR);
        this.gitFormatStash = [
            '%H', '%P', '%gD',
            useMailmap ? '%aN' : '%an', useMailmap ? '%aE' : '%ae', dateType,
            '%s'
        ].join(GIT_LOG_SEPARATOR);
    }
    getRepoInfo(repo, showRemoteBranches, showStashes, hideRemotes) {
        return Promise.all([
            this.getBranches(repo, showRemoteBranches, hideRemotes),
            this.getRemotes(repo),
            showStashes ? this.getStashes(repo) : Promise.resolve([])
        ]).then((results) => {
            return { branches: results[0].branches, head: results[0].head, remotes: results[1], stashes: results[2], error: null };
        }).catch((errorMessage) => {
            return { branches: [], head: null, remotes: [], stashes: [], error: errorMessage };
        });
    }
    getCommits(repo, branches, maxCommits, showTags, showRemoteBranches, includeCommitsMentionedByReflogs, onlyFollowFirstParent, commitOrdering, remotes, hideRemotes, stashes) {
        const config = config_1.getConfig();
        return Promise.all([
            this.getLog(repo, branches, maxCommits + 1, showTags && config.showCommitsOnlyReferencedByTags, showRemoteBranches, includeCommitsMentionedByReflogs, onlyFollowFirstParent, commitOrdering, remotes, hideRemotes, stashes),
            this.getRefs(repo, showRemoteBranches, config.showRemoteHeads, hideRemotes).then((refData) => refData, (errorMessage) => errorMessage)
        ]).then((results) => __awaiter(this, void 0, void 0, function* () {
            let commits = results[0], refData = results[1], i;
            let moreCommitsAvailable = commits.length === maxCommits + 1;
            if (moreCommitsAvailable)
                commits.pop();
            if (typeof refData === 'string') {
                if (commits.length > 0) {
                    throw refData;
                }
                else {
                    refData = { head: null, heads: [], tags: [], remotes: [] };
                }
            }
            if (refData.head !== null && config.showUncommittedChanges) {
                for (i = 0; i < commits.length; i++) {
                    if (refData.head === commits[i].hash) {
                        const numUncommittedChanges = yield this.getUncommittedChanges(repo);
                        if (numUncommittedChanges > 0) {
                            commits.unshift({ hash: utils_1.UNCOMMITTED, parents: [refData.head], author: '*', email: '', date: Math.round((new Date()).getTime() / 1000), message: 'Uncommitted Changes (' + numUncommittedChanges + ')' });
                        }
                        break;
                    }
                }
            }
            let commitNodes = [];
            let commitLookup = {};
            for (i = 0; i < commits.length; i++) {
                commitLookup[commits[i].hash] = i;
                commitNodes.push(Object.assign(Object.assign({}, commits[i]), { heads: [], tags: [], remotes: [], stash: null }));
            }
            let toAdd = [];
            for (i = 0; i < stashes.length; i++) {
                if (typeof commitLookup[stashes[i].hash] === 'number') {
                    commitNodes[commitLookup[stashes[i].hash]].stash = {
                        selector: stashes[i].selector,
                        baseHash: stashes[i].baseHash,
                        untrackedFilesHash: stashes[i].untrackedFilesHash
                    };
                }
                else if (typeof commitLookup[stashes[i].baseHash] === 'number') {
                    toAdd.push({ index: commitLookup[stashes[i].baseHash], data: stashes[i] });
                }
            }
            toAdd.sort((a, b) => a.index !== b.index ? a.index - b.index : b.data.date - a.data.date);
            for (i = toAdd.length - 1; i >= 0; i--) {
                let stash = toAdd[i].data;
                commitNodes.splice(toAdd[i].index, 0, {
                    hash: stash.hash,
                    parents: [stash.baseHash],
                    author: stash.author,
                    email: stash.email,
                    date: stash.date,
                    message: stash.message,
                    heads: [], tags: [], remotes: [],
                    stash: {
                        selector: stash.selector,
                        baseHash: stash.baseHash,
                        untrackedFilesHash: stash.untrackedFilesHash
                    }
                });
            }
            for (i = 0; i < commitNodes.length; i++) {
                commitLookup[commitNodes[i].hash] = i;
            }
            for (i = 0; i < refData.heads.length; i++) {
                if (typeof commitLookup[refData.heads[i].hash] === 'number')
                    commitNodes[commitLookup[refData.heads[i].hash]].heads.push(refData.heads[i].name);
            }
            if (showTags) {
                for (i = 0; i < refData.tags.length; i++) {
                    if (typeof commitLookup[refData.tags[i].hash] === 'number')
                        commitNodes[commitLookup[refData.tags[i].hash]].tags.push({ name: refData.tags[i].name, annotated: refData.tags[i].annotated });
                }
            }
            for (i = 0; i < refData.remotes.length; i++) {
                if (typeof commitLookup[refData.remotes[i].hash] === 'number') {
                    let name = refData.remotes[i].name;
                    let remote = remotes.find(remote => name.startsWith(remote + '/'));
                    commitNodes[commitLookup[refData.remotes[i].hash]].remotes.push({ name: name, remote: remote ? remote : null });
                }
            }
            return {
                commits: commitNodes,
                head: refData.head,
                tags: unique(refData.tags.map((tag) => tag.name)),
                moreCommitsAvailable: moreCommitsAvailable,
                error: null
            };
        })).catch((errorMessage) => {
            return { commits: [], head: null, tags: [], moreCommitsAvailable: false, error: errorMessage };
        });
    }
    getConfig(repo, remotes) {
        return Promise.all([
            this.getConfigList(repo),
            this.getConfigList(repo, "local"),
            this.getConfigList(repo, "global")
        ]).then((results) => {
            const consolidatedConfigs = results[0], localConfigs = results[1], globalConfigs = results[2];
            const branches = {};
            Object.keys(localConfigs).forEach((key) => {
                if (key.startsWith('branch.')) {
                    if (key.endsWith('.remote')) {
                        const branchName = key.substring(7, key.length - 7);
                        branches[branchName] = {
                            pushRemote: typeof branches[branchName] !== 'undefined' ? branches[branchName].pushRemote : null,
                            remote: localConfigs[key]
                        };
                    }
                    else if (key.endsWith('.pushremote')) {
                        const branchName = key.substring(7, key.length - 11);
                        branches[branchName] = {
                            pushRemote: localConfigs[key],
                            remote: typeof branches[branchName] !== 'undefined' ? branches[branchName].remote : null
                        };
                    }
                }
            });
            return {
                config: {
                    branches: branches,
                    diffTool: getConfigValue(consolidatedConfigs, exports.GIT_CONFIG.DIFF.TOOL),
                    guiDiffTool: getConfigValue(consolidatedConfigs, exports.GIT_CONFIG.DIFF.GUI_TOOL),
                    pushDefault: getConfigValue(consolidatedConfigs, exports.GIT_CONFIG.REMOTE.PUSH_DEFAULT),
                    remotes: remotes.map((remote) => ({
                        name: remote,
                        url: getConfigValue(localConfigs, 'remote.' + remote + '.url'),
                        pushUrl: getConfigValue(localConfigs, 'remote.' + remote + '.pushurl')
                    })),
                    user: {
                        name: {
                            local: getConfigValue(localConfigs, exports.GIT_CONFIG.USER.NAME),
                            global: getConfigValue(globalConfigs, exports.GIT_CONFIG.USER.NAME)
                        },
                        email: {
                            local: getConfigValue(localConfigs, exports.GIT_CONFIG.USER.EMAIL),
                            global: getConfigValue(globalConfigs, exports.GIT_CONFIG.USER.EMAIL)
                        }
                    }
                },
                error: null
            };
        }).catch((errorMessage) => {
            return { config: null, error: errorMessage };
        });
    }
    getCommitDetails(repo, commitHash, hasParents) {
        const fromCommit = commitHash + (hasParents ? '^' : '');
        return Promise.all([
            this.getCommitDetailsBase(repo, commitHash),
            this.getDiffNameStatus(repo, fromCommit, commitHash),
            this.getDiffNumStat(repo, fromCommit, commitHash)
        ]).then((results) => {
            results[0].fileChanges = generateFileChanges(results[1], results[2], null);
            return { commitDetails: results[0], error: null };
        }).catch((errorMessage) => {
            return { commitDetails: null, error: errorMessage };
        });
    }
    getStashDetails(repo, commitHash, stash) {
        return Promise.all([
            this.getCommitDetailsBase(repo, commitHash),
            this.getDiffNameStatus(repo, stash.baseHash, commitHash),
            this.getDiffNumStat(repo, stash.baseHash, commitHash),
            stash.untrackedFilesHash !== null ? this.getDiffNameStatus(repo, stash.untrackedFilesHash, stash.untrackedFilesHash) : Promise.resolve([]),
            stash.untrackedFilesHash !== null ? this.getDiffNumStat(repo, stash.untrackedFilesHash, stash.untrackedFilesHash) : Promise.resolve([])
        ]).then((results) => {
            results[0].fileChanges = generateFileChanges(results[1], results[2], null);
            if (stash.untrackedFilesHash !== null) {
                generateFileChanges(results[3], results[4], null).forEach((fileChange) => {
                    if (fileChange.type === "A") {
                        fileChange.type = "U";
                        results[0].fileChanges.push(fileChange);
                    }
                });
            }
            return { commitDetails: results[0], error: null };
        }).catch((errorMessage) => {
            return { commitDetails: null, error: errorMessage };
        });
    }
    getUncommittedDetails(repo) {
        return Promise.all([
            this.getDiffNameStatus(repo, 'HEAD', ''),
            this.getDiffNumStat(repo, 'HEAD', ''),
            this.getStatus(repo)
        ]).then((results) => {
            return {
                commitDetails: {
                    hash: utils_1.UNCOMMITTED, parents: [],
                    author: '', authorEmail: '', authorDate: 0,
                    committer: '', committerEmail: '', committerDate: 0, signature: null,
                    body: '', fileChanges: generateFileChanges(results[0], results[1], results[2])
                },
                error: null
            };
        }).catch((errorMessage) => {
            return { commitDetails: null, error: errorMessage };
        });
    }
    getCommitComparison(repo, fromHash, toHash) {
        return Promise.all([
            this.getDiffNameStatus(repo, fromHash, toHash === utils_1.UNCOMMITTED ? '' : toHash),
            this.getDiffNumStat(repo, fromHash, toHash === utils_1.UNCOMMITTED ? '' : toHash),
            toHash === utils_1.UNCOMMITTED ? this.getStatus(repo) : Promise.resolve(null)
        ]).then((results) => {
            return {
                fileChanges: generateFileChanges(results[0], results[1], results[2]),
                error: null
            };
        }).catch((errorMessage) => {
            return { fileChanges: [], error: errorMessage };
        });
    }
    getCommitFile(repo, commitHash, filePath) {
        return this._spawnGit(['show', commitHash + ':' + filePath], repo, stdout => {
            const encoding = config_1.getConfig(repo).fileEncoding;
            return iconv_lite_1.decode(stdout, iconv_lite_1.encodingExists(encoding) ? encoding : 'utf8');
        });
    }
    getCommitSubject(repo, commitHash) {
        return this.spawnGit(['-c', 'log.showSignature=false', 'log', '--format=%s', '-n', '1', commitHash, '--'], repo, (stdout) => {
            return stdout.trim().replace(/\s+/g, ' ');
        }).then((subject) => subject, () => null);
    }
    getRemoteUrl(repo, remote) {
        return this.spawnGit(['config', '--get', 'remote.' + remote + '.url'], repo, (stdout) => {
            return stdout.split(EOL_REGEX)[0];
        }).then((url) => url, () => null);
    }
    getNewPathOfRenamedFile(repo, commitHash, oldFilePath) {
        return this.getDiffNameStatus(repo, commitHash, '', 'R').then((renamed) => {
            const renamedRecordForFile = renamed.find((record) => record.oldFilePath === oldFilePath);
            return renamedRecordForFile ? renamedRecordForFile.newFilePath : null;
        }).catch(() => null);
    }
    getTagDetails(repo, tagName) {
        return this.spawnGit(['for-each-ref', 'refs/tags/' + tagName, '--format=' + ['%(objectname)', '%(taggername)', '%(taggeremail)', '%(taggerdate:unix)', '%(contents)'].join(GIT_LOG_SEPARATOR)], repo, (stdout) => {
            let data = stdout.split(GIT_LOG_SEPARATOR);
            return {
                tagHash: data[0],
                name: data[1],
                email: data[2].substring(data[2].startsWith('<') ? 1 : 0, data[2].length - (data[2].endsWith('>') ? 1 : 0)),
                date: parseInt(data[3]),
                message: removeTrailingBlankLines(data[4].split(EOL_REGEX)).join('\n'),
                error: null
            };
        }).then((data) => {
            return data;
        }).catch((errorMessage) => {
            return { tagHash: '', name: '', email: '', date: 0, message: '', error: errorMessage };
        });
    }
    getSubmodules(repo) {
        return new Promise(resolve => {
            fs.readFile(path.join(repo, '.gitmodules'), { encoding: 'utf8' }, (err, data) => __awaiter(this, void 0, void 0, function* () {
                let submodules = [];
                if (!err) {
                    let lines = data.split(EOL_REGEX), inSubmoduleSection = false, match;
                    const section = /^\s*\[.*\]\s*$/, submodule = /^\s*\[submodule "([^"]+)"\]\s*$/, pathProp = /^\s*path\s+=\s+(.*)$/;
                    for (let i = 0; i < lines.length; i++) {
                        if (lines[i].match(section) !== null) {
                            inSubmoduleSection = lines[i].match(submodule) !== null;
                            continue;
                        }
                        if (inSubmoduleSection && (match = lines[i].match(pathProp)) !== null) {
                            let root = yield this.repoRoot(utils_1.getPathFromUri(vscode.Uri.file(path.join(repo, utils_1.getPathFromStr(match[1])))));
                            if (root !== null && !submodules.includes(root)) {
                                submodules.push(root);
                            }
                        }
                    }
                }
                resolve(submodules);
            }));
        });
    }
    areStagedChanges(repo) {
        return this.spawnGit(['diff-index', 'HEAD'], repo, (stdout) => stdout !== '').then(changes => changes, () => false);
    }
    repoRoot(pathOfPotentialRepo) {
        return this.spawnGit(['rev-parse', '--show-toplevel'], pathOfPotentialRepo, (stdout) => utils_1.getPathFromUri(vscode.Uri.file(path.normalize(stdout.trim())))).then((pathReturnedByGit) => __awaiter(this, void 0, void 0, function* () {
            if (process.platform === 'win32') {
                let driveLetterPathMatch;
                if ((driveLetterPathMatch = pathOfPotentialRepo.match(DRIVE_LETTER_PATH_REGEX)) && !pathReturnedByGit.match(DRIVE_LETTER_PATH_REGEX)) {
                    const realPathForDriveLetter = utils_1.pathWithTrailingSlash(yield utils_1.realpath(driveLetterPathMatch[0], true));
                    if (realPathForDriveLetter !== driveLetterPathMatch[0] && pathReturnedByGit.startsWith(realPathForDriveLetter)) {
                        pathReturnedByGit = driveLetterPathMatch[0] + pathReturnedByGit.substring(realPathForDriveLetter.length);
                    }
                }
            }
            let path = pathOfPotentialRepo;
            let first = path.indexOf('/');
            while (true) {
                if (pathReturnedByGit === path || pathReturnedByGit === (yield utils_1.realpath(path)))
                    return path;
                let next = path.lastIndexOf('/');
                if (first !== next && next > -1) {
                    path = path.substring(0, next);
                }
                else {
                    return pathReturnedByGit;
                }
            }
        })).catch(() => null);
    }
    addRemote(repo, name, url, pushUrl, fetch) {
        return __awaiter(this, void 0, void 0, function* () {
            let status = yield this.runGitCommand(['remote', 'add', name, url], repo);
            if (status !== null)
                return status;
            if (pushUrl !== null) {
                status = yield this.runGitCommand(['remote', 'set-url', name, '--push', pushUrl], repo);
                if (status !== null)
                    return status;
            }
            return fetch ? this.fetch(repo, name, false, false) : null;
        });
    }
    deleteRemote(repo, name) {
        return this.runGitCommand(['remote', 'remove', name], repo);
    }
    editRemote(repo, nameOld, nameNew, urlOld, urlNew, pushUrlOld, pushUrlNew) {
        return __awaiter(this, void 0, void 0, function* () {
            if (nameOld !== nameNew) {
                let status = yield this.runGitCommand(['remote', 'rename', nameOld, nameNew], repo);
                if (status !== null)
                    return status;
            }
            if (urlOld !== urlNew) {
                let args = ['remote', 'set-url', nameNew];
                if (urlNew === null)
                    args.push('--delete', urlOld);
                else if (urlOld === null)
                    args.push('--add', urlNew);
                else
                    args.push(urlNew, urlOld);
                let status = yield this.runGitCommand(args, repo);
                if (status !== null)
                    return status;
            }
            if (pushUrlOld !== pushUrlNew) {
                let args = ['remote', 'set-url', '--push', nameNew];
                if (pushUrlNew === null)
                    args.push('--delete', pushUrlOld);
                else if (pushUrlOld === null)
                    args.push('--add', pushUrlNew);
                else
                    args.push(pushUrlNew, pushUrlOld);
                let status = yield this.runGitCommand(args, repo);
                if (status !== null)
                    return status;
            }
            return null;
        });
    }
    pruneRemote(repo, name) {
        return this.runGitCommand(['remote', 'prune', name], repo);
    }
    addTag(repo, tagName, commitHash, type, message, force) {
        const args = ['tag'];
        if (force) {
            args.push('-f');
        }
        if (type === 1) {
            args.push(tagName);
        }
        else {
            args.push(config_1.getConfig().signTags ? '-s' : '-a', tagName, '-m', message);
        }
        args.push(commitHash);
        return this.runGitCommand(args, repo);
    }
    deleteTag(repo, tagName, deleteOnRemote) {
        return __awaiter(this, void 0, void 0, function* () {
            if (deleteOnRemote !== null) {
                let status = yield this.runGitCommand(['push', deleteOnRemote, '--delete', tagName], repo);
                if (status !== null)
                    return status;
            }
            return this.runGitCommand(['tag', '-d', tagName], repo);
        });
    }
    fetch(repo, remote, prune, pruneTags) {
        let args = ['fetch', remote === null ? '--all' : remote];
        if (prune) {
            args.push('--prune');
        }
        if (pruneTags) {
            if (!prune) {
                return Promise.resolve('In order to Prune Tags, pruning must also be enabled when fetching from ' + (remote !== null ? 'a remote' : 'remote(s)') + '.');
            }
            else if (this.gitExecutable !== null && !utils_1.doesVersionMeetRequirement(this.gitExecutable.version, '2.17.0')) {
                return Promise.resolve(utils_1.constructIncompatibleGitVersionMessage(this.gitExecutable, '2.17.0', 'pruning tags when fetching'));
            }
            args.push('--prune-tags');
        }
        return this.runGitCommand(args, repo);
    }
    pushBranch(repo, branchName, remote, setUpstream, mode) {
        let args = ['push'];
        args.push(remote, branchName);
        if (setUpstream)
            args.push('--set-upstream');
        if (mode !== "")
            args.push('--' + mode);
        return this.runGitCommand(args, repo);
    }
    pushTag(repo, tagName, remote) {
        return this.runGitCommand(['push', remote, tagName], repo);
    }
    pushBranchToMultipleRemotes(repo, branchName, remotes, setUpstream, mode) {
        return __awaiter(this, void 0, void 0, function* () {
            if (remotes.length === 0) {
                return ['No remote(s) were specified to push the branch ' + branchName + ' to.'];
            }
            const results = [];
            for (let i = 0; i < remotes.length; i++) {
                const result = yield this.pushBranch(repo, branchName, remotes[i], setUpstream, mode);
                results.push(result);
                if (result !== null)
                    break;
            }
            return results;
        });
    }
    pushTagToMultipleRemotes(repo, tagName, remotes) {
        return __awaiter(this, void 0, void 0, function* () {
            if (remotes.length === 0) {
                return ['No remote(s) were specified to push the tag ' + tagName + ' to.'];
            }
            const results = [];
            for (let i = 0; i < remotes.length; i++) {
                const result = yield this.pushTag(repo, tagName, remotes[i]);
                results.push(result);
                if (result !== null)
                    break;
            }
            return results;
        });
    }
    checkoutBranch(repo, branchName, remoteBranch) {
        let args = ['checkout'];
        if (remoteBranch === null)
            args.push(branchName);
        else
            args.push('-b', branchName, remoteBranch);
        return this.runGitCommand(args, repo);
    }
    createBranch(repo, branchName, commitHash, checkout, force) {
        return __awaiter(this, void 0, void 0, function* () {
            const args = [];
            if (checkout && !force) {
                args.push('checkout', '-b');
            }
            else {
                args.push('branch');
                if (force) {
                    args.push('-f');
                }
            }
            args.push(branchName, commitHash);
            const statuses = [yield this.runGitCommand(args, repo)];
            if (statuses[0] === null && checkout && force) {
                statuses.push(yield this.checkoutBranch(repo, branchName, null));
            }
            return statuses;
        });
    }
    deleteBranch(repo, branchName, forceDelete) {
        let args = ['branch', '--delete'];
        if (forceDelete)
            args.push('--force');
        args.push(branchName);
        return this.runGitCommand(args, repo);
    }
    deleteRemoteBranch(repo, branchName, remote) {
        return __awaiter(this, void 0, void 0, function* () {
            let remoteStatus = yield this.runGitCommand(['push', remote, '--delete', branchName], repo);
            if (remoteStatus !== null && (new RegExp('remote ref does not exist', 'i')).test(remoteStatus)) {
                let trackingBranchStatus = yield this.runGitCommand(['branch', '-d', '-r', remote + '/' + branchName], repo);
                return trackingBranchStatus === null ? null : 'Branch does not exist on the remote, deleting the remote tracking branch ' + remote + '/' + branchName + '.\n' + trackingBranchStatus;
            }
            return remoteStatus;
        });
    }
    fetchIntoLocalBranch(repo, remote, remoteBranch, localBranch, force) {
        const args = ['fetch'];
        if (force) {
            args.push('-f');
        }
        args.push(remote, remoteBranch + ':' + localBranch);
        return this.runGitCommand(args, repo);
    }
    pullBranch(repo, branchName, remote, createNewCommit, squash) {
        const args = ['pull', remote, branchName], config = config_1.getConfig();
        if (squash) {
            args.push('--squash');
        }
        else if (createNewCommit) {
            args.push('--no-ff');
        }
        if (config.signCommits) {
            args.push('-S');
        }
        return this.runGitCommand(args, repo).then((pullStatus) => {
            return pullStatus === null && squash
                ? this.commitSquashIfStagedChangesExist(repo, remote + '/' + branchName, "Branch", config.squashPullMessageFormat, config.signCommits)
                : pullStatus;
        });
    }
    renameBranch(repo, oldName, newName) {
        return this.runGitCommand(['branch', '-m', oldName, newName], repo);
    }
    merge(repo, obj, actionOn, createNewCommit, squash, noCommit) {
        const args = ['merge', obj], config = config_1.getConfig();
        if (squash) {
            args.push('--squash');
        }
        else if (createNewCommit) {
            args.push('--no-ff');
        }
        if (noCommit) {
            args.push('--no-commit');
        }
        if (config.signCommits) {
            args.push('-S');
        }
        return this.runGitCommand(args, repo).then((mergeStatus) => {
            return mergeStatus === null && squash && !noCommit
                ? this.commitSquashIfStagedChangesExist(repo, obj, actionOn, config.squashMergeMessageFormat, config.signCommits)
                : mergeStatus;
        });
    }
    rebase(repo, obj, actionOn, ignoreDate, interactive) {
        if (interactive) {
            return this.openGitTerminal(repo, 'rebase --interactive ' + (config_1.getConfig().signCommits ? '-S ' : '') + (actionOn === "Branch" ? obj.replace(/'/g, '"\'"') : obj), 'Rebase on "' + (actionOn === "Branch" ? obj : utils_1.abbrevCommit(obj)) + '"');
        }
        else {
            const args = ['rebase', obj];
            if (ignoreDate) {
                args.push('--ignore-date');
            }
            if (config_1.getConfig().signCommits) {
                args.push('-S');
            }
            return this.runGitCommand(args, repo);
        }
    }
    archive(repo, ref, outputFilePath, type) {
        return this.runGitCommand(['archive', '--format=' + type, '-o', outputFilePath, ref], repo);
    }
    checkoutCommit(repo, commitHash) {
        return this.runGitCommand(['checkout', commitHash], repo);
    }
    cherrypickCommit(repo, commitHash, parentIndex, recordOrigin, noCommit) {
        const args = ['cherry-pick'];
        if (noCommit) {
            args.push('--no-commit');
        }
        if (recordOrigin) {
            args.push('-x');
        }
        if (config_1.getConfig().signCommits) {
            args.push('-S');
        }
        if (parentIndex > 0) {
            args.push('-m', parentIndex.toString());
        }
        args.push(commitHash);
        return this.runGitCommand(args, repo);
    }
    dropCommit(repo, commitHash) {
        const args = ['rebase'];
        if (config_1.getConfig().signCommits) {
            args.push('-S');
        }
        args.push('--onto', commitHash + '^', commitHash);
        return this.runGitCommand(args, repo);
    }
    resetToCommit(repo, commit, resetMode) {
        return this.runGitCommand(['reset', '--' + resetMode, commit], repo);
    }
    revertCommit(repo, commitHash, parentIndex) {
        const args = ['revert', '--no-edit'];
        if (config_1.getConfig().signCommits) {
            args.push('-S');
        }
        if (parentIndex > 0) {
            args.push('-m', parentIndex.toString());
        }
        args.push(commitHash);
        return this.runGitCommand(args, repo);
    }
    setConfigValue(repo, key, value, location) {
        return this.runGitCommand(['config', '--' + location, key, value], repo);
    }
    unsetConfigValue(repo, key, location) {
        return this.runGitCommand(['config', '--' + location, '--unset-all', key], repo);
    }
    cleanUntrackedFiles(repo, directories) {
        return this.runGitCommand(['clean', '-f' + (directories ? 'd' : '')], repo);
    }
    applyStash(repo, selector, reinstateIndex) {
        let args = ['stash', 'apply'];
        if (reinstateIndex)
            args.push('--index');
        args.push(selector);
        return this.runGitCommand(args, repo);
    }
    branchFromStash(repo, selector, branchName) {
        return this.runGitCommand(['stash', 'branch', branchName, selector], repo);
    }
    dropStash(repo, selector) {
        return this.runGitCommand(['stash', 'drop', selector], repo);
    }
    popStash(repo, selector, reinstateIndex) {
        let args = ['stash', 'pop'];
        if (reinstateIndex)
            args.push('--index');
        args.push(selector);
        return this.runGitCommand(args, repo);
    }
    pushStash(repo, message, includeUntracked) {
        if (this.gitExecutable === null) {
            return Promise.resolve(utils_1.UNABLE_TO_FIND_GIT_MSG);
        }
        else if (!utils_1.doesVersionMeetRequirement(this.gitExecutable.version, '2.13.2')) {
            return Promise.resolve(utils_1.constructIncompatibleGitVersionMessage(this.gitExecutable, '2.13.2'));
        }
        let args = ['stash', 'push'];
        if (includeUntracked)
            args.push('--include-untracked');
        if (message !== '')
            args.push('--message', message);
        return this.runGitCommand(args, repo);
    }
    openExternalDirDiff(repo, fromHash, toHash, isGui) {
        return new Promise((resolve) => {
            if (this.gitExecutable === null) {
                resolve(utils_1.UNABLE_TO_FIND_GIT_MSG);
            }
            else {
                const args = ['difftool', '--dir-diff'];
                if (isGui) {
                    args.push('-g');
                }
                if (fromHash === toHash) {
                    if (toHash === utils_1.UNCOMMITTED) {
                        args.push('HEAD');
                    }
                    else {
                        args.push(toHash + '^..' + toHash);
                    }
                }
                else {
                    if (toHash === utils_1.UNCOMMITTED) {
                        args.push(fromHash);
                    }
                    else {
                        args.push(fromHash + '..' + toHash);
                    }
                }
                if (isGui) {
                    this.logger.log('External diff tool is being opened (' + args[args.length - 1] + ')');
                    this.runGitCommand(args, repo).then((errorInfo) => {
                        this.logger.log('External diff tool has exited (' + args[args.length - 1] + ')');
                        if (errorInfo !== null) {
                            const errorMessage = errorInfo.replace(EOL_REGEX, ' ');
                            this.logger.logError(errorMessage);
                            utils_1.showErrorMessage(errorMessage);
                        }
                    });
                }
                else {
                    utils_1.openGitTerminal(repo, this.gitExecutable.path, args.join(' '), 'Open External Directory Diff');
                }
                setTimeout(() => resolve(null), 1500);
            }
        });
    }
    openGitTerminal(repo, command, name) {
        return new Promise((resolve) => {
            if (this.gitExecutable === null) {
                resolve(utils_1.UNABLE_TO_FIND_GIT_MSG);
            }
            else {
                utils_1.openGitTerminal(repo, this.gitExecutable.path, command, name);
                setTimeout(() => resolve(null), 1000);
            }
        });
    }
    getBranches(repo, showRemoteBranches, hideRemotes) {
        let args = ['branch'];
        if (showRemoteBranches)
            args.push('-a');
        args.push('--no-color');
        const hideRemotePatterns = hideRemotes.map((remote) => 'remotes/' + remote + '/');
        const showRemoteHeads = config_1.getConfig().showRemoteHeads;
        return this.spawnGit(args, repo, (stdout) => {
            let branchData = { branches: [], head: null, error: null };
            let lines = stdout.split(EOL_REGEX);
            for (let i = 0; i < lines.length - 1; i++) {
                let name = lines[i].substring(2).split(' -> ')[0];
                if (INVALID_BRANCH_REGEXP.test(name) || hideRemotePatterns.some((pattern) => name.startsWith(pattern)) || (!showRemoteHeads && REMOTE_HEAD_BRANCH_REGEXP.test(name))) {
                    continue;
                }
                if (lines[i][0] === '*') {
                    branchData.head = name;
                    branchData.branches.unshift(name);
                }
                else {
                    branchData.branches.push(name);
                }
            }
            return branchData;
        });
    }
    getCommitDetailsBase(repo, commitHash) {
        return this.spawnGit(['-c', 'log.showSignature=false', 'show', '--quiet', commitHash, '--format=' + this.gitFormatCommitDetails], repo, (stdout) => {
            const commitInfo = stdout.split(GIT_LOG_SEPARATOR);
            return {
                hash: commitInfo[0],
                parents: commitInfo[1] !== '' ? commitInfo[1].split(' ') : [],
                author: commitInfo[2],
                authorEmail: commitInfo[3],
                authorDate: parseInt(commitInfo[4]),
                committer: commitInfo[5],
                committerEmail: commitInfo[6],
                committerDate: parseInt(commitInfo[7]),
                signature: ['G', 'U', 'X', 'Y', 'R', 'E', 'B'].includes(commitInfo[8])
                    ? {
                        key: commitInfo[10].trim(),
                        signer: commitInfo[9].trim(),
                        status: commitInfo[8]
                    }
                    : null,
                body: removeTrailingBlankLines(commitInfo.slice(11).join(GIT_LOG_SEPARATOR).split(EOL_REGEX)).join('\n'),
                fileChanges: []
            };
        });
    }
    getConfigList(repo, location) {
        const args = ['--no-pager', 'config', '--list', '-z', '--includes'];
        if (location) {
            args.push('--' + location);
        }
        return this.spawnGit(args, repo, (stdout) => {
            const configs = {}, keyValuePairs = stdout.split('\0');
            const numPairs = keyValuePairs.length - 1;
            let comps, key;
            for (let i = 0; i < numPairs; i++) {
                comps = keyValuePairs[i].split(EOL_REGEX);
                key = comps.shift();
                configs[key] = comps.join('\n');
            }
            return configs;
        }).catch((errorMessage) => {
            if (typeof errorMessage === 'string') {
                const message = errorMessage.toLowerCase();
                if (message.startsWith('fatal: unable to read config file') && message.endsWith('no such file or directory')) {
                    return {};
                }
            }
            else {
                errorMessage = 'An unexpected error occurred while spawning the Git child process.';
            }
            throw errorMessage;
        });
    }
    getDiffNameStatus(repo, fromHash, toHash, filter = 'AMDR') {
        return this.execDiff(repo, fromHash, toHash, '--name-status', filter).then((output) => {
            let records = [], i = 0;
            while (i < output.length && output[i] !== '') {
                let type = output[i][0];
                if (type === "A" || type === "D" || type === "M") {
                    let p = utils_1.getPathFromStr(output[i + 1]);
                    records.push({ type: type, oldFilePath: p, newFilePath: p });
                    i += 2;
                }
                else if (type === "R") {
                    records.push({ type: type, oldFilePath: utils_1.getPathFromStr(output[i + 1]), newFilePath: utils_1.getPathFromStr(output[i + 2]) });
                    i += 3;
                }
                else {
                    break;
                }
            }
            return records;
        });
    }
    getDiffNumStat(repo, fromHash, toHash, filter = 'AMDR') {
        return this.execDiff(repo, fromHash, toHash, '--numstat', filter).then((output) => {
            let records = [], i = 0;
            while (i < output.length && output[i] !== '') {
                let fields = output[i].split('\t');
                if (fields.length !== 3)
                    break;
                if (fields[2] !== '') {
                    records.push({ filePath: utils_1.getPathFromStr(fields[2]), additions: parseInt(fields[0]), deletions: parseInt(fields[1]) });
                    i += 1;
                }
                else {
                    records.push({ filePath: utils_1.getPathFromStr(output[i + 2]), additions: parseInt(fields[0]), deletions: parseInt(fields[1]) });
                    i += 3;
                }
            }
            return records;
        });
    }
    getLog(repo, branches, num, includeTags, includeRemotes, includeCommitsMentionedByReflogs, onlyFollowFirstParent, order, remotes, hideRemotes, stashes) {
        const args = ['-c', 'log.showSignature=false', 'log', '--max-count=' + num, '--format=' + this.gitFormatLog, '--' + order + '-order'];
        if (onlyFollowFirstParent) {
            args.push('--first-parent');
        }
        if (branches !== null) {
            for (let i = 0; i < branches.length; i++) {
                args.push(branches[i]);
            }
        }
        else {
            args.push('--branches');
            if (includeTags)
                args.push('--tags');
            if (includeCommitsMentionedByReflogs)
                args.push('--reflog');
            if (includeRemotes) {
                if (hideRemotes.length === 0) {
                    args.push('--remotes');
                }
                else {
                    remotes.filter((remote) => !hideRemotes.includes(remote)).forEach((remote) => {
                        args.push('--glob=refs/remotes/' + remote);
                    });
                }
            }
            const stashBaseHashes = stashes.map((stash) => stash.baseHash);
            stashBaseHashes.filter((hash, index) => stashBaseHashes.indexOf(hash) === index).forEach((hash) => args.push(hash));
            args.push('HEAD');
        }
        args.push('--');
        return this.spawnGit(args, repo, (stdout) => {
            let lines = stdout.split(EOL_REGEX);
            let commits = [];
            for (let i = 0; i < lines.length - 1; i++) {
                let line = lines[i].split(GIT_LOG_SEPARATOR);
                if (line.length !== 6)
                    break;
                commits.push({ hash: line[0], parents: line[1] !== '' ? line[1].split(' ') : [], author: line[2], email: line[3], date: parseInt(line[4]), message: line[5] });
            }
            return commits;
        });
    }
    getRefs(repo, showRemoteBranches, showRemoteHeads, hideRemotes) {
        let args = ['show-ref'];
        if (!showRemoteBranches)
            args.push('--heads', '--tags');
        args.push('-d', '--head');
        const hideRemotePatterns = hideRemotes.map((remote) => 'refs/remotes/' + remote + '/');
        return this.spawnGit(args, repo, (stdout) => {
            let refData = { head: null, heads: [], tags: [], remotes: [] };
            let lines = stdout.split(EOL_REGEX);
            for (let i = 0; i < lines.length - 1; i++) {
                let line = lines[i].split(' ');
                if (line.length < 2)
                    continue;
                let hash = line.shift();
                let ref = line.join(' ');
                if (ref.startsWith('refs/heads/')) {
                    refData.heads.push({ hash: hash, name: ref.substring(11) });
                }
                else if (ref.startsWith('refs/tags/')) {
                    let annotated = ref.endsWith('^{}');
                    refData.tags.push({ hash: hash, name: (annotated ? ref.substring(10, ref.length - 3) : ref.substring(10)), annotated: annotated });
                }
                else if (ref.startsWith('refs/remotes/')) {
                    if (!hideRemotePatterns.some((pattern) => ref.startsWith(pattern)) && (showRemoteHeads || !ref.endsWith('/HEAD'))) {
                        refData.remotes.push({ hash: hash, name: ref.substring(13) });
                    }
                }
                else if (ref === 'HEAD') {
                    refData.head = hash;
                }
            }
            return refData;
        });
    }
    getStashes(repo) {
        return this.spawnGit(['reflog', '--format=' + this.gitFormatStash, 'refs/stash', '--'], repo, (stdout) => {
            let lines = stdout.split(EOL_REGEX);
            let stashes = [];
            for (let i = 0; i < lines.length - 1; i++) {
                let line = lines[i].split(GIT_LOG_SEPARATOR);
                if (line.length !== 7 || line[1] === '')
                    continue;
                let parentHashes = line[1].split(' ');
                stashes.push({
                    hash: line[0],
                    baseHash: parentHashes[0],
                    untrackedFilesHash: parentHashes.length === 3 ? parentHashes[2] : null,
                    selector: line[2],
                    author: line[3],
                    email: line[4],
                    date: parseInt(line[5]),
                    message: line[6]
                });
            }
            return stashes;
        }).catch(() => []);
    }
    getRemotes(repo) {
        return this.spawnGit(['remote'], repo, (stdout) => {
            let lines = stdout.split(EOL_REGEX);
            lines.pop();
            return lines;
        });
    }
    getUncommittedChanges(repo) {
        return this.spawnGit(['status', '--untracked-files=' + (config_1.getConfig().showUntrackedFiles ? 'all' : 'no'), '--porcelain'], repo, (stdout) => {
            const numLines = stdout.split(EOL_REGEX).length;
            return numLines > 1 ? numLines - 1 : 0;
        });
    }
    getStatus(repo) {
        return this.spawnGit(['status', '-s', '--untracked-files=' + (config_1.getConfig().showUntrackedFiles ? 'all' : 'no'), '--porcelain', '-z'], repo, (stdout) => {
            let output = stdout.split('\0'), i = 0;
            let status = { deleted: [], untracked: [] };
            let path = '', c1 = '', c2 = '';
            while (i < output.length && output[i] !== '') {
                if (output[i].length < 4)
                    break;
                path = output[i].substring(3);
                c1 = output[i].substring(0, 1);
                c2 = output[i].substring(1, 2);
                if (c1 === 'D' || c2 === 'D')
                    status.deleted.push(path);
                else if (c1 === '?' || c2 === '?')
                    status.untracked.push(path);
                if (c1 === 'R' || c2 === 'R' || c1 === 'C' || c2 === 'C') {
                    i += 2;
                }
                else {
                    i += 1;
                }
            }
            return status;
        });
    }
    commitSquashIfStagedChangesExist(repo, obj, actionOn, squashMessageFormat, signCommits) {
        return this.areStagedChanges(repo).then((changes) => {
            if (changes) {
                const args = ['commit'];
                if (signCommits) {
                    args.push('-S');
                }
                if (squashMessageFormat === 0) {
                    args.push('-m', 'Merge ' + actionOn.toLowerCase() + ' \'' + obj + '\'');
                }
                else {
                    args.push('--no-edit');
                }
                return this.runGitCommand(args, repo);
            }
            else {
                return null;
            }
        });
    }
    execDiff(repo, fromHash, toHash, arg, filter) {
        let args;
        if (fromHash === toHash) {
            args = ['diff-tree', arg, '-r', '--root', '--find-renames', '--diff-filter=' + filter, '-z', fromHash];
        }
        else {
            args = ['diff', arg, '--find-renames', '--diff-filter=' + filter, '-z', fromHash];
            if (toHash !== '')
                args.push(toHash);
        }
        return this.spawnGit(args, repo, (stdout) => {
            let lines = stdout.split('\0');
            if (fromHash === toHash)
                lines.shift();
            return lines;
        });
    }
    runGitCommand(args, repo) {
        return this._spawnGit(args, repo, () => null).catch((errorMessage) => errorMessage);
    }
    spawnGit(args, repo, resolveValue) {
        return this._spawnGit(args, repo, (stdout) => resolveValue(stdout.toString()));
    }
    _spawnGit(args, repo, resolveValue) {
        return new Promise((resolve, reject) => {
            if (this.gitExecutable === null)
                return reject(utils_1.UNABLE_TO_FIND_GIT_MSG);
            utils_1.resolveSpawnOutput(cp.spawn(this.gitExecutable.path, args, {
                cwd: repo,
                env: Object.assign({}, process.env, this.askpassEnv)
            })).then((values) => {
                let status = values[0], stdout = values[1];
                if (status.code === 0) {
                    resolve(resolveValue(stdout));
                }
                else {
                    reject(getErrorMessage(status.error, stdout, values[2]));
                }
            });
            this.logger.logCmd('git', args);
        });
    }
}
exports.DataSource = DataSource;
function generateFileChanges(nameStatusRecords, numStatRecords, status) {
    let fileChanges = [], fileLookup = {}, i = 0;
    for (i = 0; i < nameStatusRecords.length; i++) {
        fileLookup[nameStatusRecords[i].newFilePath] = fileChanges.length;
        fileChanges.push({ oldFilePath: nameStatusRecords[i].oldFilePath, newFilePath: nameStatusRecords[i].newFilePath, type: nameStatusRecords[i].type, additions: null, deletions: null });
    }
    if (status !== null) {
        let filePath;
        for (i = 0; i < status.deleted.length; i++) {
            filePath = utils_1.getPathFromStr(status.deleted[i]);
            if (typeof fileLookup[filePath] === 'number') {
                fileChanges[fileLookup[filePath]].type = "D";
            }
            else {
                fileChanges.push({ oldFilePath: filePath, newFilePath: filePath, type: "D", additions: null, deletions: null });
            }
        }
        for (i = 0; i < status.untracked.length; i++) {
            filePath = utils_1.getPathFromStr(status.untracked[i]);
            fileChanges.push({ oldFilePath: filePath, newFilePath: filePath, type: "U", additions: null, deletions: null });
        }
    }
    for (i = 0; i < numStatRecords.length; i++) {
        if (typeof fileLookup[numStatRecords[i].filePath] === 'number') {
            fileChanges[fileLookup[numStatRecords[i].filePath]].additions = numStatRecords[i].additions;
            fileChanges[fileLookup[numStatRecords[i].filePath]].deletions = numStatRecords[i].deletions;
        }
    }
    return fileChanges;
}
function getConfigValue(configs, key) {
    return typeof configs[key] !== 'undefined' ? configs[key] : null;
}
function getErrorMessage(error, stdoutBuffer, stderr) {
    let stdout = stdoutBuffer.toString(), lines;
    if (stdout !== '' || stderr !== '') {
        lines = (stderr + stdout).split(EOL_REGEX);
        lines.pop();
    }
    else if (error) {
        lines = error.message.split(EOL_REGEX);
    }
    else {
        lines = [];
    }
    return lines.join('\n');
}
function removeTrailingBlankLines(lines) {
    while (lines.length > 0 && lines[lines.length - 1] === '') {
        lines.pop();
    }
    return lines;
}
function unique(items) {
    const uniqueItems = {};
    items.forEach((item) => uniqueItems[item] = true);
    return Object.keys(uniqueItems);
}
//# sourceMappingURL=dataSource.js.map