Update to 4.4
This commit is contained in:
@@ -73,21 +73,36 @@ class GitService {
|
||||
return result.stdout;
|
||||
}
|
||||
|
||||
async expandSelectedPaths(root, files) {
|
||||
const selected = assertRepositoryRelativePaths(files);
|
||||
if (!selected.length) return [];
|
||||
const status = await this.status(root);
|
||||
const expanded = new Set(selected);
|
||||
for (const file of status.files) {
|
||||
if (selected.includes(file.path) || (file.originalPath && selected.includes(file.originalPath))) {
|
||||
expanded.add(file.path);
|
||||
if (file.originalPath) expanded.add(file.originalPath);
|
||||
}
|
||||
}
|
||||
return [...expanded];
|
||||
}
|
||||
|
||||
async stage(repoPath, files) {
|
||||
const root = await this.ensureRepository(repoPath);
|
||||
const selected = assertRepositoryRelativePaths(files);
|
||||
await run('git', selected.length ? ['add', '--', ...selected] : ['add', '--all'], { cwd: root, timeout: 60_000 });
|
||||
const selected = await this.expandSelectedPaths(root, files);
|
||||
// -A is required for deleted files and for the old side of renames.
|
||||
await run('git', selected.length ? ['add', '-A', '--', ...selected] : ['add', '--all'], { cwd: root, timeout: 60_000 });
|
||||
return this.status(root);
|
||||
}
|
||||
|
||||
async unstage(repoPath, files) {
|
||||
const root = await this.ensureRepository(repoPath);
|
||||
const selected = assertRepositoryRelativePaths(files);
|
||||
const selected = await this.expandSelectedPaths(root, files);
|
||||
const hasHead = await run('git', ['rev-parse', '--verify', 'HEAD'], { cwd: root, allowExitCodes: [128] });
|
||||
if (hasHead.exitCode === 0) {
|
||||
await run('git', selected.length ? ['restore', '--staged', '--', ...selected] : ['restore', '--staged', '.'], { cwd: root });
|
||||
} else {
|
||||
await run('git', selected.length ? ['rm', '--cached', '--', ...selected] : ['rm', '--cached', '-r', '.'], { cwd: root, allowExitCodes: [1] });
|
||||
await run('git', selected.length ? ['rm', '--cached', '--ignore-unmatch', '--', ...selected] : ['rm', '--cached', '-r', '.'], { cwd: root, allowExitCodes: [1] });
|
||||
}
|
||||
return this.status(root);
|
||||
}
|
||||
@@ -95,9 +110,11 @@ class GitService {
|
||||
async prepareSelectedStage(root, files) {
|
||||
const selected = assertRepositoryRelativePaths(files);
|
||||
if (selected.length) {
|
||||
const stagedBefore = await run('git', ['diff', '--cached', '--name-only', '-z'], { cwd: root });
|
||||
const alreadyStaged = stagedBefore.stdout.split('\0').filter(Boolean);
|
||||
const excludedStaged = alreadyStaged.filter((file) => !selected.includes(file));
|
||||
const current = await this.status(root);
|
||||
const excludedStaged = current.files
|
||||
.filter((file) => file.staged)
|
||||
.filter((file) => !selected.includes(file.path) && !(file.originalPath && selected.includes(file.originalPath)))
|
||||
.map((file) => file.path);
|
||||
if (excludedStaged.length) {
|
||||
throw new Error(`Some staged files are not selected (${excludedStaged.slice(0, 3).join(', ')}${excludedStaged.length > 3 ? ', …' : ''}). Select them or unstage them first.`);
|
||||
}
|
||||
|
||||
+20
-2
@@ -202,6 +202,14 @@ async function refreshRepositories(withLoader = true, silent = false) {
|
||||
if (selectedId && !selectedRepository()) ui.selectedRepoId = null;
|
||||
const repository = selectedRepository();
|
||||
if (repository && !repository.deploymentProfiles.some((profile) => profile.id === ui.selectedProfileId)) ui.selectedProfileId = selectedProfile(repository)?.id || null;
|
||||
if (repository) {
|
||||
const availablePaths = new Set((repository.localStatus?.files || []).map((file) => file.path));
|
||||
ui.selectedFiles = new Set([...ui.selectedFiles].filter((filePath) => availablePaths.has(filePath)));
|
||||
if (ui.selectedFile && !availablePaths.has(ui.selectedFile)) {
|
||||
ui.selectedFile = repository.localStatus?.files?.[0]?.path || null;
|
||||
ui.diff = '';
|
||||
}
|
||||
}
|
||||
if (!ui.selectedRepoId && ui.currentView === 'repository' && ui.repositories.length) selectRepository(ui.repositories[0].id, false);
|
||||
} catch (error) {
|
||||
ui.refreshError = error.message;
|
||||
@@ -655,8 +663,18 @@ async function runOperation(message, operation, successMessage, { refresh = true
|
||||
if (refresh) await refreshRepositories(false);
|
||||
return result;
|
||||
} catch (error) {
|
||||
showToast(error.code === 'PUSH_AFTER_COMMIT_FAILED' ? 'Commit created; push failed' : 'Operation failed', error.message, 'error');
|
||||
if (error.commitSha) await refreshRepositories(false, true);
|
||||
const pushAfterCommit = error.code === 'PUSH_AFTER_COMMIT_FAILED';
|
||||
showToast(pushAfterCommit ? 'Commit saved locally; push failed' : 'Operation failed', error.message, 'error');
|
||||
// Always reload the real Git state. A failed stage must keep changes visible, while a
|
||||
// failed push after a successful commit must immediately surface as an ahead branch.
|
||||
await refreshRepositories(false, true);
|
||||
if (pushAfterCommit) {
|
||||
ui.selectedFiles.clear();
|
||||
ui.selectedFile = null;
|
||||
ui.diff = '';
|
||||
ui.commitMessage = '';
|
||||
render();
|
||||
}
|
||||
return null;
|
||||
} finally { setLoading(false); }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user