gent-cli 2.0.0 → 5.0.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 +28 -42
- package/package.json +1 -1
- package/src/commands/add.js +102 -23
- package/src/commands/clone.js +137 -86
- package/src/commands/commit.js +89 -81
- package/src/commands/diff.js +257 -0
- package/src/commands/init.js +5 -36
- package/src/commands/log.js +57 -13
- package/src/commands/merge.js +245 -0
- package/src/commands/pull.js +176 -86
- package/src/commands/push.js +172 -79
- package/src/commands/remote.js +97 -113
- package/src/commands/reset.js +149 -0
- package/src/commands/rm.js +85 -0
- package/src/commands/show.js +167 -0
- package/src/commands/stash.js +255 -0
- package/src/commands/status.js +80 -46
- package/src/commands/tag.js +146 -0
- package/src/index.js +108 -57
- package/src/utils/constants.js +10 -26
- package/src/utils/diff-engine.js +236 -0
- package/src/utils/fileSystem.js +8 -60
- package/src/utils/hash-engine.js +337 -0
- package/src/utils/merge-engine.js +379 -0
- package/src/utils/object-store.js +54 -0
- package/src/commands/create.js +0 -121
- package/src/commands/list.js +0 -67
- package/src/services/repo-service.js +0 -284
- package/src/utils/cloud-sync.js +0 -323
- package/src/utils/diff.js +0 -121
|
@@ -1,284 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Repository Service - Handles all cloud repository operations
|
|
3
|
-
* Provides methods for creating, managing, and syncing repositories with the cloud
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const apiClient = require('../utils/api-client');
|
|
7
|
-
const { API_ENDPOINTS } = require('../utils/constants');
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Build URL with path parameters
|
|
11
|
-
* @param {string} template - URL template with placeholders
|
|
12
|
-
* @param {Object} params - Parameters to replace
|
|
13
|
-
* @returns {string} Built URL
|
|
14
|
-
*/
|
|
15
|
-
function buildUrl(template, params = {}) {
|
|
16
|
-
let url = template;
|
|
17
|
-
Object.keys(params).forEach(key => {
|
|
18
|
-
url = url.replace(`{${key}}`, params[key]);
|
|
19
|
-
});
|
|
20
|
-
return url;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Create a new repository
|
|
25
|
-
* @param {string} name - Repository name
|
|
26
|
-
* @param {string} description - Repository description
|
|
27
|
-
* @param {boolean} isPrivate - Whether the repository is private
|
|
28
|
-
* @param {string} defaultBranch - Default branch name
|
|
29
|
-
* @returns {Promise<Object>} Created repository data
|
|
30
|
-
*/
|
|
31
|
-
async function createRepository(name, description = '', isPrivate = false, defaultBranch = 'main') {
|
|
32
|
-
try {
|
|
33
|
-
const response = await apiClient.post(API_ENDPOINTS.REPOS_CREATE, {
|
|
34
|
-
name,
|
|
35
|
-
project_name: name, // Add project_name as well for consistency
|
|
36
|
-
description,
|
|
37
|
-
is_private: isPrivate,
|
|
38
|
-
default_branch: defaultBranch
|
|
39
|
-
});
|
|
40
|
-
return response;
|
|
41
|
-
} catch (error) {
|
|
42
|
-
throw new Error(`Failed to create repository: ${error.response?.data?.message || error.message}`);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* List all repositories owned by the authenticated user
|
|
48
|
-
* @returns {Promise<Array>} List of repositories
|
|
49
|
-
*/
|
|
50
|
-
async function listRepositories() {
|
|
51
|
-
try {
|
|
52
|
-
const response = await apiClient.get(API_ENDPOINTS.REPOS_LIST);
|
|
53
|
-
return response;
|
|
54
|
-
} catch (error) {
|
|
55
|
-
throw new Error(`Failed to list repositories: ${error.response?.data?.message || error.message}`);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Get repository details
|
|
61
|
-
* @param {number} ownerId - Owner user ID
|
|
62
|
-
* @param {string} projectName - Project name
|
|
63
|
-
* @returns {Promise<Object>} Repository data
|
|
64
|
-
*/
|
|
65
|
-
async function getRepository(ownerId, projectName) {
|
|
66
|
-
try {
|
|
67
|
-
const url = buildUrl(API_ENDPOINTS.REPOS_GET, { owner_id: ownerId, project_name: projectName });
|
|
68
|
-
const response = await apiClient.get(url);
|
|
69
|
-
return response;
|
|
70
|
-
} catch (error) {
|
|
71
|
-
throw new Error(`Failed to get repository: ${error.response?.data?.message || error.message}`);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Delete a repository
|
|
77
|
-
* @param {number} ownerId - Owner user ID
|
|
78
|
-
* @param {string} projectName - Project name
|
|
79
|
-
* @returns {Promise<Object>} Delete response
|
|
80
|
-
*/
|
|
81
|
-
async function deleteRepository(ownerId, projectName) {
|
|
82
|
-
try {
|
|
83
|
-
const url = buildUrl(API_ENDPOINTS.REPOS_DELETE, { owner_id: ownerId, project_name: projectName });
|
|
84
|
-
const response = await apiClient.delete(url);
|
|
85
|
-
return response;
|
|
86
|
-
} catch (error) {
|
|
87
|
-
throw new Error(`Failed to delete repository: ${error.response?.data?.message || error.message}`);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Create a blob (file content) in the repository
|
|
93
|
-
* @param {number} ownerId - Owner user ID
|
|
94
|
-
* @param {string} projectName - Project name
|
|
95
|
-
* @param {string} content - File content
|
|
96
|
-
* @param {string} encoding - Encoding type (utf-8 or base64)
|
|
97
|
-
* @returns {Promise<Object>} Created blob data
|
|
98
|
-
*/
|
|
99
|
-
async function createBlob(ownerId, projectName, content, encoding = 'utf-8') {
|
|
100
|
-
try {
|
|
101
|
-
const url = buildUrl(API_ENDPOINTS.BLOB_CREATE, { owner_id: ownerId, project_name: projectName });
|
|
102
|
-
const response = await apiClient.post(url, {
|
|
103
|
-
content,
|
|
104
|
-
encoding
|
|
105
|
-
});
|
|
106
|
-
return response;
|
|
107
|
-
} catch (error) {
|
|
108
|
-
throw new Error(`Failed to create blob: ${error.response?.data?.message || error.message}`);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Get a blob by SHA
|
|
114
|
-
* @param {number} ownerId - Owner user ID
|
|
115
|
-
* @param {string} projectName - Project name
|
|
116
|
-
* @param {string} sha - Blob SHA
|
|
117
|
-
* @returns {Promise<Object>} Blob data
|
|
118
|
-
*/
|
|
119
|
-
async function getBlob(ownerId, projectName, sha) {
|
|
120
|
-
try {
|
|
121
|
-
const url = buildUrl(API_ENDPOINTS.BLOB_GET, { owner_id: ownerId, project_name: projectName, sha });
|
|
122
|
-
const response = await apiClient.get(url);
|
|
123
|
-
return response;
|
|
124
|
-
} catch (error) {
|
|
125
|
-
throw new Error(`Failed to get blob: ${error.response?.data?.message || error.message}`);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Create a tree (directory structure) in the repository
|
|
131
|
-
* @param {number} ownerId - Owner user ID
|
|
132
|
-
* @param {string} projectName - Project name
|
|
133
|
-
* @param {Array} entries - Tree entries
|
|
134
|
-
* @returns {Promise<Object>} Created tree data
|
|
135
|
-
*/
|
|
136
|
-
async function createTree(ownerId, projectName, entries) {
|
|
137
|
-
try {
|
|
138
|
-
const url = buildUrl(API_ENDPOINTS.TREE_CREATE, { owner_id: ownerId, project_name: projectName });
|
|
139
|
-
const response = await apiClient.post(url, {
|
|
140
|
-
entries
|
|
141
|
-
});
|
|
142
|
-
return response;
|
|
143
|
-
} catch (error) {
|
|
144
|
-
throw new Error(`Failed to create tree: ${error.response?.data?.message || error.message}`);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Get a tree by SHA
|
|
150
|
-
* @param {number} ownerId - Owner user ID
|
|
151
|
-
* @param {string} projectName - Project name
|
|
152
|
-
* @param {string} sha - Tree SHA
|
|
153
|
-
* @returns {Promise<Object>} Tree data
|
|
154
|
-
*/
|
|
155
|
-
async function getTree(ownerId, projectName, sha) {
|
|
156
|
-
try {
|
|
157
|
-
const url = buildUrl(API_ENDPOINTS.TREE_GET, { owner_id: ownerId, project_name: projectName, sha });
|
|
158
|
-
const response = await apiClient.get(url);
|
|
159
|
-
return response;
|
|
160
|
-
} catch (error) {
|
|
161
|
-
throw new Error(`Failed to get tree: ${error.response?.data?.message || error.message}`);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Create a commit in the repository
|
|
167
|
-
* @param {number} ownerId - Owner user ID
|
|
168
|
-
* @param {string} projectName - Project name
|
|
169
|
-
* @param {Object} commitData - Commit data (message, tree_sha, parent_shas, author_name, author_email, branch)
|
|
170
|
-
* @returns {Promise<Object>} Created commit data
|
|
171
|
-
*/
|
|
172
|
-
async function createCommit(ownerId, projectName, commitData) {
|
|
173
|
-
try {
|
|
174
|
-
const url = buildUrl(API_ENDPOINTS.COMMITS_CREATE, { owner_id: ownerId, project_name: projectName });
|
|
175
|
-
const response = await apiClient.post(url, commitData);
|
|
176
|
-
return response;
|
|
177
|
-
} catch (error) {
|
|
178
|
-
throw new Error(`Failed to create commit: ${error.response?.data?.message || error.message}`);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* List all commits in a repository
|
|
184
|
-
* @param {number} ownerId - Owner user ID
|
|
185
|
-
* @param {string} projectName - Project name
|
|
186
|
-
* @returns {Promise<Array>} List of commits
|
|
187
|
-
*/
|
|
188
|
-
async function listCommits(ownerId, projectName) {
|
|
189
|
-
try {
|
|
190
|
-
const url = buildUrl(API_ENDPOINTS.COMMITS_LIST, { owner_id: ownerId, project_name: projectName });
|
|
191
|
-
const response = await apiClient.get(url);
|
|
192
|
-
return response;
|
|
193
|
-
} catch (error) {
|
|
194
|
-
throw new Error(`Failed to list commits: ${error.response?.data?.message || error.message}`);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Get a commit by SHA
|
|
200
|
-
* @param {number} ownerId - Owner user ID
|
|
201
|
-
* @param {string} projectName - Project name
|
|
202
|
-
* @param {string} sha - Commit SHA
|
|
203
|
-
* @returns {Promise<Object>} Commit data
|
|
204
|
-
*/
|
|
205
|
-
async function getCommit(ownerId, projectName, sha) {
|
|
206
|
-
try {
|
|
207
|
-
const url = buildUrl(API_ENDPOINTS.COMMITS_GET, { owner_id: ownerId, project_name: projectName, sha });
|
|
208
|
-
const response = await apiClient.get(url);
|
|
209
|
-
return response;
|
|
210
|
-
} catch (error) {
|
|
211
|
-
throw new Error(`Failed to get commit: ${error.response?.data?.message || error.message}`);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Create a branch in the repository
|
|
217
|
-
* @param {number} ownerId - Owner user ID
|
|
218
|
-
* @param {string} projectName - Project name
|
|
219
|
-
* @param {string} branchName - Branch name
|
|
220
|
-
* @param {string} commitSha - Commit SHA to point the branch to
|
|
221
|
-
* @returns {Promise<Object>} Created branch data
|
|
222
|
-
*/
|
|
223
|
-
async function createBranch(ownerId, projectName, branchName, commitSha) {
|
|
224
|
-
try {
|
|
225
|
-
const url = buildUrl(API_ENDPOINTS.BRANCHES_CREATE, { owner_id: ownerId, project_name: projectName });
|
|
226
|
-
const response = await apiClient.post(url, {
|
|
227
|
-
name: branchName,
|
|
228
|
-
commit_sha: commitSha
|
|
229
|
-
});
|
|
230
|
-
return response;
|
|
231
|
-
} catch (error) {
|
|
232
|
-
throw new Error(`Failed to create branch: ${error.response?.data?.message || error.message}`);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* List all branches in a repository
|
|
238
|
-
* @param {number} ownerId - Owner user ID
|
|
239
|
-
* @param {string} projectName - Project name
|
|
240
|
-
* @returns {Promise<Array>} List of branches
|
|
241
|
-
*/
|
|
242
|
-
async function listBranches(ownerId, projectName) {
|
|
243
|
-
try {
|
|
244
|
-
const url = buildUrl(API_ENDPOINTS.BRANCHES_LIST, { owner_id: ownerId, project_name: projectName });
|
|
245
|
-
const response = await apiClient.get(url);
|
|
246
|
-
return response;
|
|
247
|
-
} catch (error) {
|
|
248
|
-
throw new Error(`Failed to list branches: ${error.response?.data?.message || error.message}`);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* Get a branch by name
|
|
254
|
-
* @param {number} ownerId - Owner user ID
|
|
255
|
-
* @param {string} projectName - Project name
|
|
256
|
-
* @param {string} branchName - Branch name
|
|
257
|
-
* @returns {Promise<Object>} Branch data
|
|
258
|
-
*/
|
|
259
|
-
async function getBranch(ownerId, projectName, branchName) {
|
|
260
|
-
try {
|
|
261
|
-
const url = buildUrl(API_ENDPOINTS.BRANCHES_GET, { owner_id: ownerId, project_name: projectName, branch_name: branchName });
|
|
262
|
-
const response = await apiClient.get(url);
|
|
263
|
-
return response;
|
|
264
|
-
} catch (error) {
|
|
265
|
-
throw new Error(`Failed to get branch: ${error.response?.data?.message || error.message}`);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
module.exports = {
|
|
270
|
-
createRepository,
|
|
271
|
-
listRepositories,
|
|
272
|
-
getRepository,
|
|
273
|
-
deleteRepository,
|
|
274
|
-
createBlob,
|
|
275
|
-
getBlob,
|
|
276
|
-
createTree,
|
|
277
|
-
getTree,
|
|
278
|
-
createCommit,
|
|
279
|
-
listCommits,
|
|
280
|
-
getCommit,
|
|
281
|
-
createBranch,
|
|
282
|
-
listBranches,
|
|
283
|
-
getBranch
|
|
284
|
-
};
|
package/src/utils/cloud-sync.js
DELETED
|
@@ -1,323 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cloud Sync Utilities - Handles synchronization between local and cloud repositories
|
|
3
|
-
* Provides functions for uploading and downloading commits, blobs, and trees
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const fs = require('fs').promises;
|
|
7
|
-
const path = require('path');
|
|
8
|
-
const chalk = require('chalk');
|
|
9
|
-
const ora = require('ora');
|
|
10
|
-
const repoService = require('../services/repo-service');
|
|
11
|
-
const { GENT_DIR, OBJECTS_DIR } = require('./constants');
|
|
12
|
-
const { readJSON, writeJSON, pathExists } = require('./fileSystem');
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Upload blobs (file contents) to cloud
|
|
16
|
-
* @param {Array} files - Array of file objects with path and content
|
|
17
|
-
* @param {number} ownerId - Owner user ID
|
|
18
|
-
* @param {string} repoName - Repository name
|
|
19
|
-
* @returns {Promise<Object>} Map of file paths to blob SHAs
|
|
20
|
-
*/
|
|
21
|
-
async function uploadBlobs(files, ownerId, repoName) {
|
|
22
|
-
const spinner = ora('Uploading file contents...').start();
|
|
23
|
-
const blobMap = {};
|
|
24
|
-
|
|
25
|
-
try {
|
|
26
|
-
for (let i = 0; i < files.length; i++) {
|
|
27
|
-
const file = files[i];
|
|
28
|
-
spinner.text = `Uploading ${file.path} (${i + 1}/${files.length})`;
|
|
29
|
-
|
|
30
|
-
const blob = await repoService.createBlob(
|
|
31
|
-
ownerId,
|
|
32
|
-
repoName,
|
|
33
|
-
file.content,
|
|
34
|
-
'utf-8'
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
blobMap[file.path] = blob.sha;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
spinner.succeed(`Uploaded ${files.length} file(s)`);
|
|
41
|
-
return blobMap;
|
|
42
|
-
} catch (error) {
|
|
43
|
-
spinner.fail('Failed to upload files');
|
|
44
|
-
throw error;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Download blobs from cloud
|
|
50
|
-
* @param {Array} shas - Array of blob SHAs to download
|
|
51
|
-
* @param {number} ownerId - Owner user ID
|
|
52
|
-
* @param {string} repoName - Repository name
|
|
53
|
-
* @returns {Promise<Object>} Map of SHAs to content
|
|
54
|
-
*/
|
|
55
|
-
async function downloadBlobs(shas, ownerId, repoName) {
|
|
56
|
-
const spinner = ora('Downloading file contents...').start();
|
|
57
|
-
const blobMap = {};
|
|
58
|
-
|
|
59
|
-
try {
|
|
60
|
-
for (let i = 0; i < shas.length; i++) {
|
|
61
|
-
const sha = shas[i];
|
|
62
|
-
spinner.text = `Downloading blob ${sha.substring(0, 7)} (${i + 1}/${shas.length})`;
|
|
63
|
-
|
|
64
|
-
const blob = await repoService.getBlob(ownerId, repoName, sha);
|
|
65
|
-
blobMap[sha] = blob.content;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
spinner.succeed(`Downloaded ${shas.length} blob(s)`);
|
|
69
|
-
return blobMap;
|
|
70
|
-
} catch (error) {
|
|
71
|
-
spinner.fail('Failed to download blobs');
|
|
72
|
-
throw error;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Build tree structure from file list
|
|
78
|
-
* @param {Array} files - Array of file objects with path and SHA
|
|
79
|
-
* @returns {Array} Tree entries
|
|
80
|
-
*/
|
|
81
|
-
function buildTreeEntries(files) {
|
|
82
|
-
return files.map(file => ({
|
|
83
|
-
path: file.path,
|
|
84
|
-
mode: '100644', // Regular file
|
|
85
|
-
type: 'blob',
|
|
86
|
-
sha: file.sha
|
|
87
|
-
}));
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Upload a tree structure to cloud
|
|
92
|
-
* @param {Array} files - Array of file objects with path and SHA
|
|
93
|
-
* @param {number} ownerId - Owner user ID
|
|
94
|
-
* @param {string} repoName - Repository name
|
|
95
|
-
* @returns {Promise<Object>} Created tree data
|
|
96
|
-
*/
|
|
97
|
-
async function uploadTree(files, ownerId, repoName) {
|
|
98
|
-
const spinner = ora('Creating tree structure...').start();
|
|
99
|
-
|
|
100
|
-
try {
|
|
101
|
-
const entries = buildTreeEntries(files);
|
|
102
|
-
const tree = await repoService.createTree(ownerId, repoName, entries);
|
|
103
|
-
|
|
104
|
-
spinner.succeed(`Created tree ${tree.sha.substring(0, 7)}`);
|
|
105
|
-
return tree;
|
|
106
|
-
} catch (error) {
|
|
107
|
-
spinner.fail('Failed to create tree');
|
|
108
|
-
throw error;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Upload a commit to cloud
|
|
114
|
-
* @param {Object} commit - Local commit object
|
|
115
|
-
* @param {string} treeSha - Tree SHA
|
|
116
|
-
* @param {number} ownerId - Owner user ID
|
|
117
|
-
* @param {string} repoName - Repository name
|
|
118
|
-
* @param {string} branchName - Branch name
|
|
119
|
-
* @returns {Promise<Object>} Created commit data
|
|
120
|
-
*/
|
|
121
|
-
async function uploadCommit(commit, treeSha, ownerId, repoName, branchName) {
|
|
122
|
-
const spinner = ora('Creating commit...').start();
|
|
123
|
-
|
|
124
|
-
try {
|
|
125
|
-
const commitData = {
|
|
126
|
-
message: commit.message,
|
|
127
|
-
tree_sha: treeSha,
|
|
128
|
-
parent_shas: Array.isArray(commit.parent) ? commit.parent : (commit.parent ? [commit.parent] : []),
|
|
129
|
-
author_name: commit.author.name,
|
|
130
|
-
author_email: commit.author.email,
|
|
131
|
-
branch: branchName
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
const cloudCommit = await repoService.createCommit(ownerId, repoName, commitData);
|
|
135
|
-
|
|
136
|
-
spinner.succeed(`Created commit ${cloudCommit.sha.substring(0, 7)}`);
|
|
137
|
-
return cloudCommit;
|
|
138
|
-
} catch (error) {
|
|
139
|
-
spinner.fail('Failed to create commit');
|
|
140
|
-
throw error;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Sync local commits to cloud
|
|
146
|
-
* @param {Array} commits - Array of local commits to upload
|
|
147
|
-
* @param {number} ownerId - Owner user ID
|
|
148
|
-
* @param {string} repoName - Repository name
|
|
149
|
-
* @param {string} branchName - Branch name
|
|
150
|
-
* @param {string} cwd - Current working directory
|
|
151
|
-
* @returns {Promise<Array>} Array of created cloud commits
|
|
152
|
-
*/
|
|
153
|
-
async function syncCommitsToCloud(commits, ownerId, repoName, branchName, cwd) {
|
|
154
|
-
console.log(chalk.cyan(`\nPushing ${commits.length} commit(s) to ${ownerId}/${repoName}...`));
|
|
155
|
-
|
|
156
|
-
const cloudCommits = [];
|
|
157
|
-
|
|
158
|
-
for (let i = 0; i < commits.length; i++) {
|
|
159
|
-
const commit = commits[i];
|
|
160
|
-
console.log(chalk.gray(`\nProcessing commit ${commit.sha.substring(0, 7)}: ${commit.message}`));
|
|
161
|
-
|
|
162
|
-
// Read files from commit
|
|
163
|
-
const files = [];
|
|
164
|
-
const commitFiles = Array.isArray(commit.files) ? commit.files : [];
|
|
165
|
-
for (const file of commitFiles) {
|
|
166
|
-
const objectPath = path.join(cwd, GENT_DIR, OBJECTS_DIR, file.hash);
|
|
167
|
-
if (await pathExists(objectPath)) {
|
|
168
|
-
const content = await fs.readFile(objectPath, 'utf-8');
|
|
169
|
-
files.push({ path: file.path, content });
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Upload blobs
|
|
174
|
-
const blobMap = await uploadBlobs(files, ownerId, repoName);
|
|
175
|
-
|
|
176
|
-
// Build file list with SHAs
|
|
177
|
-
const fileList = Object.keys(blobMap).map(path => ({
|
|
178
|
-
path,
|
|
179
|
-
sha: blobMap[path]
|
|
180
|
-
}));
|
|
181
|
-
|
|
182
|
-
// Upload tree
|
|
183
|
-
const tree = await uploadTree(fileList, ownerId, repoName);
|
|
184
|
-
|
|
185
|
-
// Upload commit
|
|
186
|
-
const cloudCommit = await uploadCommit(commit, tree.sha, ownerId, repoName, branchName);
|
|
187
|
-
|
|
188
|
-
cloudCommits.push(cloudCommit);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
console.log(chalk.green(`\n✓ Successfully pushed ${commits.length} commit(s)`));
|
|
192
|
-
return cloudCommits;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Download commits from cloud to local
|
|
197
|
-
* @param {number} ownerId - Owner user ID
|
|
198
|
-
* @param {string} repoName - Repository name
|
|
199
|
-
* @param {string} cwd - Current working directory
|
|
200
|
-
* @returns {Promise<Array>} Array of downloaded commits
|
|
201
|
-
*/
|
|
202
|
-
async function syncCommitsFromCloud(ownerId, repoName, cwd) {
|
|
203
|
-
const spinner = ora('Fetching commits from cloud...').start();
|
|
204
|
-
|
|
205
|
-
try {
|
|
206
|
-
const cloudCommits = await repoService.listCommits(ownerId, repoName);
|
|
207
|
-
spinner.succeed(`Fetched ${cloudCommits.length} commit(s)`);
|
|
208
|
-
|
|
209
|
-
// Download trees and blobs for each commit
|
|
210
|
-
for (const commit of cloudCommits) {
|
|
211
|
-
console.log(chalk.gray(`Processing commit ${commit.sha.substring(0, 7)}: ${commit.message}`));
|
|
212
|
-
|
|
213
|
-
// Download tree
|
|
214
|
-
const tree = await repoService.getTree(ownerId, repoName, commit.tree_sha);
|
|
215
|
-
|
|
216
|
-
// Store entries for local metadata
|
|
217
|
-
commit.files = tree.entries.map(entry => ({
|
|
218
|
-
path: entry.path,
|
|
219
|
-
hash: entry.sha
|
|
220
|
-
}));
|
|
221
|
-
|
|
222
|
-
// Download blobs from tree entries
|
|
223
|
-
const blobShas = tree.entries.map(entry => entry.sha);
|
|
224
|
-
const blobMap = await downloadBlobs(blobShas, ownerId, repoName);
|
|
225
|
-
|
|
226
|
-
// Save blobs to local objects directory
|
|
227
|
-
const objectsDir = path.join(cwd, GENT_DIR, OBJECTS_DIR);
|
|
228
|
-
for (const [sha, content] of Object.entries(blobMap)) {
|
|
229
|
-
const objectPath = path.join(objectsDir, sha);
|
|
230
|
-
await fs.writeFile(objectPath, content, 'utf-8');
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
console.log(chalk.green(`\n✓ Successfully pulled ${cloudCommits.length} commit(s)`));
|
|
235
|
-
return cloudCommits;
|
|
236
|
-
} catch (error) {
|
|
237
|
-
spinner.fail('Failed to fetch commits');
|
|
238
|
-
throw error;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* Get or create remote configuration
|
|
244
|
-
* @param {string} cwd - Current working directory
|
|
245
|
-
* @returns {Promise<Object>} Remote configuration
|
|
246
|
-
*/
|
|
247
|
-
async function getRemoteConfig(cwd) {
|
|
248
|
-
const remotePath = path.join(cwd, GENT_DIR, 'remote.json');
|
|
249
|
-
|
|
250
|
-
if (await pathExists(remotePath)) {
|
|
251
|
-
return await readJSON(remotePath);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
return { remotes: {} };
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* Save remote configuration
|
|
259
|
-
* @param {Object} config - Remote configuration
|
|
260
|
-
* @param {string} cwd - Current working directory
|
|
261
|
-
*/
|
|
262
|
-
async function saveRemoteConfig(config, cwd) {
|
|
263
|
-
const remotePath = path.join(cwd, GENT_DIR, 'remote.json');
|
|
264
|
-
await writeJSON(remotePath, config);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Add a remote to configuration
|
|
269
|
-
* @param {string} name - Remote name
|
|
270
|
-
* @param {number} ownerId - Owner user ID
|
|
271
|
-
* @param {string} repoName - Repository name
|
|
272
|
-
* @param {string} cwd - Current working directory
|
|
273
|
-
*/
|
|
274
|
-
async function addRemote(name, ownerId, repoName, cwd) {
|
|
275
|
-
const config = await getRemoteConfig(cwd);
|
|
276
|
-
|
|
277
|
-
config.remotes[name] = {
|
|
278
|
-
owner_id: ownerId,
|
|
279
|
-
repo_name: repoName
|
|
280
|
-
};
|
|
281
|
-
|
|
282
|
-
await saveRemoteConfig(config, cwd);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Remove a remote from configuration
|
|
287
|
-
* @param {string} name - Remote name
|
|
288
|
-
* @param {string} cwd - Current working directory
|
|
289
|
-
*/
|
|
290
|
-
async function removeRemote(name, cwd) {
|
|
291
|
-
const config = await getRemoteConfig(cwd);
|
|
292
|
-
|
|
293
|
-
if (config.remotes[name]) {
|
|
294
|
-
delete config.remotes[name];
|
|
295
|
-
await saveRemoteConfig(config, cwd);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* Get remote by name
|
|
301
|
-
* @param {string} name - Remote name
|
|
302
|
-
* @param {string} cwd - Current working directory
|
|
303
|
-
* @returns {Promise<Object|null>} Remote configuration or null
|
|
304
|
-
*/
|
|
305
|
-
async function getRemote(name, cwd) {
|
|
306
|
-
const config = await getRemoteConfig(cwd);
|
|
307
|
-
return config.remotes[name] || null;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
module.exports = {
|
|
311
|
-
uploadBlobs,
|
|
312
|
-
downloadBlobs,
|
|
313
|
-
buildTreeEntries,
|
|
314
|
-
uploadTree,
|
|
315
|
-
uploadCommit,
|
|
316
|
-
syncCommitsToCloud,
|
|
317
|
-
syncCommitsFromCloud,
|
|
318
|
-
getRemoteConfig,
|
|
319
|
-
saveRemoteConfig,
|
|
320
|
-
addRemote,
|
|
321
|
-
removeRemote,
|
|
322
|
-
getRemote
|
|
323
|
-
};
|