UNPKG

git-release-manager

Version:

A tool to generate release notes from git commit history

239 lines 11.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BranchManager = void 0; const simple_git_1 = require("simple-git"); const GitVersionManager_1 = require("../version/GitVersionManager"); class BranchManager { constructor() { this.git = (0, simple_git_1.simpleGit)(); this.gitVersionManager = new GitVersionManager_1.GitVersionManager(); } async createBranch(branchName, basedOn = null, switchToNewBranch = true) { const currentBranch = await this.getCurrentBranch(); const branchToCheckout = basedOn !== null && basedOn !== void 0 ? basedOn : currentBranch; // Check out to the base branch if specified, otherwise stay on the current branch if (basedOn && currentBranch != basedOn) { await this.git.checkout(basedOn); console.log(`Checked out to base branch '${basedOn}'`); } // Create and/or check out to the new branch await this.git.checkoutLocalBranch(branchName); console.log(`Created branch '${branchName}' from '${branchToCheckout}'`); // Decide whether to switch to the new branch if (!switchToNewBranch) { let switchBackBranch = branchToCheckout; // if (basedOn && currentBranch != basedOn) { // switchBackBranch = currentBranch // } // If switchToNewBranch is false, switch back to the original branch await this.git.checkout(switchBackBranch); console.log(`Switched back to branch '${switchBackBranch}'`); } } async deleteBranch(branchName) { const currentBranch = await this.getCurrentBranch(); if (currentBranch === branchName) { throw new Error(`Cannot delete the current branch: ${branchName}`); } // Check if branch is protected if (await this.isBranchProtected(branchName)) { throw new Error(`Cannot delete protected branch: ${branchName}`); } await this.git.deleteLocalBranch(branchName, true); console.log(`Deleted branch '${branchName}'`); } async listBranches(config, remote) { const branchSource = typeof remote === 'boolean' ? config.repository.remote.default : remote; let localBranches = []; let remoteBranches = []; // Fetch branch information based on remote setting if (branchSource) { // Fetch branches from the specified remote or default remote await this.git.fetch(branchSource); const fetchedRemoteBranches = await this.git.branch(['-r']); remoteBranches = fetchedRemoteBranches.all.filter(branch => branch.startsWith(`remotes/${branchSource}/`)); } else { // Retrieve all branches for both local and remotes when no specific remote is specified const branchSummary = await this.git.branch(['-a']); localBranches = branchSummary.all.filter(branch => !branch.includes('remotes/')); remoteBranches = branchSummary.all.filter(branch => branch.includes('remotes/')); } // Output branches in requested format if (!branchSource) { // Print local branches if no remote is explicitly specified console.log('\nLocal:'); localBranches.forEach(branch => console.log(` ${branch}`)); } // Group remote branches by their specific remote name and print const groupedRemoteBranches = {}; remoteBranches.forEach(branch => { const branchInfo = branch.replace('remotes/', '').split('/'); const remoteName = branchInfo[0]; const branchName = branchInfo.slice(1).join('/'); if (!groupedRemoteBranches[remoteName]) { groupedRemoteBranches[remoteName] = []; } groupedRemoteBranches[remoteName].push(branchName); }); Object.entries(groupedRemoteBranches).forEach(([remoteName, branches]) => { console.log(`\n${remoteName}:`); branches.forEach(branch => console.log(` ${branch}`)); }); } async switchBranch(branchName) { await this.git.checkout(branchName); console.log(`Switched to branch '${branchName}'`); } async mergeBranch(config, branchName, squash) { const currentBranch = await this.getCurrentBranch(); try { await this.git.fetch(); // Determine if squash is requested and merge accordingly if (squash) { // Perform a squash merge await this.git.raw(['merge', '--squash', branchName]); await this.git.commit(`Squashed merge of '${branchName}' into '${currentBranch}'`); console.log(`Squashed '${branchName}' into '${currentBranch}'`); } else { // Perform a regular merge await this.git.merge([branchName, '--no-ff']); console.log(`Merged '${branchName}' into '${currentBranch}'`); } } catch (error) { console.error(`Failed to complete merge or push operation:`, error); } } async rebaseBranch(config, branchName) { const currentBranch = await this.getCurrentBranch(); try { await this.git.fetch(); // Rebase from either remote or local branch await this.git.rebase([branchName]); console.log(`Rebased '${currentBranch}' with '${branchName}'`); } catch (error) { console.error(`Failed to complete rebase or push operation:`, error); } } async protectBranch(branchName) { const currentBranch = await this.getCurrentBranch(); const activeBranch = typeof branchName === 'string' && branchName.trim() !== '' ? branchName : currentBranch; const protectedBranches = await this.getProtectedBranches(); if (!protectedBranches.includes(activeBranch)) { protectedBranches.push(activeBranch); await this.saveProtectedBranches(protectedBranches); console.log(`Protected branch '${activeBranch}'`); } } async unprotectBranch(branchName) { const currentBranch = await this.getCurrentBranch(); const activeBranch = typeof branchName === 'string' && branchName.trim() !== '' ? branchName : currentBranch; const protectedBranches = await this.getProtectedBranches(); const index = protectedBranches.indexOf(activeBranch); if (index > -1) { protectedBranches.splice(index, 1); await this.saveProtectedBranches(protectedBranches); console.log(`Removed protection from branch '${activeBranch}'`); } } async syncBranch(config, push) { const currentBranch = await this.getCurrentBranch(); await this.git.pull(config.repository.remote.default, currentBranch, ['--rebase']); console.log(`Synced '${currentBranch}' with remote`); if (push) { await this.git.push(config.repository.remote.default, currentBranch); console.log(`Pushed local changes to remote '${currentBranch}'`); } } async getCurrentBranch() { const branchSummary = await this.git.branch(); return branchSummary.current; } async isBranchProtected(branchName) { const protectedBranches = await this.getProtectedBranches(); return protectedBranches.includes(branchName); } async getProtectedBranches() { try { const config = await this.git.listConfig(); const protectedBranchesConfig = config.all['branch.protected']; // If it's already an array, return it if (Array.isArray(protectedBranchesConfig)) { return protectedBranchesConfig; } // If it's a string, parse it if (typeof protectedBranchesConfig === 'string') { return JSON.parse(protectedBranchesConfig); } // If it's undefined or null, return default return []; } catch (_a) { return []; } } async saveProtectedBranches(branches) { await this.git.addConfig('branch.protected', JSON.stringify(branches)); } async createReleaseBranch(config, version, push) { const branchName = `release/${version}`; await this.createBranch(branchName); await this.protectBranch(branchName); console.log(`Created protected release branch '${branchName}'`); } async createHotfixBranch(config, version, push) { const branchName = `hotfix/${version}`; await this.createBranch(branchName); console.log(`Created hotfix branch '${branchName}'`); } async createFeatureBranch(config, name, push) { const branchName = `feature/${name}`; await this.createBranch(branchName); console.log(`Created feature branch '${branchName}'`); } async finishBranch(config, branchName, push) { var _a, _b; const currentBranch = await this.getCurrentBranch(); const activeBranch = typeof branchName === 'string' && branchName.trim() !== '' ? branchName : currentBranch; // Ensure the branch exists const branches = await this.git.branchLocal(); if (!branches.all.includes(activeBranch)) { console.log(`The branch ${branchName} does not exist.`); return; } const gitVersionManager = new GitVersionManager_1.GitVersionManager(); // Determine the branch type from the name const branchType = activeBranch.split('/')[0]; const branchConfig = config.branchStrategies[branchType]; if (!branchConfig) { console.log(`No configuration found for branch type: ${branchType}`); return; } // Merge into base branches as per the configuration for (const baseBranch of branchConfig.baseBranches) { console.log(`Attempting to merge ${activeBranch} into ${baseBranch}`); await this.git.checkout(baseBranch); await this.mergeBranch(config, activeBranch, push); } // Create a tag if required by the configuration if (branchConfig.createTag) { const version = activeBranch.replace(`${branchType}/`, ''); const formattedVersion = version.startsWith((_a = branchConfig.tagPrefix) !== null && _a !== void 0 ? _a : '') ? version : `${(_b = branchConfig.tagPrefix) !== null && _b !== void 0 ? _b : ''}${version}`; const tagName = `${formattedVersion}`; await gitVersionManager.createGitTag(tagName); } // Delete the branch after merge if specified by the configuration if (branchConfig.deleteAfterMerge) { if (activeBranch == currentBranch) { this.switchBranch(branchConfig.baseBranches[0]); } await this.deleteBranch(activeBranch); console.log(`Finished and cleaned up branch '${activeBranch}'`); } } } exports.BranchManager = BranchManager; //# sourceMappingURL=BranchManager.js.map