mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-31 00:10:04 +08:00
Session window: apply patch to local
This commit is contained in:
@@ -1061,6 +1061,20 @@ export class CommandCenter {
|
||||
await repo.pull();
|
||||
}
|
||||
|
||||
@command('_git.applyPatch')
|
||||
async applyPatch(repositoryPath: string, patchContent: string): Promise<void> {
|
||||
const dotGit = await this.git.getRepositoryDotGit(repositoryPath);
|
||||
const repo = new GitRepository(this.git, repositoryPath, undefined, dotGit, this.logger);
|
||||
const patchPath = path.join(os.tmpdir(), `vscode-patch-${Date.now()}.patch`);
|
||||
const { promises: fsp } = await import('fs');
|
||||
try {
|
||||
await fsp.writeFile(patchPath, patchContent, 'utf8');
|
||||
await repo.apply(patchPath, { threeWay: true });
|
||||
} finally {
|
||||
await fsp.unlink(patchPath).catch(() => { });
|
||||
}
|
||||
}
|
||||
|
||||
@command('git.init')
|
||||
async init(skipFolderPrompt = false): Promise<void> {
|
||||
let repositoryPath: string | undefined = undefined;
|
||||
|
||||
@@ -10,6 +10,7 @@ import { autorun } from '../../../../base/common/observable.js';
|
||||
import { Codicon } from '../../../../base/common/codicons.js';
|
||||
import { localize, localize2 } from '../../../../nls.js';
|
||||
import { Action2, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js';
|
||||
import { ICommandService } from '../../../../platform/commands/common/commands.js';
|
||||
import { ContextKeyExpr, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js';
|
||||
import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js';
|
||||
import { IFileService } from '../../../../platform/files/common/files.js';
|
||||
@@ -18,12 +19,13 @@ import { IOpenerService } from '../../../../platform/opener/common/opener.js';
|
||||
import { IProductService } from '../../../../platform/product/common/productService.js';
|
||||
import { IWorkbenchContribution, registerWorkbenchContribution2, WorkbenchPhase } from '../../../../workbench/common/contributions.js';
|
||||
import { CHAT_CATEGORY } from '../../../../workbench/contrib/chat/browser/actions/chatActions.js';
|
||||
import { generateUnifiedDiff } from '../../../../workbench/contrib/chat/browser/chatRepoInfo.js';
|
||||
import { ChatContextKeys } from '../../../../workbench/contrib/chat/common/actions/chatContextKeys.js';
|
||||
import { IAgentSessionsService } from '../../../../workbench/contrib/chat/browser/agentSessions/agentSessionsService.js';
|
||||
import { isIChatSessionFileChange2 } from '../../../../workbench/contrib/chat/common/chatSessionsService.js';
|
||||
import { ISessionsManagementService } from '../../sessions/browser/sessionsManagementService.js';
|
||||
import { IsSessionsWindowContext } from '../../../../workbench/common/contextkeys.js';
|
||||
import { isEqualOrParent, joinPath, relativePath } from '../../../../base/common/resources.js';
|
||||
import { isEqualOrParent, relativePath } from '../../../../base/common/resources.js';
|
||||
import { ILogService } from '../../../../platform/log/common/log.js';
|
||||
import { URI } from '../../../../base/common/uri.js';
|
||||
|
||||
@@ -89,6 +91,7 @@ class ApplyToParentRepoAction extends Action2 {
|
||||
const logService = accessor.get(ILogService);
|
||||
const openerService = accessor.get(IOpenerService);
|
||||
const productService = accessor.get(IProductService);
|
||||
const commandService = accessor.get(ICommandService);
|
||||
|
||||
const activeSession = sessionManagementService.getActiveSession();
|
||||
if (!activeSession?.worktree || !activeSession?.repository) {
|
||||
@@ -104,9 +107,9 @@ class ApplyToParentRepoAction extends Action2 {
|
||||
return;
|
||||
}
|
||||
|
||||
let copiedCount = 0;
|
||||
let deletedCount = 0;
|
||||
let errorCount = 0;
|
||||
// Generate a combined unified diff patch from all changes
|
||||
const patchParts: string[] = [];
|
||||
let fileCount = 0;
|
||||
|
||||
for (const change of changes) {
|
||||
try {
|
||||
@@ -117,34 +120,54 @@ class ApplyToParentRepoAction extends Action2 {
|
||||
? change.modifiedUri === undefined
|
||||
: false;
|
||||
|
||||
const originalUri = change.originalUri;
|
||||
let relPath: string | undefined;
|
||||
|
||||
if (isDeletion) {
|
||||
const originalUri = change.originalUri;
|
||||
if (originalUri && isEqualOrParent(toFileUri(originalUri), worktreeRoot)) {
|
||||
const relPath = relativePath(worktreeRoot, toFileUri(originalUri));
|
||||
if (relPath) {
|
||||
const targetUri = joinPath(repoRoot, relPath);
|
||||
if (await fileService.exists(targetUri)) {
|
||||
await fileService.del(targetUri);
|
||||
deletedCount++;
|
||||
}
|
||||
}
|
||||
relPath = relativePath(worktreeRoot, toFileUri(originalUri));
|
||||
}
|
||||
} else {
|
||||
if (isEqualOrParent(toFileUri(modifiedUri), worktreeRoot)) {
|
||||
const relPath = relativePath(worktreeRoot, toFileUri(modifiedUri));
|
||||
if (relPath) {
|
||||
const targetUri = joinPath(repoRoot, relPath);
|
||||
await fileService.copy(modifiedUri, targetUri, true);
|
||||
copiedCount++;
|
||||
}
|
||||
relPath = relativePath(worktreeRoot, toFileUri(modifiedUri));
|
||||
}
|
||||
}
|
||||
|
||||
if (!relPath) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const changeType: 'added' | 'modified' | 'deleted' = isDeletion
|
||||
? 'deleted'
|
||||
: originalUri ? 'modified' : 'added';
|
||||
|
||||
const diff = await generateUnifiedDiff(
|
||||
fileService,
|
||||
relPath,
|
||||
originalUri,
|
||||
modifiedUri,
|
||||
changeType
|
||||
);
|
||||
|
||||
if (diff) {
|
||||
patchParts.push(diff);
|
||||
fileCount++;
|
||||
}
|
||||
} catch (err) {
|
||||
logService.error('[ApplyToParentRepo] Failed to apply change', err);
|
||||
errorCount++;
|
||||
logService.error('[ApplyToParentRepo] Failed to generate diff for change', err);
|
||||
}
|
||||
}
|
||||
|
||||
if (patchParts.length === 0) {
|
||||
notificationService.notify({
|
||||
severity: Severity.Info,
|
||||
message: localize('applyToParentRepoNoDiffs', "No applicable changes to apply to parent repo."),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const combinedPatch = patchParts.join('\n') + '\n';
|
||||
|
||||
const openFolderAction = toAction({
|
||||
id: 'applyToParentRepo.openFolder',
|
||||
label: localize('openInVSCode', "Open in VS Code"),
|
||||
@@ -168,21 +191,21 @@ class ApplyToParentRepoAction extends Action2 {
|
||||
}
|
||||
});
|
||||
|
||||
const totalApplied = copiedCount + deletedCount;
|
||||
if (errorCount > 0) {
|
||||
notificationService.notify({
|
||||
severity: Severity.Warning,
|
||||
message: totalApplied === 1
|
||||
? localize('applyToParentRepoPartial1', "Applied 1 file to parent repo with {0} error(s).", errorCount)
|
||||
: localize('applyToParentRepoPartialN', "Applied {0} files to parent repo with {1} error(s).", totalApplied, errorCount),
|
||||
actions: { primary: [openFolderAction] }
|
||||
});
|
||||
} else if (totalApplied > 0) {
|
||||
try {
|
||||
await commandService.executeCommand('_git.applyPatch', repoRoot.fsPath, combinedPatch);
|
||||
|
||||
notificationService.notify({
|
||||
severity: Severity.Info,
|
||||
message: totalApplied === 1
|
||||
message: fileCount === 1
|
||||
? localize('applyToParentRepoSuccess1', "Applied 1 file to parent repo.")
|
||||
: localize('applyToParentRepoSuccessN', "Applied {0} files to parent repo.", totalApplied),
|
||||
: localize('applyToParentRepoSuccessN', "Applied {0} files to parent repo.", fileCount),
|
||||
actions: { primary: [openFolderAction] }
|
||||
});
|
||||
} catch (err) {
|
||||
logService.error('[ApplyToParentRepo] git apply failed', err);
|
||||
notificationService.notify({
|
||||
severity: Severity.Warning,
|
||||
message: localize('applyToParentRepoConflict', "Failed to apply patch to parent repo. The parent repo may have diverged — resolve conflicts manually."),
|
||||
actions: { primary: [openFolderAction] }
|
||||
});
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ function determineChangeType(resource: ISCMResource, groupId: string): 'added' |
|
||||
* files is the presence/absence of a trailing newline (content otherwise identical),
|
||||
* no diff will be generated because VS Code's diff algorithm treats the lines as equal.
|
||||
*/
|
||||
async function generateUnifiedDiff(
|
||||
export async function generateUnifiedDiff(
|
||||
fileService: IFileService,
|
||||
relPath: string,
|
||||
originalUri: URI | undefined,
|
||||
|
||||
Reference in New Issue
Block a user