├── .gitignore ├── commands ├── add-alias.js ├── ai-push.js ├── config.js ├── del-alias.js ├── first-push.js ├── new-repo.js ├── push.js └── revert.js ├── index.js ├── package-lock.json ├── package.json ├── readme.md └── utils ├── checkVersion.js ├── config.json ├── git.js ├── globalConfig.js ├── helpers.js └── reservedNames.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | test.js 3 | test2.js 4 | .env 5 | index.test.js 6 | jest.config.mjs 7 | babel.config.js -------------------------------------------------------------------------------- /commands/add-alias.js: -------------------------------------------------------------------------------- 1 | import fs from "node:fs"; 2 | import path from "node:path"; 3 | import globalModules from "global-modules"; 4 | import reservedNames, { commands } from "../utils/reservedNames.js"; 5 | import inquirer from "inquirer"; 6 | 7 | // Define the path to the config file 8 | const configFile = path.join(globalModules, "easygit-cli/utils/config.json"); 9 | 10 | // Function to read the config file and return its contents as a JSON object 11 | const getConfig = async () => { 12 | const data = await fs.promises.readFile(configFile, "utf-8"); 13 | return JSON.parse(data); 14 | }; 15 | 16 | // Function to add a new alias to the config file 17 | const addAlias = async (alias, command) => { 18 | // Get the current config 19 | const config = await getConfig(); 20 | 21 | try { 22 | // If alias or command is not provided, prompt the user to enter them 23 | if (!command ||!alias) { 24 | const { alias, command } = await inquirer.prompt([ 25 | { 26 | type: "input", 27 | name: "alias", 28 | message: "Enter the alias you want to use", 29 | // Validate the alias input 30 | validate: (input) => { 31 | if (input.trim() === "") { 32 | return "Alias name cannot be empty"; 33 | } 34 | if (reservedNames.includes(input)) { 35 | return `Cannot use already existing alias Name or reserved name ${input} as Alias \n Here is a list of reserved names: ${reservedNames}`; 36 | } 37 | return true; 38 | }, 39 | }, 40 | { 41 | type: "input", 42 | name: "command", 43 | message: "Enter the command its for", 44 | // Validate the command input 45 | validate: (input) => { 46 | if (input.trim() === "") { 47 | return "Command cannot be empty"; 48 | } 49 | if (!(commands.includes(input))) { 50 | return `Command ${input} does not exist\n Here is a list of commands: ${commands}`; 51 | } 52 | return true; 53 | }, 54 | }, 55 | ]); 56 | 57 | // Add the new alias to the config 58 | config.aliases = [...config.aliases, { command, alias, desc: `(alias) for ${command} command` }]; 59 | await fs.promises.writeFile(configFile, JSON.stringify(config), "utf-8"); 60 | } else { 61 | // If alias or command is provided, validate them 62 | if (reservedNames.includes(alias)) { 63 | console.log(`Cannot use already existing alias Name or reserved name ${alias} as Alias \n Here is a list of reserved names: ${reservedNames}`); 64 | return; 65 | } 66 | if (!(commands.includes(command))) { 67 | console.log(`Command ${command} does not exist\n Here is a list of commands: ${commands}`); 68 | return; 69 | } 70 | 71 | // Add the new alias to the config 72 | config.aliases = [...config.aliases, { command, alias, desc: `(alias) for ${command} command` }]; 73 | await fs.promises.writeFile(configFile, JSON.stringify(config), "utf-8"); 74 | } 75 | } catch (err) { 76 | console.error(`Error adding alias: ${err}`); 77 | } 78 | }; 79 | 80 | export default addAlias; -------------------------------------------------------------------------------- /commands/ai-push.js: -------------------------------------------------------------------------------- 1 | import git from '../lib/git'; 2 | import inquirer from "inquirer"; 3 | 4 | async function push() { 5 | try { 6 | await git.reset(['--']); 7 | console.log('All changes unstaged successfully'); 8 | } catch (err) { 9 | console.error('Error unstaging changes:', err); 10 | return; 11 | } 12 | 13 | try { 14 | const diff = await git.diff(); 15 | const status = await git.status(); 16 | const newFiles = status.not_added; 17 | let prompt; 18 | if (diff || newFiles.length) { 19 | prompt = diff 20 | ? newFiles.length 21 | ? `generate a commit message for this diff:${diff} and these new files:${newFiles}. Your response should be an object with the keys of subject and body only` //prompt for diff and newFiles 22 | : `generate a commit message for this diff:${diff}. Your response should be an object with the keys of subject and body only` //prompt for diff alone 23 | : `generate a commit message for these new files:${newFiles}your response should be an object with the keys of subject and body only`; //prompt for newFiles alone 24 | const commitMessage = await model 25 | .generateContent(prompt) 26 | .then((result) => { 27 | console.log(result.response.text()); 28 | const response = JSON.parse(result.response.text().replaceAll(/```|json/, '')); // Assuming JSON format 29 | const subject = response.subject; 30 | const body = response.body; 31 | const msg = { 32 | subject: subject, 33 | body: body, 34 | }; 35 | return msg 36 | }) 37 | .catch((err) => { 38 | console.error("Error generating commit message:", err); 39 | return; 40 | // console.error("Prompt used:", prompt); 41 | }); 42 | try { 43 | await git.add("."); 44 | console.log("File(s) added to staging area."); 45 | } catch (err) { 46 | console.error("Error adding files:", err); 47 | return; 48 | } 49 | try { 50 | await git.commit([`${commitMessage.subject}`, `${commitMessage.body}`]); 51 | console.log("Successfully commited"); 52 | } catch (err) { 53 | console.error("Error commiting:", err); 54 | return; 55 | } 56 | try { 57 | await git.push(); 58 | console.log("Changes pushed successfully to remote repository."); 59 | } catch (err) { 60 | console.error("Error pushing changes to remote repository:", err); 61 | return; 62 | } 63 | } 64 | else { 65 | return console.log("there are no changes to be commited"); 66 | } 67 | 68 | 69 | } catch (error) { 70 | 71 | } 72 | 73 | 74 | } 75 | 76 | export default push; 77 | -------------------------------------------------------------------------------- /commands/config.js: -------------------------------------------------------------------------------- 1 | import inquirer from "inquirer"; 2 | import { getGlobalConfig, setGlobalConfig } from "../utils/globalConfig.js"; 3 | import { updateVersion } from "../utils/checkVersion.js"; 4 | 5 | const config = async () => { 6 | updateVersion() 7 | let username; 8 | let email; 9 | try { 10 | username = await getGlobalConfig("user.name"); 11 | email = await getGlobalConfig("user.email"); 12 | } catch (err) { 13 | console.error(err); 14 | } 15 | if (!username || !email) { 16 | console.info("Your global Git username or email is not configured."); 17 | console.log("Would you like to set them now? (recommended)"); 18 | } 19 | else{ 20 | console.info("Your global Git username or email has been configured."); 21 | console.log("Would you like to update them now?"); 22 | 23 | } 24 | const configureCredentials = await inquirer.prompt([ 25 | { 26 | type: "confirm", 27 | name: "configure", 28 | message: "Press Y to configure and N to skip", 29 | default: true, 30 | }, 31 | ]); 32 | 33 | if (configureCredentials.configure) { 34 | const usernamePrompt = await inquirer.prompt([ 35 | { 36 | type: "input", 37 | name: "username", 38 | message: "Enter your Git username:", 39 | }, 40 | ]); 41 | const emailPrompt = await inquirer.prompt([ 42 | { 43 | type: "input", 44 | name: "email", 45 | message: "Enter your Git email address:", 46 | }, 47 | ]); 48 | await setGlobalConfig("user.name", usernamePrompt.username); 49 | await setGlobalConfig("user.email", emailPrompt.email); 50 | console.log("Username and email successfully configured globally."); 51 | username=usernamePrompt.username 52 | email=emailPrompt.email 53 | } 54 | 55 | console.log(`Username: ${username}`); 56 | console.log(`Email: ${email}`); 57 | 58 | }; 59 | 60 | export default config; 61 | -------------------------------------------------------------------------------- /commands/del-alias.js: -------------------------------------------------------------------------------- 1 | import fs from "node:fs"; 2 | import path from "node:path"; 3 | import globalModules from "global-modules"; 4 | import reservedNames, { commands } from "../utils/reservedNames.js"; 5 | import inquirer from "inquirer"; 6 | 7 | // Define the path to the config file 8 | const configFile = path.join(globalModules, "easygit-cli/utils/config.json"); 9 | 10 | // Function to read the config file and return its contents as a JSON object 11 | const getConfig = async () => { 12 | const data = await fs.promises.readFile(configFile, "utf-8"); 13 | return JSON.parse(data); 14 | }; 15 | 16 | // Function to add a new alias to the config file 17 | const addAlias = async (alias, command) => { 18 | // Get the current config 19 | const config = await getConfig(); 20 | 21 | try { 22 | // If alias or command is not provided, prompt the user to enter them 23 | if (!command ||!alias) { 24 | const { alias, command } = await inquirer.prompt([ 25 | { 26 | type: "input", 27 | name: "alias", 28 | message: "Enter the alias you want to use", 29 | // Validate the alias input 30 | validate: (input) => { 31 | if (input.trim() === "") { 32 | return "Alias name cannot be empty"; 33 | } 34 | if (reservedNames.includes(input)) { 35 | return `Cannot use already existing alias Name or reserved name ${input} as Alias \n Here is a list of reserved names: ${reservedNames}`; 36 | } 37 | return true; 38 | }, 39 | }, 40 | { 41 | type: "input", 42 | name: "command", 43 | message: "Enter the command its for", 44 | // Validate the command input 45 | validate: (input) => { 46 | if (input.trim() === "") { 47 | return "Command cannot be empty"; 48 | } 49 | if (!(commands.includes(input))) { 50 | return `Command ${input} does not exist\n Here is a list of commands: ${commands}`; 51 | } 52 | return true; 53 | }, 54 | }, 55 | ]); 56 | 57 | // Add the new alias to the config 58 | config.aliases = [...config.aliases, { command, alias, desc: `(alias) for ${command} command` }]; 59 | await fs.promises.writeFile(configFile, JSON.stringify(config), "utf-8"); 60 | } else { 61 | // If alias or command is provided, validate them 62 | if (reservedNames.includes(alias)) { 63 | console.log(`Cannot use already existing alias Name or reserved name ${alias} as Alias \n Here is a list of reserved names: ${reservedNames}`); 64 | return; 65 | } 66 | if (!(commands.includes(command))) { 67 | console.log(`Command ${command} does not exist\n Here is a list of commands: ${commands}`); 68 | return; 69 | } 70 | 71 | // Add the new alias to the config 72 | config.aliases = [...config.aliases, { command, alias, desc: `(alias) for ${command} command` }]; 73 | await fs.promises.writeFile(configFile, JSON.stringify(config), "utf-8"); 74 | } 75 | } catch (err) { 76 | console.error(`Error adding alias: ${err}`); 77 | } 78 | }; 79 | 80 | export default addAlias; -------------------------------------------------------------------------------- /commands/first-push.js: -------------------------------------------------------------------------------- 1 | import inquirer from "inquirer"; 2 | import git from "../utils/git.js"; 3 | import ora from "ora"; 4 | import chalk from "chalk"; 5 | import { updateVersion } from "../utils/checkVersion.js"; 6 | 7 | 8 | const firstPush = async (repo) => { 9 | updateVersion() 10 | // If no repository URL is provided, prompt the user to enter one 11 | if (!repo) { 12 | repo = await inquirer 13 | .prompt([ 14 | { 15 | type: "input", 16 | name: "repo", 17 | message: "Enter the repository you wish to push to", 18 | validate: (input) => { 19 | if (input.trim() === "") { 20 | return "Repository URL cannot be empty"; 21 | } 22 | return true; 23 | }, 24 | }, 25 | ]) 26 | .then(({ repo }) => repo); 27 | } 28 | console.log(`Using repository URL: ${repo}`); 29 | 30 | try { 31 | // Initialize an empty Git repository 32 | await git.init(); 33 | console.log("Initialized empty Git repository."); 34 | } catch (err) { 35 | console.error("Error initializing Git repository:", err); 36 | return; 37 | } 38 | 39 | try { 40 | // Add all files in the current directory to the staging area 41 | await git.add("."); 42 | console.log("File(s) added to staging area."); 43 | } catch (err) { 44 | console.error("Error adding files:", err); 45 | return; 46 | } 47 | 48 | try { 49 | // Create an initial commit with a default message 50 | await git.commit(["initial Commit"]); 51 | console.log("Successfully committed"); 52 | } catch (err) { 53 | console.error("Error committing:", err); 54 | return; 55 | } 56 | 57 | try { 58 | // Set the default branch to "main" 59 | await git.branch(["-M", "main"]); 60 | console.log("Successfully changed branch to main."); 61 | } catch (err) { 62 | console.error("Error changing branch to main:", err); 63 | return; 64 | } 65 | 66 | // Check if a remote origin already exists 67 | let existingRemote; 68 | try { 69 | // Get verbose remote information to check if a remote origin exists 70 | existingRemote = await git.remote(["-v"]); 71 | } catch (err) { 72 | // Ignore error if there's no remote yet (assuming `git remote -v` fails) 73 | console.log("No existing remote found. Creating new remote..."); 74 | } 75 | 76 | const remoteName = "origin"; 77 | const remoteUrl = repo; 78 | 79 | if (!existingRemote) { 80 | try { 81 | // Add a new remote origin with the provided repository URL 82 | await git.addRemote(remoteName, remoteUrl); 83 | console.log(`Added remote origin "${remoteName}" with URL "${remoteUrl}"`); 84 | } catch (err) { 85 | console.error(`Error adding remote origin:`, err); 86 | return; 87 | } 88 | } else { 89 | console.log("Using existing remote origin:", existingRemote); 90 | } 91 | 92 | // Push the changes to the remote repository and set the upstream tracking information 93 | try { 94 | const spinner = ora('Pushing changes to remote repository...').start(); // Start the spinner 95 | await git.push("origin", "main", ["-u"]); 96 | spinner.succeed(chalk.green("Done! Changes pushed successfully to remote repository.")); 97 | } catch (err) { 98 | console.error("Error pushing changes to remote repository:", err); 99 | return; 100 | } 101 | }; 102 | 103 | export default firstPush; -------------------------------------------------------------------------------- /commands/new-repo.js: -------------------------------------------------------------------------------- 1 | // import git from "../utils/git.js"; 2 | 3 | // // Set your GitHub credentials 4 | // const githubUsername = 'o-david'; 5 | // const githubPassword = 'Mr_davId@01'; 6 | // const repoName = 'new-repo'; 7 | 8 | // // Initialize a new Git repository 9 | // git.init().then(() => { 10 | // // Create a new remote repository on GitHub 11 | // const githubApiUrl = 'https://api.github.com/repos'; 12 | // const repoData = { 13 | // name: repoName, 14 | // description: 'My new repository', 15 | // private: false, 16 | // }; 17 | 18 | // const auth = `Basic ${Buffer.from(`${githubUsername}:${githubPassword}`).toString('base64')}`; 19 | // const headers = { 20 | // 'Content-Type': 'application/json', 21 | // Authorization: auth, 22 | // }; 23 | 24 | // fetch(githubApiUrl, { 25 | // method: 'POST', 26 | // headers, 27 | // body: JSON.stringify(repoData), 28 | // }) 29 | // .then(response => response.json()) 30 | // .then(data => { 31 | // // const repoUrl = data.html_url; 32 | // console.log(data); 33 | // // console.log(`Repository created: ${repoUrl}`); 34 | 35 | // // Add the remote repository to the local Git config 36 | // // git.addRemote('origin', repoUrl).then(() => { 37 | // // console.log('Remote repository added to local Git config'); 38 | // // }); 39 | // }) 40 | // .catch(error => { 41 | // console.error('Error creating repository:', error); 42 | // }); 43 | // }); 44 | 45 | import git from "../utils/git.js"; 46 | 47 | // Replace with your desired repo name and access details 48 | const repoName = "your-repo-name"; 49 | const username = "your_username"; 50 | const password = "your_password"; // Not recommended for security reasons 51 | 52 | // Construct the SSH URL with username for secure authentication (replace with appropriate URL) 53 | const gitUrl = `ssh://${username}@github.com/${username}/${repoName}.git`; 54 | 55 | (async () => { 56 | 57 | try { 58 | 59 | // Add a remote named "origin" pointing to the git URL 60 | await git.addRemote('origin2', gitUrl); 61 | console.log("Remote repository created and pushed successfully!"); 62 | } catch (err) { 63 | console.error("Error creating remote repo:", err); 64 | } 65 | })(); 66 | -------------------------------------------------------------------------------- /commands/push.js: -------------------------------------------------------------------------------- 1 | // Import required modules 2 | import git from "../utils/git.js"; 3 | import inquirer from "inquirer"; 4 | import ora from "ora"; 5 | import chalk from "chalk"; 6 | import { updateVersion } from "../utils/checkVersion.js"; 7 | 8 | 9 | // Define the push function 10 | const push = async (commitMessage) => { 11 | updateVersion() 12 | try { 13 | // Add a try-catch block around the entire function 14 | // If no commit message is provided, prompt the user for one 15 | if (!commitMessage) { 16 | commitMessage = await inquirer 17 | .prompt([ 18 | { 19 | type: "input", 20 | name: "commitMessage", 21 | message: "Enter a commit message", 22 | validate: (input) => { 23 | if (input.trim() === "") { 24 | return "Repository URL cannot be empty"; 25 | } 26 | return true; 27 | }, 28 | }, 29 | ]) 30 | .then(({ commitMessage }) => commitMessage); 31 | } 32 | commitMessage = commitMessage.replaceAll(/'|"/g, ""); 33 | 34 | // Add all files to the staging area 35 | try { 36 | await git.add("-A"); 37 | console.log("File(s) added to staging area."); 38 | } catch (err) { 39 | console.error("Error adding files:", err); 40 | return; 41 | } 42 | 43 | // Commit the changes with the provided commit message 44 | try { 45 | await git.commit([commitMessage]); 46 | console.log("Successfully committed (" + commitMessage + ")"); 47 | } catch (err) { 48 | console.error("Error committing:", err); 49 | return; 50 | } 51 | 52 | // Check if the local repository is up-to-date with the remote repository 53 | try { 54 | const status = await git.status(); 55 | if (status.behind > 0) { 56 | console.error( 57 | "Local repository is not up-to-date with the remote repository. Please pull the latest changes before pushing." 58 | ); 59 | return; 60 | } 61 | } catch (err) { 62 | console.error("Error checking git status:", err); 63 | return; 64 | } 65 | 66 | // Push the changes to the remote repository 67 | try { 68 | const spinner = ora('Pushing changes to remote repository...').start(); // Start the spinner 69 | await git.push(); 70 | spinner.succeed(chalk.green("Done! Changes pushed successfully to remote repository.")); 71 | } catch (err) { 72 | console.error("Error pushing changes to remote repository:", err); 73 | return; 74 | } 75 | } catch (err) { 76 | console.error("An unexpected error occurred:", err); 77 | } 78 | }; 79 | 80 | // Export the push function 81 | export default push; 82 | -------------------------------------------------------------------------------- /commands/revert.js: -------------------------------------------------------------------------------- 1 | // Import required modules 2 | import git from "../utils/git.js"; 3 | import inquirer from "inquirer"; 4 | import colors from "colors"; 5 | import fs from "node:fs"; 6 | import path from "node:path"; 7 | import globalModules from "global-modules"; 8 | import { updateVersion } from "../utils/checkVersion.js"; 9 | 10 | // Define the path to the config file 11 | const configFile = path.join(globalModules, "easygit-cli/utils/config.json"); 12 | 13 | // Read the config file and parse its contents 14 | const data = fs.readFileSync(`${configFile}`, "utf-8"); 15 | const config = JSON.parse(data); 16 | 17 | // Define the revert function 18 | const revert = async () => { 19 | updateVersion() 20 | // Get the current branch 21 | let currentBranch; 22 | try { 23 | const branch = await git.branch(); 24 | currentBranch = branch.current; 25 | console.log(`Current branch: ${currentBranch}`); 26 | } catch (error) { 27 | console.error('Failed to get current branch', error); 28 | } 29 | 30 | // Show a warning message if the user has not disabled it 31 | if (config.showResetMessage) { 32 | console.log( 33 | "WARNING: This script will find the commit hash of the previous commit, reset the local repository to that commit, and force push the changes to the remote repository on GitHub.".yellow + 34 | "\nBe careful when using --force, as it will overwrite the remote repository with your local repository." + 35 | "\nThis can cause issues if other people are also working on the repository." 36 | ); 37 | 38 | // Ask the user if they want to proceed with the action 39 | const warn = await inquirer.prompt([ 40 | { 41 | type: "confirm", 42 | name: "confirm", 43 | message: "Do you want to proceed with this action?", 44 | default: false, 45 | }, 46 | { 47 | type: "confirm", 48 | name: "showWarningAgain", 49 | message: "Show this warning next time?", 50 | default: true, 51 | }, 52 | ]); 53 | 54 | // If the user does not want to proceed, cancel the action 55 | if (!warn.confirm) { 56 | console.log('Action cancelled.'); 57 | return; 58 | } 59 | 60 | // If the user does not want to see the warning again, disable it 61 | if (!warn.showWarningAgain) { 62 | config.showResetMessage = false; 63 | fs.writeFileSync(configFile, JSON.stringify(config), "utf-8"); 64 | } 65 | } 66 | 67 | // Perform the revert action 68 | try { 69 | // Get the commit hash of the previous commit 70 | await git.stash(); 71 | const log = await git.log(); 72 | const previousCommitHash = log.all[1].hash; 73 | 74 | // Reset the local repository to the previous commit 75 | await git.reset([previousCommitHash, '--soft']); 76 | await git.stash(); 77 | 78 | // Force push the changes to the remote repository 79 | await git.push(['origin', currentBranch, '--force']); 80 | 81 | console.log(`Successfully reverted to commit ${log[0]}`); 82 | } catch (error) { 83 | console.error(error); 84 | } 85 | }; 86 | 87 | // Export the revert function 88 | export default revert; -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { program } from "commander"; 3 | import firstPush from "./commands/first-push.js"; 4 | import push from "./commands/push.js"; 5 | import config from "./commands/config.js"; 6 | import revert from "./commands/revert.js"; 7 | import fs from "node:fs"; 8 | import path from "node:path"; 9 | import globalModules from "global-modules"; 10 | import addAlias from "./commands/add-alias.js"; 11 | 12 | // Define the path to the config file 13 | const configFile = path.join(globalModules, "easygit-cli/utils/config.json"); 14 | 15 | // Read the config file and parse its contents 16 | const data = fs.readFileSync(`${configFile}`, "utf-8"); 17 | const aliases = JSON.parse(data).aliases; 18 | 19 | const commandActions = { 20 | "first-push": firstPush, 21 | push, 22 | config, 23 | revert, 24 | }; 25 | 26 | // Command to add files to staging area 27 | program 28 | .command("first-push [repo]") 29 | .description( 30 | "Initialize git, add to staging area, commit and push to remote repo " 31 | ) 32 | .action(firstPush); 33 | 34 | program 35 | .command("push [commitMessage]") 36 | .description("For adding, commiting and pushing changes") 37 | .action(push); 38 | 39 | program 40 | .command("config") 41 | .description("For setting or retrieving global username and email") 42 | .action(config); 43 | 44 | program 45 | .command("add-alias [alias] [command]") 46 | .description("For setting or retrieving global username and email") 47 | .action(addAlias); 48 | 49 | program 50 | .command("revert") 51 | .description( 52 | "This command will find the commit hash of the previous commit, reset the local repository to that commit, and force push the changes to the remote repository on GitHub." 53 | ) 54 | .action(revert); 55 | 56 | aliases.map((element) => { 57 | program 58 | .command( 59 | element.command === "push" 60 | ? `${element.alias} [commitMessage]` 61 | : element.command === "first-push" 62 | ? `${element.alias} [repo]` 63 | : `${element.alias}` 64 | ) 65 | .description(element.desc) 66 | .action(commandActions[element.command]); 67 | }); 68 | 69 | // Parse command line arguments 70 | program.parse(process.argv); 71 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "easygit-cli", 3 | "version": "4.1.3", 4 | "description": "The EasyGit CLI is a simple command line interface for Git that automates the processes in git", 5 | "type": "module", 6 | "main": "index.js", 7 | "bin": { 8 | "easygit": "index.js" 9 | }, 10 | "scripts": { 11 | "test": "jest" 12 | }, 13 | "keywords": [ 14 | "git", 15 | "push", 16 | "commit", 17 | "branch", 18 | "init", 19 | "cli", 20 | "command-line", 21 | "terminal", 22 | "terminal-app", 23 | "terminal-app-cli", 24 | "terminal-app-git", 25 | "easygit" 26 | ], 27 | "author": "", 28 | "license": "ISC", 29 | "dependencies": { 30 | "@google/generative-ai": "^0.11.1", 31 | "colors": "^1.4.0", 32 | "commander": "^12.0.0", 33 | "dotenv": "^16.4.5", 34 | "global-modules": "^2.0.0", 35 | "inquirer": "^9.2.20", 36 | "simple-git": "^3.24.0" 37 | }, 38 | "repository": { 39 | "type": "git", 40 | "url": "https://github.com/o-david/EasyGit-CLI.git" 41 | }, 42 | "devDependencies": { 43 | "@babel/preset-env": "^7.24.5", 44 | "@babel/register": "^7.23.7", 45 | "jest": "^29.7.0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # EasyGit CLI 2 | 3 | ```bash 4 | npm install -g easygit-cli 5 | ``` 6 | 7 | The EasyGit CLI is a simple command line interface for Git that automates the process of initializing a new Git repository, adding files to the staging area, committing changes, and pushing them to a remote repository. It is built using Node.js and the simple-git library. 8 | 9 | ## Installation 10 | 11 | To install the EasyGit CLI, you will need to have Node.js installed on your computer. Once you have Node.js installed, you can install it by running the following command: 12 | 13 | ```bash 14 | npm install -g easygit-cli 15 | ``` 16 | 17 | or update with 18 | 19 | ```bash 20 | npm update -g easygit-cli 21 | ``` 22 | 23 | This will install the EasyGit CLI globally on your computer, allowing you to use it from any directory. 24 | 25 | ### Enabling Scripts on Windows 26 | 27 | If you are using Windows, you need to enable the execution of scripts in PowerShell 28 | 29 | ### HOW? 30 | 31 | - Open PowerShell as Administrator. 32 | - Run the following command to allow script execution: 33 | 34 | ```powershell 35 | Set-ExecutionPolicy RemoteSigned 36 | ``` 37 | 38 | ## Usage 39 | 40 | ## First Push Command 41 | 42 | The first-push command initializes a new Git repository, stages all files, commits them with the message "Initial Commit", switches the branch to main, sets the remote origin (or uses the existing one), and pushes the changes to the remote repository. 43 | 44 | ### Syntax 45 | 46 | ```bash 47 | easygit first-push [repo] 48 | ``` 49 | 50 | - `repo`: Optional. The URL of the remote repository to push changes to. If not provided, the user will be prompted to enter the repository URL. 51 | 52 | ### Example 53 | 54 | ```bash 55 | easygit first-push https://github.com/username/repository.git 56 | ``` 57 | 58 | ## Push Command 59 | 60 | The `push` command stages changes, commits them with a provided commit message, and pushes the changes to the remote repository. You need to supply a commit message. 61 | 62 | ### Syntax 63 | 64 | ```bash 65 | easygit push [commitMessage] 66 | ``` 67 | 68 | - `commitMessage`: Optional. If not provided, you will be prompted to enter a commit message. 69 | 70 | ### Example 71 | 72 | ```bash 73 | easygit push "refactored index file" 74 | ``` 75 | 76 | ```bash 77 | easygit push 78 | ``` 79 | 80 | ## Alias Command 81 | 82 | The alias feature in easygit-cli allows you to create custom aliases for existing commands. This feature is useful when you want to create shortcuts for frequently used commands or when you want to customize the command names to fit your workflow. 83 | 84 | ### Syntax 85 | 86 | ```bash 87 | easygit add-alias [alias] [command] 88 | ``` 89 | 90 | - `alias` & `command`: Optional. If not provided, you will be prompted to enter both. 91 | 92 | ### Example 93 | 94 | ```bash 95 | easygit add-alias fp first-push 96 | ``` 97 | 98 | ## Config Command 99 | 100 | The `config` command returns the global Git username and email if they are set. If they are not available, it prompts the user to enter them and sets the global configuration accordingly. 101 | 102 | ### Syntax 103 | 104 | ```bash 105 | easygit config 106 | ``` 107 | 108 | ## Revert Command 109 | 110 | The revert command reverts the local Git repository to the previous commit, and force pushes the changes to the remote repository on GitHub.This command finds the commit hash of the previous commit, resets the local repository to that commit, and force pushes the changes to the remote repository on GitHub. `Be careful when using this command, as it will overwrite the remote repository with your local repository. This can cause issues if other people are also working on the repository.` 111 | 112 | ### Syntax 113 | 114 | ```bash 115 | easygit revert 116 | ``` 117 | 118 | ## Contributing 119 | 120 | Contributions are welcome! Please follow these steps to contribute: 121 | 122 | - Fork the repository. 123 | - Create a new branch with a descriptive name. 124 | - Make your changes and commit them with a clear message. 125 | - Push your changes to your fork. 126 | - Submit a pull request. 127 | 128 | or 129 | 130 | - Create an issue 131 | 132 | ## License 133 | 134 | The Git CLI is licensed under the MIT License. See the [MIT License](https://opensource.org/licenses/MIT "MIT License") file for more information. 135 | 136 | ## Contact 137 | 138 | If you have any questions or feedback, please feel free to contact me at okekedavid1333@gmail.com or send a dm on linkedIn: https://www.linkedin.com/in/obinna-okeke-0a7445173/. 139 | -------------------------------------------------------------------------------- /utils/checkVersion.js: -------------------------------------------------------------------------------- 1 | import { execSync } from "child_process"; 2 | import ora from "ora"; 3 | import chalk from "chalk"; 4 | 5 | // Function to get the installed version of a package 6 | const getInstalledVersion = (packageName) => { 7 | try { 8 | // Executes npm list command to get the installed version of the package 9 | const installedVersion = execSync( 10 | `npm list -g ${packageName} --depth=0` 11 | ).toString(); 12 | // Extracts the version number from the command output using regex 13 | const versionMatch = installedVersion.match( 14 | new RegExp(`${packageName}@([\\d.]+)`) 15 | ); 16 | // Returns the version number if matched, else returns null 17 | return versionMatch ? versionMatch[1] : null; 18 | } catch (error) { 19 | // Logs an error message if there is an issue fetching the installed version 20 | console.error(`Error fetching installed version: ${error.message}`); 21 | return null; 22 | } 23 | }; 24 | 25 | // Function to get the latest version of a package 26 | const getLatestVersion = (packageName, timeout = 5000) => { 27 | const spinner = ora("Checking Latest Version").start(); 28 | try { 29 | const latestVersion = execSync(`npm view -g ${packageName} version`, { 30 | timeout, 31 | }) 32 | .toString() 33 | .trim(); 34 | spinner.stop(); 35 | return latestVersion; 36 | // Returns the latest version number 37 | } catch (error) { 38 | spinner.stop(); 39 | return null; 40 | } 41 | }; 42 | 43 | // Function to update the version of the package if necessary 44 | export const updateVersion = () => { 45 | const packageName = "easygit-cli"; 46 | const installedVersion = getInstalledVersion(packageName); 47 | const latestVersion = getLatestVersion(packageName); 48 | 49 | // Checks if both installed and latest versions are retrieved 50 | if (installedVersion && latestVersion) { 51 | // Compares the installed and latest versions 52 | if (installedVersion !== latestVersion) { 53 | try { 54 | // Creates and starts a spinner for the update process 55 | const spinner = ora("Updating easygit-cli version").start(); 56 | // Executes npm update command to update the package 57 | execSync(`npm update -g ${packageName}`).toString(); 58 | // Stops the spinner with a success message 59 | spinner.succeed(chalk.green("Updated Easygit")); 60 | } catch (err) { 61 | // Logs an error message if there is an issue during the update process 62 | console.error( 63 | "Error updating package, Please check your network connection and try again." 64 | ); 65 | return; 66 | } 67 | } 68 | } 69 | }; 70 | -------------------------------------------------------------------------------- /utils/config.json: -------------------------------------------------------------------------------- 1 | {"showResetMessage":true,"aliases":[{"command":"first-push","alias":"fp","desc":"(alias) for first-push command"}]} -------------------------------------------------------------------------------- /utils/git.js: -------------------------------------------------------------------------------- 1 | import simpleGit from 'simple-git'; 2 | 3 | const git = simpleGit(); 4 | 5 | export default git; -------------------------------------------------------------------------------- /utils/globalConfig.js: -------------------------------------------------------------------------------- 1 | import { spawn, exec } from "child_process"; // Required for checking global config 2 | 3 | export const setGlobalConfig = async (key, value) => { 4 | return new Promise((resolve, reject) => { 5 | const process = spawn("git", ["config", "--global", key, value]); 6 | process.on("close", (code) => { 7 | if (code === 0) { 8 | resolve(); 9 | } else { 10 | reject(new Error(`Failed to set ${key} in global Git configuration`)); 11 | } 12 | }); 13 | }); 14 | }; 15 | 16 | export const getGlobalConfig = (key) => { 17 | return new Promise((resolve, reject) => { 18 | exec(`git config ${key}`, (err, stdout, stderr) => { 19 | if (err) { 20 | reject(err); 21 | return; 22 | } 23 | resolve(stdout.trim()); 24 | }); 25 | }); 26 | }; -------------------------------------------------------------------------------- /utils/helpers.js: -------------------------------------------------------------------------------- 1 | import { program } from "commander"; 2 | import inquirer from "inquirer"; 3 | import simpleGit from "simple-git"; 4 | import { GoogleGenerativeAI } from "@google/generative-ai"; 5 | import dotenv from "dotenv"; 6 | dotenv.config(); 7 | 8 | const genAI = new GoogleGenerativeAI(process.env.GOOGLE_API_KEY); 9 | const model = genAI.getGenerativeModel({ model: "gemini-pro" }); 10 | 11 | // Initialize simple-git 12 | const git = simpleGit(); 13 | 14 | export const generateCommitMessage = async()=>{ 15 | const diff = await git.diff(); 16 | const status = await git.status(); 17 | const newFiles = status.not_added; 18 | let prompt; 19 | if (diff || newFiles.length) { 20 | prompt = diff 21 | ? newFiles.length 22 | ? `generate a commit message for this diff:${diff} and these new files:${newFiles}. Your response should be an object with the keys of subject and body only` //prompt for diff and newFiles 23 | : `generate a commit message for this diff:${diff}. Your response should be an object with the keys of subject and body only` //prompt for diff alone 24 | : `generate a commit message for these new files:${newFiles}your response should be an object with the keys of subject and body only`; //prompt for newFiles alone 25 | model 26 | .generateContent(prompt) 27 | .then((result) => { 28 | const response = JSON.parse(result.response.text().replace('```', '').replace('json', '').replace('```', '')); // Assuming JSON format 29 | const subject = response.subject; 30 | const body = response.body; 31 | const commitMessage = { 32 | subject: subject, 33 | body: body, 34 | }; 35 | return commitMessage 36 | }) 37 | .catch((err) => { 38 | console.error("Error generating commit message:", err); 39 | // console.error("Prompt used:", prompt); 40 | }); 41 | } 42 | else { 43 | return console.log("there are no changes to be commited"); 44 | } 45 | 46 | 47 | } -------------------------------------------------------------------------------- /utils/reservedNames.js: -------------------------------------------------------------------------------- 1 | // Import required modules 2 | import fs from "node:fs"; // Node's file system module 3 | import path from "node:path"; // Node's path module 4 | import globalModules from "global-modules"; // Global modules directory 5 | 6 | // Define the path to the config file 7 | // We're storing our config file in the global-modules directory 8 | const configFile = path.join(globalModules, "easygit-cli/utils/config.json"); 9 | 10 | // Read the config file and parse its contents 11 | const getConfig = () => { 12 | try { 13 | // Read the file synchronously and return its contents as a string 14 | const data = fs.readFileSync(configFile, "utf-8"); 15 | // Parse the JSON data and return the resulting object 16 | return JSON.parse(data); 17 | } catch (error) { 18 | // If there's an error reading the file, log it to the console 19 | console.error(`Error reading config file: ${error.message}`); 20 | } 21 | }; 22 | 23 | // Define an array of reserved command names 24 | export const commands = ["push", "first-push", "config", "revert"]; 25 | 26 | // Initialize an array to store reserved names 27 | let reservedNames = [...commands]; 28 | 29 | // Read the config file and extract aliases 30 | const config = getConfig(); 31 | if (config && config.aliases) { 32 | // Add each alias to the reservedNames array 33 | config.aliases.forEach((alias) => { 34 | reservedNames.push(alias.alias); 35 | }); 36 | } 37 | 38 | // Export the reservedNames array as the default export 39 | export default reservedNames; --------------------------------------------------------------------------------