gent-cli 1.5.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -33
- package/package.json +1 -1
- package/src/commands/clone.js +133 -0
- package/src/commands/commit.js +45 -3
- package/src/commands/create.js +118 -0
- package/src/commands/init.js +33 -5
- package/src/commands/list.js +67 -0
- package/src/commands/pull.js +123 -0
- package/src/commands/push.js +112 -0
- package/src/commands/remote.js +133 -0
- package/src/index.js +49 -0
- package/src/services/repo-service.js +283 -0
- package/src/utils/cloud-sync.js +316 -0
- package/src/utils/constants.js +28 -1
- package/src/utils/diff.js +121 -0
- package/src/utils/fileSystem.js +46 -2
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pull Command - Pull commits from cloud to local repository
|
|
3
|
+
* Downloads commits, trees, and blobs from the cloud
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const chalk = require('chalk');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const ora = require('ora');
|
|
9
|
+
const { pathExists, readJSON, writeJSON } = require('../utils/fileSystem');
|
|
10
|
+
const { GENT_DIR, COMMITS_FILE } = require('../utils/constants');
|
|
11
|
+
const { getRemote, syncCommitsFromCloud } = require('../utils/cloud-sync');
|
|
12
|
+
const authStorage = require('../utils/auth-storage');
|
|
13
|
+
const repoService = require('../services/repo-service');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Pull commits from cloud to local
|
|
17
|
+
* @param {string} remoteName - Remote name (default: origin)
|
|
18
|
+
* @param {string} branchName - Branch name (optional)
|
|
19
|
+
* @param {Object} options - Command options
|
|
20
|
+
*/
|
|
21
|
+
async function pull(remoteName, branchName, options) {
|
|
22
|
+
try {
|
|
23
|
+
const cwd = process.cwd();
|
|
24
|
+
const gentPath = path.join(cwd, GENT_DIR);
|
|
25
|
+
|
|
26
|
+
// Check if in a gent repository
|
|
27
|
+
if (!(await pathExists(gentPath))) {
|
|
28
|
+
console.error(chalk.red('Error: Not a gent repository'));
|
|
29
|
+
console.log(chalk.yellow('Run'), chalk.cyan('gent init'), chalk.yellow('to initialize a repository'));
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Check authentication
|
|
34
|
+
const user = await authStorage.getUser();
|
|
35
|
+
if (!user) {
|
|
36
|
+
console.error(chalk.red('Error: You must be logged in to pull'));
|
|
37
|
+
console.log(chalk.yellow('Run'), chalk.cyan('gent login'), chalk.yellow('to authenticate'));
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Default to 'origin' if no remote specified
|
|
42
|
+
remoteName = remoteName || 'origin';
|
|
43
|
+
|
|
44
|
+
// Get remote configuration
|
|
45
|
+
const remote = await getRemote(remoteName, cwd);
|
|
46
|
+
if (!remote) {
|
|
47
|
+
console.error(chalk.red(`Error: Remote '${remoteName}' not found`));
|
|
48
|
+
console.log(chalk.yellow('Add a remote with:'), chalk.cyan('gent remote add origin <owner_id>/<repo_name>'));
|
|
49
|
+
console.log(chalk.yellow('Or list remotes with:'), chalk.cyan('gent remote'));
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Get current branch if not specified
|
|
54
|
+
const commitsData = await readJSON(path.join(gentPath, COMMITS_FILE));
|
|
55
|
+
branchName = branchName || commitsData.currentBranch;
|
|
56
|
+
|
|
57
|
+
if (!branchName) {
|
|
58
|
+
console.error(chalk.red('Error: No branch specified and no current branch found'));
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
console.log(chalk.cyan(`Pulling from ${remoteName}/${branchName}...\n`));
|
|
63
|
+
|
|
64
|
+
// Verify repository exists
|
|
65
|
+
const spinner = ora('Verifying remote repository...').start();
|
|
66
|
+
try {
|
|
67
|
+
await repoService.getRepository(remote.owner_id, remote.repo_name);
|
|
68
|
+
spinner.succeed('Remote repository verified');
|
|
69
|
+
} catch (error) {
|
|
70
|
+
spinner.fail('Remote repository not found or access denied');
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Pull commits
|
|
75
|
+
const cloudCommits = await syncCommitsFromCloud(remote.owner_id, remote.repo_name, cwd);
|
|
76
|
+
|
|
77
|
+
// Update local commits file with cloud commits
|
|
78
|
+
const localCommits = commitsData.commits || [];
|
|
79
|
+
const mergedCommits = [...localCommits];
|
|
80
|
+
|
|
81
|
+
// Add cloud commits that don't exist locally
|
|
82
|
+
for (const cloudCommit of cloudCommits) {
|
|
83
|
+
const exists = localCommits.find(c => c.sha === cloudCommit.sha);
|
|
84
|
+
if (!exists) {
|
|
85
|
+
// Convert cloud commit format to local format
|
|
86
|
+
mergedCommits.push({
|
|
87
|
+
sha: cloudCommit.sha,
|
|
88
|
+
message: cloudCommit.message,
|
|
89
|
+
author: {
|
|
90
|
+
name: cloudCommit.author_name,
|
|
91
|
+
email: cloudCommit.author_email
|
|
92
|
+
},
|
|
93
|
+
timestamp: cloudCommit.committed_at,
|
|
94
|
+
parent: cloudCommit.parent_shas || [],
|
|
95
|
+
files: {} // Files are stored in objects directory
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Update branch pointer if we have cloud commits
|
|
101
|
+
if (cloudCommits.length > 0) {
|
|
102
|
+
const latestCloudCommit = cloudCommits[cloudCommits.length - 1];
|
|
103
|
+
commitsData.branches[branchName] = latestCloudCommit.sha;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
commitsData.commits = mergedCommits;
|
|
107
|
+
|
|
108
|
+
await writeJSON(path.join(gentPath, COMMITS_FILE), commitsData);
|
|
109
|
+
|
|
110
|
+
if (cloudCommits.length > 0) {
|
|
111
|
+
console.log(chalk.green(`\n✓ Successfully pulled ${cloudCommits.length} commit(s) from ${remoteName}/${branchName}`));
|
|
112
|
+
} else {
|
|
113
|
+
console.log(chalk.yellow('Already up to date'));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
} catch (error) {
|
|
117
|
+
console.error(chalk.red('Failed to pull'));
|
|
118
|
+
console.error(chalk.red('Error:'), error.message);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
module.exports = pull;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Push Command - Push local commits to cloud repository
|
|
3
|
+
* Uploads commits, trees, and blobs to the cloud
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const chalk = require('chalk');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const ora = require('ora');
|
|
9
|
+
const { pathExists, readJSON } = require('../utils/fileSystem');
|
|
10
|
+
const { GENT_DIR, COMMITS_FILE } = require('../utils/constants');
|
|
11
|
+
const { getRemote, syncCommitsToCloud } = require('../utils/cloud-sync');
|
|
12
|
+
const authStorage = require('../utils/auth-storage');
|
|
13
|
+
const repoService = require('../services/repo-service');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Push local commits to cloud
|
|
17
|
+
* @param {string} remoteName - Remote name (default: origin)
|
|
18
|
+
* @param {string} branchName - Branch name (optional)
|
|
19
|
+
* @param {Object} options - Command options
|
|
20
|
+
*/
|
|
21
|
+
async function push(remoteName, branchName, options) {
|
|
22
|
+
try {
|
|
23
|
+
const cwd = process.cwd();
|
|
24
|
+
const gentPath = path.join(cwd, GENT_DIR);
|
|
25
|
+
|
|
26
|
+
// Check if in a gent repository
|
|
27
|
+
if (!(await pathExists(gentPath))) {
|
|
28
|
+
console.error(chalk.red('Error: Not a gent repository'));
|
|
29
|
+
console.log(chalk.yellow('Run'), chalk.cyan('gent init'), chalk.yellow('to initialize a repository'));
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Check authentication
|
|
34
|
+
const user = await authStorage.getUser();
|
|
35
|
+
if (!user) {
|
|
36
|
+
console.error(chalk.red('Error: You must be logged in to push'));
|
|
37
|
+
console.log(chalk.yellow('Run'), chalk.cyan('gent login'), chalk.yellow('to authenticate'));
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Default to 'origin' if no remote specified
|
|
42
|
+
remoteName = remoteName || 'origin';
|
|
43
|
+
|
|
44
|
+
// Get remote configuration
|
|
45
|
+
const remote = await getRemote(remoteName, cwd);
|
|
46
|
+
if (!remote) {
|
|
47
|
+
console.error(chalk.red(`Error: Remote '${remoteName}' not found`));
|
|
48
|
+
console.log(chalk.yellow('Add a remote with:'), chalk.cyan('gent remote add origin <owner_id>/<repo_name>'));
|
|
49
|
+
console.log(chalk.yellow('Or list remotes with:'), chalk.cyan('gent remote'));
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Get current branch if not specified
|
|
54
|
+
const commitsData = await readJSON(path.join(gentPath, COMMITS_FILE));
|
|
55
|
+
branchName = branchName || commitsData.currentBranch;
|
|
56
|
+
|
|
57
|
+
if (!branchName) {
|
|
58
|
+
console.error(chalk.red('Error: No branch specified and no current branch found'));
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Get commits for the branch
|
|
63
|
+
const commits = commitsData.commits || [];
|
|
64
|
+
const branchCommitSha = commitsData.branches[branchName];
|
|
65
|
+
|
|
66
|
+
if (!branchCommitSha) {
|
|
67
|
+
console.error(chalk.red(`Error: Branch '${branchName}' not found`));
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Get commits to push (find all commits in the branch)
|
|
72
|
+
const commitsToPush = [];
|
|
73
|
+
let currentSha = branchCommitSha;
|
|
74
|
+
|
|
75
|
+
while (currentSha) {
|
|
76
|
+
const commit = commits.find(c => c.sha === currentSha);
|
|
77
|
+
if (!commit) break;
|
|
78
|
+
|
|
79
|
+
commitsToPush.unshift(commit); // Add to beginning to maintain order
|
|
80
|
+
currentSha = commit.parent ? commit.parent[0] : null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (commitsToPush.length === 0) {
|
|
84
|
+
console.log(chalk.yellow('Nothing to push'));
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
console.log(chalk.cyan(`Pushing ${commitsToPush.length} commit(s) to ${remoteName}/${branchName}...`));
|
|
89
|
+
|
|
90
|
+
// Verify repository exists
|
|
91
|
+
const spinner = ora('Verifying remote repository...').start();
|
|
92
|
+
try {
|
|
93
|
+
await repoService.getRepository(remote.owner_id, remote.repo_name);
|
|
94
|
+
spinner.succeed('Remote repository verified');
|
|
95
|
+
} catch (error) {
|
|
96
|
+
spinner.fail('Remote repository not found or access denied');
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Push commits
|
|
101
|
+
await syncCommitsToCloud(commitsToPush, remote.owner_id, remote.repo_name, branchName, cwd);
|
|
102
|
+
|
|
103
|
+
console.log(chalk.green(`\n✓ Successfully pushed to ${remoteName}/${branchName}`));
|
|
104
|
+
|
|
105
|
+
} catch (error) {
|
|
106
|
+
console.error(chalk.red('Failed to push'));
|
|
107
|
+
console.error(chalk.red('Error:'), error.message);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
module.exports = push;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote Command - Manage remote repositories
|
|
3
|
+
* Add, remove, and list remote repository configurations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const chalk = require('chalk');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const ora = require('ora');
|
|
9
|
+
const { getRemoteConfig, addRemote, removeRemote } = require('../utils/cloud-sync');
|
|
10
|
+
const { GENT_DIR } = require('../utils/constants');
|
|
11
|
+
const { pathExists } = require('../utils/fileSystem');
|
|
12
|
+
const repoService = require('../services/repo-service');
|
|
13
|
+
const authStorage = require('../utils/auth-storage');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Manage remote repositories
|
|
17
|
+
* @param {string} action - Action to perform (add, remove, or list)
|
|
18
|
+
* @param {string} name - Remote name
|
|
19
|
+
* @param {string} url - Remote URL (owner_id/repo_name)
|
|
20
|
+
* @param {Object} options - Command options
|
|
21
|
+
*/
|
|
22
|
+
async function remote(action, name, url, options) {
|
|
23
|
+
try {
|
|
24
|
+
const cwd = process.cwd();
|
|
25
|
+
const gentPath = path.join(cwd, GENT_DIR);
|
|
26
|
+
|
|
27
|
+
// Check if in a gent repository
|
|
28
|
+
if (!(await pathExists(gentPath))) {
|
|
29
|
+
console.error(chalk.red('Error: Not a gent repository'));
|
|
30
|
+
console.log(chalk.yellow('Run'), chalk.cyan('gent init'), chalk.yellow('to initialize a repository'));
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// List remotes (default action)
|
|
35
|
+
if (!action || action === 'list' || options.verbose) {
|
|
36
|
+
const config = await getRemoteConfig(cwd);
|
|
37
|
+
const remotes = Object.keys(config.remotes || {});
|
|
38
|
+
|
|
39
|
+
if (remotes.length === 0) {
|
|
40
|
+
console.log(chalk.yellow('No remotes configured'));
|
|
41
|
+
console.log(chalk.gray('Add a remote with:'), chalk.cyan('gent remote add <name> <owner_id>/<repo_name>'));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
console.log(chalk.cyan('Configured remotes:\n'));
|
|
46
|
+
for (const remoteName of remotes) {
|
|
47
|
+
const remote = config.remotes[remoteName];
|
|
48
|
+
if (options.verbose) {
|
|
49
|
+
console.log(chalk.bold(remoteName));
|
|
50
|
+
console.log(chalk.gray(` Owner ID: ${remote.owner_id}`));
|
|
51
|
+
console.log(chalk.gray(` Repository: ${remote.repo_name}`));
|
|
52
|
+
console.log(chalk.gray(` URL: ${remote.owner_id}/${remote.repo_name}\n`));
|
|
53
|
+
} else {
|
|
54
|
+
console.log(`${remoteName}\t${remote.owner_id}/${remote.repo_name}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Add remote
|
|
61
|
+
if (action === 'add') {
|
|
62
|
+
if (!name || !url) {
|
|
63
|
+
console.error(chalk.red('Error: Remote name and URL are required'));
|
|
64
|
+
console.log(chalk.yellow('Usage:'), chalk.cyan('gent remote add <name> <owner_id>/<repo_name>'));
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Parse owner_id/repo_name
|
|
69
|
+
const parts = url.split('/');
|
|
70
|
+
if (parts.length !== 2) {
|
|
71
|
+
console.error(chalk.red('Error: Invalid remote URL format'));
|
|
72
|
+
console.log(chalk.yellow('Expected:'), chalk.cyan('<owner_id>/<repo_name>'));
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const ownerId = parseInt(parts[0]);
|
|
77
|
+
const repoName = parts[1];
|
|
78
|
+
|
|
79
|
+
if (isNaN(ownerId)) {
|
|
80
|
+
console.error(chalk.red('Error: Owner ID must be a number'));
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Check authentication
|
|
85
|
+
const user = await authStorage.getUser();
|
|
86
|
+
if (!user) {
|
|
87
|
+
console.error(chalk.red('Error: You must be logged in to add a remote'));
|
|
88
|
+
console.log(chalk.yellow('Run'), chalk.cyan('gent login'), chalk.yellow('to authenticate'));
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Verify repository exists
|
|
93
|
+
const spinner = ora('Verifying repository...').start();
|
|
94
|
+
try {
|
|
95
|
+
await repoService.getRepository(ownerId, repoName);
|
|
96
|
+
spinner.succeed('Repository verified');
|
|
97
|
+
} catch (error) {
|
|
98
|
+
spinner.fail('Repository not found or access denied');
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Add remote
|
|
103
|
+
await addRemote(name, ownerId, repoName, cwd);
|
|
104
|
+
console.log(chalk.green(`✓ Remote '${name}' added: ${ownerId}/${repoName}`));
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Remove remote
|
|
109
|
+
if (action === 'remove' || action === 'rm') {
|
|
110
|
+
if (!name) {
|
|
111
|
+
console.error(chalk.red('Error: Remote name is required'));
|
|
112
|
+
console.log(chalk.yellow('Usage:'), chalk.cyan('gent remote remove <name>'));
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
await removeRemote(name, cwd);
|
|
117
|
+
console.log(chalk.green(`✓ Remote '${name}' removed`));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Unknown action
|
|
122
|
+
console.error(chalk.red(`Error: Unknown action '${action}'`));
|
|
123
|
+
console.log(chalk.yellow('Available actions:'), chalk.cyan('add, remove, list'));
|
|
124
|
+
process.exit(1);
|
|
125
|
+
|
|
126
|
+
} catch (error) {
|
|
127
|
+
console.error(chalk.red('Failed to manage remote'));
|
|
128
|
+
console.error(chalk.red('Error:'), error.message);
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
module.exports = remote;
|
package/src/index.js
CHANGED
|
@@ -27,6 +27,14 @@ const loginCommand = require('./commands/login');
|
|
|
27
27
|
const logoutCommand = require('./commands/logout');
|
|
28
28
|
const whoamiCommand = require('./commands/whoami');
|
|
29
29
|
|
|
30
|
+
// Import cloud repository commands
|
|
31
|
+
const createCommand = require('./commands/create');
|
|
32
|
+
const listCommand = require('./commands/list');
|
|
33
|
+
const cloneCommand = require('./commands/clone');
|
|
34
|
+
const pushCommand = require('./commands/push');
|
|
35
|
+
const pullCommand = require('./commands/pull');
|
|
36
|
+
const remoteCommand = require('./commands/remote');
|
|
37
|
+
|
|
30
38
|
// Configure CLI
|
|
31
39
|
program
|
|
32
40
|
.name('gent')
|
|
@@ -38,6 +46,9 @@ program
|
|
|
38
46
|
.command('init')
|
|
39
47
|
.description('Initialize a new gent repository')
|
|
40
48
|
.option('-y, --yes', 'Skip prompts and use defaults')
|
|
49
|
+
.option('--cloud', 'Create a corresponding cloud repository')
|
|
50
|
+
.option('-d, --description <description>', 'Repository description (for cloud)')
|
|
51
|
+
.option('-p, --private', 'Make cloud repository private')
|
|
41
52
|
.action(initCommand);
|
|
42
53
|
|
|
43
54
|
program
|
|
@@ -57,6 +68,7 @@ program
|
|
|
57
68
|
.description('Record changes to the repository')
|
|
58
69
|
.option('-m, --message <message>', 'Commit message')
|
|
59
70
|
.option('-a, --all', 'Automatically stage all modified files')
|
|
71
|
+
.option('--push', 'Push to remote after commit')
|
|
60
72
|
.action(commitCommand);
|
|
61
73
|
|
|
62
74
|
program
|
|
@@ -103,6 +115,43 @@ program
|
|
|
103
115
|
.description('Display current user information')
|
|
104
116
|
.action(whoamiCommand);
|
|
105
117
|
|
|
118
|
+
// Cloud repository commands
|
|
119
|
+
program
|
|
120
|
+
.command('create <repo-name>')
|
|
121
|
+
.description('Create a new cloud repository')
|
|
122
|
+
.option('-d, --description <description>', 'Repository description')
|
|
123
|
+
.option('-p, --private', 'Make repository private')
|
|
124
|
+
.option('-y, --yes', 'Skip prompts and use defaults')
|
|
125
|
+
.option('--init-local', 'Initialize local repository and link remote')
|
|
126
|
+
.action(createCommand);
|
|
127
|
+
|
|
128
|
+
program
|
|
129
|
+
.command('list')
|
|
130
|
+
.alias('ls')
|
|
131
|
+
.description('List all your cloud repositories')
|
|
132
|
+
.action(listCommand);
|
|
133
|
+
|
|
134
|
+
program
|
|
135
|
+
.command('clone <repo-url> [directory]')
|
|
136
|
+
.description('Clone a cloud repository (format: owner_id/repo_name)')
|
|
137
|
+
.action(cloneCommand);
|
|
138
|
+
|
|
139
|
+
program
|
|
140
|
+
.command('push [remote] [branch]')
|
|
141
|
+
.description('Push local commits to cloud')
|
|
142
|
+
.action(pushCommand);
|
|
143
|
+
|
|
144
|
+
program
|
|
145
|
+
.command('pull [remote] [branch]')
|
|
146
|
+
.description('Pull commits from cloud to local')
|
|
147
|
+
.action(pullCommand);
|
|
148
|
+
|
|
149
|
+
program
|
|
150
|
+
.command('remote [action] [name] [url]')
|
|
151
|
+
.description('Manage remote repositories')
|
|
152
|
+
.option('-v, --verbose', 'Show verbose output')
|
|
153
|
+
.action(remoteCommand);
|
|
154
|
+
|
|
106
155
|
// Help command
|
|
107
156
|
program
|
|
108
157
|
.command('help [command]')
|