Git - refactor migrate changes functionality (#278426)

* Git - rework migrate changes

* Add extension API

* Revert some of the options

* Remove staged option

* More cleanup

* More command cleanup
This commit is contained in:
Ladislau Szomoru
2025-11-19 20:55:43 +00:00
committed by GitHub
parent 3a7911fc9f
commit 7c999f6f62
4 changed files with 111 additions and 86 deletions

View File

@@ -3383,103 +3383,38 @@ export class CommandCenter {
}
@command('git.migrateWorktreeChanges', { repository: true, repositoryFilter: ['repository', 'submodule'] })
async migrateWorktreeChanges(repository: Repository, worktreeUri?: Uri): Promise<void> {
async migrateWorktreeChanges(repository: Repository): Promise<void> {
let worktreeRepository: Repository | undefined;
if (worktreeUri !== undefined) {
worktreeRepository = this.model.getRepository(worktreeUri);
const worktrees = await repository.getWorktrees();
if (worktrees.length === 1) {
worktreeRepository = this.model.getRepository(worktrees[0].path);
} else {
const worktrees = await repository.getWorktrees();
if (worktrees.length === 1) {
worktreeRepository = this.model.getRepository(worktrees[0].path);
} else {
const worktreePicks = async (): Promise<WorktreeItem[] | QuickPickItem[]> => {
return worktrees.length === 0
? [{ label: l10n.t('$(info) This repository has no worktrees.') }]
: worktrees.map(worktree => new WorktreeItem(worktree));
};
const worktreePicks = async (): Promise<WorktreeItem[] | QuickPickItem[]> => {
return worktrees.length === 0
? [{ label: l10n.t('$(info) This repository has no worktrees.') }]
: worktrees.map(worktree => new WorktreeItem(worktree));
};
const placeHolder = l10n.t('Select a worktree to migrate changes from');
const choice = await this.pickRef<WorktreeItem | QuickPickItem>(worktreePicks(), placeHolder);
const placeHolder = l10n.t('Select a worktree to migrate changes from');
const choice = await this.pickRef<WorktreeItem | QuickPickItem>(worktreePicks(), placeHolder);
if (!choice || !(choice instanceof WorktreeItem)) {
return;
}
worktreeRepository = this.model.getRepository(choice.worktree.path);
if (!choice || !(choice instanceof WorktreeItem)) {
return;
}
worktreeRepository = this.model.getRepository(choice.worktree.path);
}
if (!worktreeRepository || worktreeRepository.kind !== 'worktree') {
return;
}
if (worktreeRepository.indexGroup.resourceStates.length === 0 &&
worktreeRepository.workingTreeGroup.resourceStates.length === 0 &&
worktreeRepository.untrackedGroup.resourceStates.length === 0) {
await window.showInformationMessage(l10n.t('There are no changes in the selected worktree to migrate.'));
return;
}
const worktreeChangedFilePaths = [
...worktreeRepository.indexGroup.resourceStates,
...worktreeRepository.workingTreeGroup.resourceStates,
...worktreeRepository.untrackedGroup.resourceStates
].map(resource => path.relative(worktreeRepository.root, resource.resourceUri.fsPath));
const targetChangedFilePaths = [
...repository.workingTreeGroup.resourceStates,
...repository.untrackedGroup.resourceStates
].map(resource => path.relative(repository.root, resource.resourceUri.fsPath));
// Detect overlapping unstaged files in worktree stash and target repository
const conflicts = worktreeChangedFilePaths.filter(path => targetChangedFilePaths.includes(path));
// Check for 'LocalChangesOverwritten' error
if (conflicts.length > 0) {
const maxFilesShown = 5;
const filesToShow = conflicts.slice(0, maxFilesShown);
const remainingCount = conflicts.length - maxFilesShown;
const fileList = filesToShow.join('\n ') +
(remainingCount > 0 ? l10n.t('\n and {0} more file{1}...', remainingCount, remainingCount > 1 ? 's' : '') : '');
const message = l10n.t('Your local changes to the following files would be overwritten by merge:\n {0}\n\nPlease stage, commit, or stash your changes in the repository before migrating changes.', fileList);
await window.showErrorMessage(message, { modal: true });
return;
}
if (worktreeUri === undefined) {
// Non-interactive migration, do not show confirmation dialog
const message = l10n.t('Proceed with migrating changes to the current repository?');
const detail = l10n.t('This will apply the worktree\'s changes to this repository and discard changes in the worktree.\nThis is IRREVERSIBLE!');
const proceed = l10n.t('Proceed');
const pick = await window.showWarningMessage(message, { modal: true, detail }, proceed);
if (pick !== proceed) {
return;
}
}
await worktreeRepository.createStash(undefined, true);
const stashes = await worktreeRepository.getStashes();
try {
await repository.applyStash(stashes[0].index);
worktreeRepository.dropStash(stashes[0].index);
} catch (err) {
if (err.gitErrorCode !== GitErrorCodes.StashConflict) {
await worktreeRepository.popStash();
throw err;
}
repository.isWorktreeMigrating = true;
const message = l10n.t('There are merge conflicts from migrating changes. Please resolve them before committing.');
const show = l10n.t('Show Changes');
const choice = await window.showWarningMessage(message, show);
if (choice === show) {
await commands.executeCommand('workbench.view.scm');
}
worktreeRepository.dropStash(stashes[0].index);
}
await repository.migrateChanges(worktreeRepository.root, {
confirmation: true,
deleteFromSource: false,
untracked: true
});
}
@command('git.openWorktreeMergeEditor')