UNPKG

8.85 kBJavaScriptView Raw
1const { execSync } = require('child_process');
2const fs = require('fs-extra');
3const path = require('path');
4
5const CLiError = require('@src/exceptions/cli-error');
6const Messenger = require('@src/view/messenger');
7
8/**
9 * Class for git commands management
10 */
11module.exports = class GitClient {
12 /**
13 * Constructor for GitClient with projectPath and verbosity options
14 * @param {string} projectPath the path of a project
15 * @param {object} verbosityOptions | showOutput
16 * | showCommand
17 */
18 constructor(projectPath, verbosityOptions) {
19 this.projectPath = projectPath;
20 this.verbosityOptions = verbosityOptions;
21 }
22
23 /**
24 * Execute git-init to create an empty Git repository or reinitialize an existing one
25 */
26 init() {
27 const commands = [`git init "${this.projectPath}"${this.verbosityOptions.showOutput === false ? ' --quiet' : ''}`];
28 const options = {
29 showStdOut: this.verbosityOptions.showOutput,
30 showStdErr: true,
31 showCmd: this.verbosityOptions.showCommand
32 };
33 this._execCommands(commands, options);
34 }
35
36 /**
37 * Config local git credential helper with credentialHelperPath
38 * @param {string} credentialHelperPath the path of git credential helper
39 */
40 configureCredentialHelper(credentialHelperPath) {
41 const commands = [
42 'git config --local credential.helper ""',
43 `git config --local --add credential.helper "!${credentialHelperPath}"`,
44 'git config --local credential.UseHttpPath true'];
45 const options = {
46 showStdOut: this.verbosityOptions.showOutput,
47 showStdErr: true,
48 showCmd: this.verbosityOptions.showCommand,
49 workingDir: this.projectPath
50 };
51 this._execCommands(commands, options);
52 }
53
54 /**
55 * Replace local git credential helper with credentialHelperPath
56 * @param {string} credentialHelperPath the path of git credential helper
57 */
58 updateCredentialHelper(credentialHelperPath) {
59 const commands = [
60 `git config --local --replace-all credential.helper "!${credentialHelperPath}"`,
61 'git config --local credential.UseHttpPath true'];
62 const options = {
63 showStdOut: this.verbosityOptions.showOutput,
64 showStdErr: true,
65 showCmd: this.verbosityOptions.showCommand,
66 workingDir: this.projectPath
67 };
68 this._execCommands(commands, options);
69 }
70
71 /**
72 * Add a new remote in the directory your repository is stored at
73 * @param {String} repoUrl a remote url
74 */
75 addOrigin(repoUrl) {
76 const commands = [`git remote add origin ${repoUrl}`];
77 const options = {
78 showStdOut: this.verbosityOptions.showOutput,
79 showStdErr: true,
80 showCmd: this.verbosityOptions.showCommand,
81 workingDir: this.projectPath
82 };
83 this._execCommands(commands, options);
84 }
85
86 /**
87 * Execute git fetch to fetch all remotes
88 */
89 fetchAll() {
90 const commands = [`git fetch --all${this.verbosityOptions.showOutput === false ? ' --quiet' : ''}`];
91 const options = {
92 showStdOut: this.verbosityOptions.showOutput,
93 showStdErr: false,
94 showCmd: this.verbosityOptions.showCommand,
95 workingDir: this.projectPath
96 };
97 this._execCommands(commands, options);
98 }
99
100 /**
101 * Execute git checkout to switch branch or restore working tree files
102 * @param {string} branch the branch name
103 */
104 checkoutBranch(branch) {
105 const commands = [`git checkout ${branch}${this.verbosityOptions.showOutput === false ? ' --quiet' : ''}`];
106 const options = {
107 showStdOut: this.verbosityOptions.showOutput,
108 showStdErr: true,
109 showCmd: this.verbosityOptions.showCommand,
110 workingDir: this.projectPath
111 };
112 this._execCommands(commands, options);
113 }
114
115 /**
116 * Execute git clone to clone a repository into a new directory
117 * @param {string} cloneUrl the clone url
118 * @param {string} branch the branch name
119 * @param {string} folderName the directory folder name
120 */
121 clone(cloneUrl, branch, folderName) {
122 const commands = [`git clone --branch ${branch} ${cloneUrl} ${folderName} ${this.verbosityOptions.showOutput === false ? ' --quiet' : ''}`];
123 const options = {
124 showStdOut: this.verbosityOptions.showOutput,
125 showStdErr: true,
126 showCmd: this.verbosityOptions.showCommand
127 };
128 this._execCommands(commands, options);
129 }
130
131 /**
132 * Set up or update a gitignore file
133 * @param {Array} filesToIgnore the list of files to ignore for git
134 */
135 setupGitIgnore(filesToIgnore) {
136 const gitignorePath = path.join(this.projectPath, '.gitignore');
137 if (fs.existsSync(gitignorePath) === false) {
138 fs.writeFileSync(gitignorePath, `${filesToIgnore.join('\n')}`);
139 } else {
140 const gitignoreFile = fs.readFileSync(gitignorePath).toString();
141 filesToIgnore.forEach((file) => {
142 if (gitignoreFile.indexOf(file) === -1) {
143 fs.appendFileSync(gitignorePath, `\n${file}`);
144 }
145 });
146 }
147 this.add('.gitignore');
148 }
149
150 /**
151 * Execute git add to add file contents to the index
152 * @param {string} file the file to add content from
153 */
154 add(file) {
155 const commands = [`git add "${file}"`];
156 const options = {
157 showStdOut: this.verbosityOptions.showOutput,
158 showStdErr: true,
159 showCmd: this.verbosityOptions.showCommand,
160 workingDir: this.projectPath
161 };
162 this._execCommands(commands, options);
163 }
164
165 /**
166 * Execute git delete to delete a local branch
167 * @param {string} file the file to add content from
168 */
169 deleteBranch(branch) {
170 const commands = [`git branch -d ${branch}`];
171 const options = {
172 showStdOut: this.verbosityOptions.showOutput,
173 showStdErr: true,
174 showCmd: this.verbosityOptions.showCommand
175 };
176 this._execCommands(commands, options);
177 }
178
179 /**
180 * Execute git merge to join two development histories together
181 * @param {String} branch the development branch
182 */
183 merge(branch) {
184 const commands = [`git merge ${branch}`];
185 const options = {
186 showStdOut: this.verbosityOptions.showOutput,
187 showStdErr: true,
188 showCmd: this.verbosityOptions.showCommand
189 };
190 this._execCommands(commands, options);
191 }
192
193 /**
194 * Execute git status to show the working tree status in short-format
195 * @param {callback} callback { error }
196 */
197 shortStatus() {
198 const command = 'git status -s';
199 const options = {
200 showStdOut: this.verbosityOptions.showOutput,
201 showStdErr: true,
202 showCmd: this.verbosityOptions.showCommand
203 };
204 try {
205 return this._execChildProcessSync(command, options);
206 } catch (ex) {
207 throw new CLiError(`${ex}`);
208 }
209 }
210
211 /**
212 * Execute git rev-list to count the list of commit objects
213 * @param {String} commit1 the first commit
214 * @param {String} commit2 the second commit
215 * @param {callback} callback { error }
216 */
217 countCommitDifference(commit1, commit2) {
218 const command = `git rev-list --count ${commit1}...${commit2}`;
219 const options = {
220 showStdOut: this.verbosityOptions.showOutput,
221 showStdErr: true,
222 showCmd: this.verbosityOptions.showCommand,
223 workingDir: this.projectPath
224 };
225 try {
226 return this._execChildProcessSync(command, options);
227 } catch (ex) {
228 throw new CLiError(`${ex}`);
229 }
230 }
231
232 _execCommands(commands, options) {
233 for (const command of commands) {
234 try {
235 this._execChildProcessSync(command, options);
236 } catch (ex) {
237 throw new CLiError(`${ex}`);
238 }
239 }
240 }
241
242 _execChildProcessSync(command, options) {
243 const { showOutput, showStdErr, showCommand, workingDir } = options;
244 const execOptions = {
245 stdio: [null, showOutput ? 1 : null, showStdErr ? 2 : null],
246 shell: true,
247 windowsHide: true
248 };
249 if (workingDir) {
250 execOptions.cwd = options.workingDir;
251 }
252 if (showCommand) {
253 Messenger.getInstance().info(command);
254 }
255 return execSync(command, execOptions);
256 }
257};