1
1
mirror of https://github.com/kahole/edamagit.git synced 2024-09-11 07:15:31 +03:00

doing work

This commit is contained in:
kahole 2020-01-31 23:53:32 +01:00
parent 6a4aa81e68
commit bfdcf3e0ba
19 changed files with 344 additions and 167 deletions

View File

@ -4,7 +4,7 @@
TAB on view toggles fold v unfold propert
-> Trigger rerender
Also much simpler to save and reload the folding state.
Makes everything else hard af
----------
### VSCode folding
editor.foldLevel2 fixes a lot!
@ -28,22 +28,24 @@ FIRST TIME OPENING: default fold levels
- Stash zero levels
- Recent commits zero levels
## BUGS (also remember MINOR FUTURE)
## TODO: BUGS (also remember MINOR FUTURE)
Bug#2
After commiting, sometimes another magit status window is next to the existing one.
- How to reproduce? Never happens in debug, or?
Bug#3
Section model is responsible for too much
Must fix. Fine for deciding what section something is. But changes and hunks should use another enum, because it isnt one-to-one between them
## Feature requests
Feature#1
magit-dispatch-popup - from any file
magit-file-popup
- Staging
- Log
- Blaming
Also, make all commands available so long as there is an active editor.
This means commands need to be able to handle no VIEW.
This should really be no extra effort
Might just need to prime a bit smarter?
## FUTURE: Eie egen modell
Burde ikke extende git modellen kanskje.
@ -52,7 +54,6 @@ med mappere i mellom?
## FUTURE: injection for repo etc?
@command('magit.branching', { repository: true })
https://github.com/microsoft/vscode/blob/master/extensions/git/src/commands.ts
## Workspaces
@ -60,11 +61,6 @@ https://github.com/microsoft/vscode/blob/master/extensions/git/src/commands.ts
- Find out how to deal with status views and other views
- Dispose of stuff when quit workspace etc..
## UI
- icons
https://code.visualstudio.com/api/references/icons-in-labels
e.g $(git-branch)
## Dispose
- Proper use of dispose()
https://vscode-docs.readthedocs.io/en/stable/extensions/patterns-and-principles/#disposables
@ -79,17 +75,16 @@ https://github.com/microsoft/vscode/blob/master/extensions/git/src/commands.ts
o GPL2/3, MIT, etc
o git.d.ts microsoft licence, effects?
- VsVim?
Maybe.. Is it possible to disable VSVim for certain languages / file extensions / etc?
-----
## TODO: Release ALPHA, MVP
# RELEASE ALPHA MVP
## Github sponsor skru på donasjoner på github?
https://github.com/sponsors
# Magit for VSCode
# Magit for VSCode (alpha)
!alpha: use at own your own risk ??
Inspired by the original (Magit)[https://magit.vc/] for Emacs

View File

@ -11,7 +11,9 @@
"Other"
],
"activationEvents": [
"onCommand:extension.magit"
"onCommand:extension.magit",
"onCommand:extension.magit-dispatch",
"onCommand:extension.magit-file-popup"
],
"main": "./out/extension.js",
"contributes": {
@ -20,6 +22,14 @@
"command": "extension.magit",
"title": "Magit Status"
},
{
"command": "extension.magit-dispatch",
"title": "Magit Dispatch"
},
{
"command": "extension.magit-file-popup",
"title": "Magit File Popup"
},
{
"command": "extension.magit-visit-at-point",
"title": "Magit Visit-at-point"
@ -44,10 +54,18 @@
"command": "extension.magit-merging",
"title": "Magit Merging"
},
{
"command": "extension.magit-rebasing",
"title": "Magit Rebasing"
},
{
"command": "extension.magit-pushing",
"title": "Magit Pushing"
},
{
"command": "extension.magit-pulling",
"title": "Magit Pulling"
},
{
"command": "extension.magit-stage",
"title": "Magit Stage"
@ -75,6 +93,10 @@
],
"menus": {
"commandPalette": [
{
"command": "extension.magit-help",
"when": "editorLangId == magit"
},
{
"command": "extension.magit-branching",
"when": "editorLangId == magit"
@ -83,6 +105,10 @@
"command": "extension.magit-merging",
"when": "editorLangId == magit"
},
{
"command": "extension.magit-rebasing",
"when": "editorLangId == magit"
},
{
"command": "extension.magit-visit-at-point",
"when": "editorLangId == magit"
@ -119,6 +145,10 @@
"command": "extension.magit-pushing",
"when": "editorLangId == magit"
},
{
"command": "extension.magit-pulling",
"when": "editorLangId == magit"
},
{
"command": "extension.magit-stashing",
"when": "editorLangId == magit"
@ -150,6 +180,11 @@
}
],
"keybindings": [
{
"command": "extension.magit",
"key": "ctrl+x g",
"when": "editorTextFocus"
},
{
"command": "editor.toggleFold",
"key": "tab",
@ -195,6 +230,11 @@
"key": "m",
"when": "editorTextFocus && editorLangId == magit"
},
{
"command": "extension.magit-rebasing",
"key": "r",
"when": "editorTextFocus && editorLangId == magit"
},
{
"command": "extension.magit-pushing",
"key": "shift+p",

View File

@ -3,7 +3,7 @@ import { MagitRepository } from '../models/magitRepository';
import { TextEditor, window } from 'vscode';
import { DocumentView } from '../views/general/documentView';
export class CommandPrimer {
export class Command {
// static primeRepo(command: (repository: MagitRepository) => Promise<void>) {
// return (editor: TextEditor) => {
@ -26,9 +26,6 @@ export class CommandPrimer {
await command(repository, currentView);
} catch (error) {
if (error.gitErrorCode) {
// This needs to be cleared somehow as well?
// Maybe it gets removed once it is rendered. So next render will not have it?
// e.g: GitError! Your local changes to the following files would be overwritten by checkout
repository.magitState!.latestGitError = error.stderr ?? error.message;
} else {
// This error type, too heavy for most errors?

View File

@ -26,14 +26,11 @@ export async function fetching(repository: MagitRepository, currentView: Documen
fetchingMenuItems.push({ label: 'o', description: 'another branch', action: fetchAnotherBranch });
// MINOR: more fetching options exist in magit
// - explicit refspec
// - submodules
//fetchingMenuItems.push({ label: 'o', description: 'another branch', action: () => { } });
return MenuUtil.showMenu({ title: 'Fetching', commands: fetchingMenuItems }, { repository, currentView });
}
// TODO: fetching: some work remains for MVP
async function fetchFromPushRemote() {
}

View File

@ -5,6 +5,8 @@ import { DocumentView } from '../views/general/documentView';
import { gitRun } from '../utils/gitRawRunner';
import * as CommitCommands from '../commands/commitCommands';
// TODO: merging: some work remains for MVP
const mergingMenu = {
title: 'Merging',
commands: [
@ -12,7 +14,7 @@ const mergingMenu = {
{ label: 'e', description: 'Merge and edit message', action: merge },
{ label: 'n', description: 'Merge, don\'t commit', action: merge },
{ label: 'a', description: 'Absorb', action: absorb },
{ label: 'p', description: 'Preview Merge', action: mergePreview },
// { label: 'p', description: 'Preview Merge', action: mergePreview },
{ label: 's', description: 'Squash Merge', action: merge },
{ label: 's', description: 'Merge into', action: merge },
]
@ -52,12 +54,12 @@ async function absorb({ repository }: MenuState) {
}
}
async function mergePreview() {
// Commands to preview a merge between ref1 and ref2:
// git merge-base HEAD {ref2}
// git merge-tree {MERGE-BASE} HEAD {ref2}
// https://stackoverflow.com/questions/501407/is-there-a-git-merge-dry-run-option/6283843#6283843
}
// async function mergePreview() {
// // Commands to preview a merge between ref1 and ref2:
// // git merge-base HEAD {ref2}
// // git merge-tree {MERGE-BASE} HEAD {ref2}
// // https://stackoverflow.com/questions/501407/is-there-a-git-merge-dry-run-option/6283843#6283843
// }
async function _merge(repository: MagitRepository, ref: string, noCommit = false, squashMerge = false, editMessage = false) {

View File

@ -0,0 +1,89 @@
import { window, commands } from 'vscode';
import { Menu, MenuState, MenuUtil } from '../menu/menu';
import { MagitRepository } from '../models/magitRepository';
import { DocumentView } from '../views/general/documentView';
import { gitRun } from '../utils/gitRawRunner';
import * as CommitCommands from '../commands/commitCommands';
// TODO: rebasing: some work remains for MVP
const whileRebasingMenu = {
title: 'Rebasing',
commands: [
{ label: 'r', description: 'Continue', action: (state: MenuState) => rebaseControlCommand(state, '--continue') },
{ label: 's', description: 'Skip', action: (state: MenuState) => rebaseControlCommand(state, '--skip') },
{ label: 'e', description: 'Edit', action: (state: MenuState) => rebaseControlCommand(state, '--edit-todo') },
{ label: 'a', description: 'Abort', action: (state: MenuState) => rebaseControlCommand(state, '--abort') }
]
};
export async function rebasing(repository: MagitRepository, currentView: DocumentView) {
const HEAD = repository.magitState?.HEAD;
const rebasingMenu = {
title: `Rebasing ${HEAD?.name}`,
commands: [
{ label: 'p', description: `onto ${HEAD?.pushRemote?.remote}/${HEAD?.pushRemote?.name}`, action: rebase },
{ label: 'u', description: `onto ${HEAD?.upstreamRemote?.remote}/${HEAD?.upstreamRemote?.name}`, action: rebase },
{ label: 'e', description: `onto elsewhere`, action: rebase },
{ label: 'i', description: `interactively`, action: rebase },
]
};
if (repository.magitState?.rebasingState) {
return MenuUtil.showMenu(whileRebasingMenu, { repository, currentView });
} else {
return MenuUtil.showMenu(rebasingMenu, { repository, currentView });
}
}
async function rebase({ repository }: MenuState) {
const ref = await window.showQuickPick(repository.state.refs.map(r => r.name!), { placeHolder: 'Merge' });
if (ref) {
return _merge(repository, ref);
}
}
async function absorb({ repository }: MenuState) {
const ref = await window.showQuickPick(repository.state.refs.map(r => r.name!), { placeHolder: 'Merge' });
if (ref) {
await _merge(repository, ref);
return await repository.deleteBranch(ref, false);
}
}
async function mergePreview() {
// Commands to preview a merge between ref1 and ref2:
// git merge-base HEAD {ref2}
// git merge-tree {MERGE-BASE} HEAD {ref2}
// https://stackoverflow.com/questions/501407/is-there-a-git-merge-dry-run-option/6283843#6283843
}
async function _merge(repository: MagitRepository, ref: string, noCommit = false, squashMerge = false, editMessage = false) {
const args = ['merge', ref];
if (noCommit) {
args.push(...['--no-commit', '--no-ff']);
}
if (squashMerge) {
args.push('--squash');
}
if (editMessage) {
// TODO: This might need a separate handler, because of message editing??
args.push(...['--edit', '--no-ff']);
} else {
args.push('--no-edit');
}
return gitRun(repository, args);
}
async function rebaseControlCommand({ repository }: MenuState, command: string) {
const args = ['rebase', command];
return gitRun(repository, args);
}

View File

@ -11,10 +11,11 @@ import { MagitBranch } from '../models/magitBranch';
import { Section } from '../views/general/sectionHeader';
import { gitRun } from '../utils/gitRawRunner';
import * as Constants from '../common/constants';
import { getCommit } from '../utils/commitCache';
export async function magitRefresh() { }
export async function magitStatus(preserveFocus = false) {
export async function magitStatus(preserveFocus = false): Promise<any> {
if (window.activeTextEditor) {
@ -42,9 +43,8 @@ export async function magitStatus(preserveFocus = false) {
// Open and focus magit status view
// Run update
await MagitUtils.magitStatusAndUpdate(repository, view);
workspace.openTextDocument(view.uri).then(doc => window.showTextDocument(doc, { viewColumn: ViewColumn.Beside, preserveFocus, preview: false }));
console.log('Update existing view');
return;
return workspace.openTextDocument(view.uri).then(doc => window.showTextDocument(doc, { viewColumn: ViewColumn.Beside, preserveFocus, preview: false }));
}
}
} else {
@ -56,35 +56,39 @@ export async function magitStatus(preserveFocus = false) {
const magitRepo: MagitRepository = repository;
magitRepositories.set(repository.rootUri.path, repository);
internalMagitStatus(magitRepo)
.then(() => {
const uri = MagitStatusView.encodeLocation(magitRepo.rootUri.path);
views.set(uri.toString(), new MagitStatusView(uri, magitRepo.magitState!));
workspace.openTextDocument(uri).then(doc => window.showTextDocument(doc, { viewColumn: ViewColumn.Beside, preserveFocus, preview: false }))
await internalMagitStatus(magitRepo);
// TODO LATE PRI: branch highlighting...
// THIS WORKS
// Decorations could be added by the views in the view hierarchy?
// yes as we go down the hierarchy make these decorations at exactly the points wanted
// and should be pretty simple to collect them and set the editors decorations
// needs something super smart.. https://github.com/Microsoft/vscode/issues/585
// .then(e => e.setDecorations(
// window.createTextEditorDecorationType({
// color: 'rgba(100,200,100,0.5)',
// border: '0.1px solid grey'
// }), [new Range(0, 11, 0, 17)]))
// MINOR: clean up all of this
.then(() => {
return commands.executeCommand('editor.foldLevel2');
});
const uri = MagitStatusView.encodeLocation(magitRepo.rootUri.path);
views.set(uri.toString(), new MagitStatusView(uri, magitRepo.magitState!));
return workspace.openTextDocument(uri).then(doc => window.showTextDocument(doc, { viewColumn: ViewColumn.Beside, preserveFocus, preview: false }))
// TODO LATE PRI: branch highlighting...
// THIS WORKS
// Decorations could be added by the views in the view hierarchy?
// yes as we go down the hierarchy make these decorations at exactly the points wanted
// and should be pretty simple to collect them and set the editors decorations
// needs something super smart.. https://github.com/Microsoft/vscode/issues/585
// .then(e => e.setDecorations(
// window.createTextEditorDecorationType({
// color: 'rgba(100,200,100,0.5)',
// border: '0.1px solid grey'
// }), [new Range(0, 11, 0, 17)]))
// MINOR: clean up all of this
.then(() => {
return commands.executeCommand('editor.foldLevel2');
});
} else {
// Prompt to create repo
await commands.executeCommand('git.init');
magitStatus();
const newRepo = await commands.executeCommand('git.init');
if (newRepo) {
return magitStatus();
}
}
}
else {
// MINOR: could be nice to rather show the list of repos to choose from?
throw new Error('Current file not part of a workspace');
}
}
@ -166,78 +170,73 @@ export async function internalMagitStatus(repository: MagitRepository): Promise<
const rebaseHeadNamePath = Uri.parse(dotGitPath + 'rebase-apply/head-name');
const rebaseOntoPath = Uri.parse(dotGitPath + 'rebase-apply/onto');
const rebaseHeadNameFileTask = repository.state.rebaseCommit ? workspace.fs.readFile(rebaseHeadNamePath).then(f => f.toString().replace(Constants.FinalLineBreakRegex, '')) : undefined;
const rebaseOntoPathFileTask = repository.state.rebaseCommit ? workspace.fs.readFile(rebaseOntoPath).then(f => f.toString().replace(Constants.FinalLineBreakRegex, '')) : undefined;
const rebaseCommitListTask = repository.state.rebaseCommit ? workspace.fs.readFile(Uri.parse(dotGitPath + 'rebase-apply/last')).then(f => f.toString().replace(Constants.FinalLineBreakRegex, '')).then(Number.parseInt)
.then(last => Promise.all(Array.from(Array(last).keys()).map(
index => workspace.fs.readFile(Uri.parse(dotGitPath + 'rebase-apply/' + (index + 1).toString().padStart(4, '0'))).then(f => f.toString().replace(Constants.FinalLineBreakRegex, ''))
.then(GitTextUtils.commitDetailTextToCommit)
))) : undefined;
let rebaseHeadNameFileTask: Thenable<string>;
let rebaseOntoPathFileTask: Thenable<string>;
let rebaseCommitListTask: Thenable<Commit[]> | undefined = undefined;
let rebaseNextIndex: number = 0;
if (repository.state.rebaseCommit) {
rebaseHeadNameFileTask = workspace.fs.readFile(rebaseHeadNamePath).then(f => f.toString().replace(Constants.FinalLineBreakRegex, ''));
rebaseOntoPathFileTask = workspace.fs.readFile(rebaseOntoPath).then(f => f.toString().replace(Constants.FinalLineBreakRegex, ''));
const rebaseLastIndexTask = workspace.fs.readFile(Uri.parse(dotGitPath + 'rebase-apply/last')).then(f => f.toString().replace(Constants.FinalLineBreakRegex, '')).then(Number.parseInt);
rebaseNextIndex = await workspace.fs.readFile(Uri.parse(dotGitPath + 'rebase-apply/next')).then(f => f.toString().replace(Constants.FinalLineBreakRegex, '')).then(Number.parseInt);
const indices: number[] = [];
for (let i = await rebaseLastIndexTask; i > rebaseNextIndex; i--) {
indices.push(i);
}
rebaseCommitListTask =
Promise.all(
indices.map(
index => workspace.fs.readFile(Uri.parse(dotGitPath + 'rebase-apply/' + index.toString().padStart(4, '0'))).then(f => f.toString().replace(Constants.FinalLineBreakRegex, ''))
.then(GitTextUtils.commitDetailTextToCommit)
));
}
// TODO: drop the interesting commit thing?
const commitTasks = Promise.all(
interestingCommits
.map(c => repository.getCommit(c)));
.map(c => getCommit(repository, c)));
//TODO: await stuff only where its needed?
const [commits, workingTreeChanges, indexChanges, mergeChanges, stashes, log] =
await Promise.all([
commitTasks,
workingTreeChangesTasks,
indexChangesTasks,
mergeChangesTasks,
stashTask,
logTask
]);
// TODO: load details about the merging commits, ONLY if there is a mergingState !
// make a nice chain of commands that can be awaited for, like the ones above.
let mergingState;
try {
mergingState =
await Promise.all([
mergeHeadFileTask,
mergeMsgFileTask
])
.then(([mergeHeadFile, mergeMsgFile]) => (GitTextUtils.parseMergeStatus(mergeHeadFile, mergeMsgFile)));
const parsedMergeState = GitTextUtils.parseMergeStatus(await mergeHeadFileTask, await mergeMsgFileTask);
if (parsedMergeState) {
const [mergeCommits, mergingBranches] = parsedMergeState;
mergingState = {
mergingBranches,
commits: await Promise.all(mergeCommits.map(c => getCommit(repository, c)))
};
}
} catch { }
const log = await logTask;
let rebasingState;
if (repository.state.rebaseCommit) {
const ontoCommit = await rebaseOntoPathFileTask!;
const ontoBranch = repository.state.refs.find(ref => ref.commit === ontoCommit && ref.type !== RefType.RemoteHead) as MagitBranch;
const ontoCommit = await getCommit(repository, await rebaseOntoPathFileTask!);
const rebaseCommits = await rebaseCommitListTask;
const ontoBranch = repository.state.refs.find(ref => ref.commit === ontoCommit.hash && ref.type !== RefType.RemoteHead) as MagitBranch;
ontoBranch.commitDetails = ontoCommit;
const doneCommits: Commit[] = [];
const upcomingCommits: Commit[] = [];
let pastCurrent = false;
for (const c of rebaseCommits ?? []) {
if (c.hash === repository.state.rebaseCommit.hash) {
pastCurrent = true;
} else if (pastCurrent) {
upcomingCommits.push(c);
} else {
doneCommits.push(c);
}
}
const doneCommits: Commit[] = log.slice(0, rebaseNextIndex - 1);
const upcomingCommits: Commit[] = (await rebaseCommitListTask) ?? [];
rebasingState = {
currentCommit: repository.state.rebaseCommit,
origBranchName: await rebaseHeadNameFileTask!,
origBranchName: (await rebaseHeadNameFileTask!).split('/')[2],
ontoBranch,
doneCommits,
upcomingCommits
};
}
const commitMap: { [id: string]: Commit; } = commits.reduce((prev, commit) => ({ ...prev, [commit.hash]: commit }), {});
const commitMap: { [id: string]: Commit; } = (await commitTasks).reduce((prev, commit) => ({ ...prev, [commit.hash]: commit }), {});
const HEAD = repository.state.HEAD as MagitBranch | undefined;
@ -251,8 +250,23 @@ export async function internalMagitStatus(repository: MagitRepository): Promise<
// MINOR: clean up?
try {
const remote = await repository.getConfig(`branch.${HEAD.name}.pushRemote`);
HEAD.pushRemote = { remote, name: HEAD!.name! };
const pushRemote = await repository.getConfig(`branch.${HEAD.name}.pushRemote`);
const upstreamRemote = HEAD.upstream?.remote;
const upstreamRemoteCommit = repository.state.refs.find(ref => ref.remote === upstreamRemote && ref.name === `${upstreamRemote}/${HEAD.upstream?.name}`)?.commit;
const upstreamRemoteCommitDetails = upstreamRemoteCommit ? getCommit(repository, upstreamRemoteCommit) : undefined;
const pushRemoteCommit = repository.state.refs.find(ref => ref.remote === pushRemote && ref.name === `${pushRemote}/${HEAD.name}`)?.commit;
const pushRemoteCommitDetails = pushRemoteCommit ? getCommit(repository, pushRemoteCommit) : undefined;
HEAD.pushRemote = { remote: pushRemote, name: HEAD.name!, commit: await pushRemoteCommitDetails };
if (HEAD.upstream) {
HEAD.upstreamRemote = HEAD.upstream;
HEAD.upstreamRemote.commit = await upstreamRemoteCommitDetails;
}
} catch { }
}
@ -263,11 +277,11 @@ export async function internalMagitStatus(repository: MagitRepository): Promise<
repository.magitState = {
HEAD,
stashes,
stashes: await stashTask,
log,
workingTreeChanges,
indexChanges,
mergeChanges,
workingTreeChanges: await workingTreeChangesTasks,
indexChanges: await indexChangesTasks,
mergeChanges: await mergeChangesTasks,
untrackedFiles,
rebasingState,
mergingState,

View File

@ -1,4 +1,4 @@
import { workspace, extensions, commands, ExtensionContext, Disposable, languages } from 'vscode';
import { workspace, extensions, commands, ExtensionContext, Disposable, languages, window } from 'vscode';
import ContentProvider from './providers/contentProvider';
import { GitExtension, API } from './typings/git';
import { pushing } from './commands/pushingCommands';
@ -12,7 +12,7 @@ import { magitStage, magitStageAll, magitUnstageAll, magitUnstage } from './comm
import { saveClose } from './commands/macros';
import FoldingRangeProvider from './providers/foldingRangeProvider';
import HighlightProvider from './providers/highlightProvider';
import { CommandPrimer } from './commands/commandPrimer';
import { Command } from './commands/commandPrimer';
import * as Constants from './common/constants';
import { fetching } from './commands/fetchingCommands';
import { pulling } from './commands/pullingCommands';
@ -21,6 +21,7 @@ import { DocumentView } from './views/general/documentView';
import { magitApplyEntityAtPoint } from './commands/applyCommands';
import { magitDiscardAtPoint } from './commands/discardCommands';
import { merging } from './commands/mergingCommands';
import { rebasing } from './commands/rebasingCommands';
export const magitRepositories: Map<string, MagitRepository> = new Map<string, MagitRepository>();
export const views: Map<string, DocumentView> = new Map<string, DocumentView>();
@ -56,28 +57,43 @@ export function activate(context: ExtensionContext) {
);
context.subscriptions.push(commands.registerCommand('extension.magit', magitStatus));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-refresh', CommandPrimer.primeRepoAndView(magitRefresh)));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-commit', CommandPrimer.primeRepoAndView(magitCommit)));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-visit-at-point', CommandPrimer.primeRepoAndView(magitVisitAtPoint, false)));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-apply-at-point', CommandPrimer.primeRepoAndView(magitApplyEntityAtPoint)));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-discard-at-point', CommandPrimer.primeRepoAndView(magitDiscardAtPoint)));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-refresh', Command.primeRepoAndView(magitRefresh)));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-commit', Command.primeRepoAndView(magitCommit)));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-visit-at-point', Command.primeRepoAndView(magitVisitAtPoint, false)));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-apply-at-point', Command.primeRepoAndView(magitApplyEntityAtPoint)));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-discard-at-point', Command.primeRepoAndView(magitDiscardAtPoint)));
context.subscriptions.push(commands.registerCommand('extension.magit-help', magitHelp));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-pulling', CommandPrimer.primeRepoAndView(pulling)));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-pushing', CommandPrimer.primeRepoAndView(pushing)));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-stashing', CommandPrimer.primeRepoAndView(stashing)));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-pulling', Command.primeRepoAndView(pulling)));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-pushing', Command.primeRepoAndView(pushing)));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-stashing', Command.primeRepoAndView(stashing)));
context.subscriptions.push(commands.registerCommand('extension.magit-fetching', fetching));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-branching', CommandPrimer.primeRepoAndView(branching)));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-merging', CommandPrimer.primeRepoAndView(merging)));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-stage', CommandPrimer.primeRepoAndView(magitStage)));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-stage-all', CommandPrimer.primeRepoAndView(magitStageAll)));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-unstage', CommandPrimer.primeRepoAndView(magitUnstage)));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-unstage-all', CommandPrimer.primeRepoAndView(magitUnstageAll)));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-fetching', Command.primeRepoAndView(fetching)));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-branching', Command.primeRepoAndView(branching)));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-merging', Command.primeRepoAndView(merging)));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-rebasing', Command.primeRepoAndView(rebasing)));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-stage', Command.primeRepoAndView(magitStage)));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-stage-all', Command.primeRepoAndView(magitStageAll)));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-unstage', Command.primeRepoAndView(magitUnstage)));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-unstage-all', Command.primeRepoAndView(magitUnstageAll)));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-file-popup', (textEditor) => {
const file = textEditor.document.uri.fsPath;
// TODO: refactor and show menu
window.showInformationMessage(file);
}));
context.subscriptions.push(commands.registerTextEditorCommand('extension.magit-dispatch', async () => {
// MINOR: this command should be usable without needing to pull up the status view
await magitStatus();
return magitHelp();
}));
context.subscriptions.push(commands.registerCommand('extension.magit-save-and-close-commit-msg', saveClose));
}
export function deactivate() {
// clean up? views, repositories etc??
// MINOR: clean up? views, repositories etc??
}

View File

@ -33,7 +33,15 @@ export class MenuUtil {
_quickPick.items = menu.commands;
// Select with single key stroke
const eventListenerDisposable = _quickPick.onDidChangeValue(async (e) => {
if (_quickPick.value === 'q') {
_quickPick.dispose();
eventListenerDisposable.dispose();
acceptListenerDisposable.dispose();
resolve();
}
const chosenItems = _quickPick.activeItems.filter(i => i.label === _quickPick.value);
_quickPick.value = '';
_quickPick.dispose();
@ -47,7 +55,7 @@ export class MenuUtil {
}
});
// Keep both of these (Select with key or with arrows + enter)
// Select with key or with arrows + enter
const acceptListenerDisposable = _quickPick.onDidAccept(async () => {

View File

@ -2,8 +2,13 @@ import { Branch, Commit, UpstreamRef, Ref } from '../typings/git';
export interface MagitBranch extends Branch {
commitDetails: Commit;
pushRemote?: UpstreamRef;
upstreamRemote: MagitUpstreamRef;
pushRemote?: MagitUpstreamRef;
commitsAhead?: Commit[];
commitsBehind?: Commit[];
tag?: Ref;
}
export interface MagitUpstreamRef extends UpstreamRef {
commit?: Commit;
}

11
src/utils/commitCache.ts Normal file
View File

@ -0,0 +1,11 @@
import { Commit } from '../typings/git';
import { MagitRepository } from '../models/magitRepository';
const commitCache: { [hash: string]: Commit; } = {};
export async function getCommit(repository: MagitRepository, hash: string) {
return commitCache[hash] ?? repository.getCommit(hash).then(c => {
commitCache[c.hash] = c;
return c;
});
}

View File

@ -19,18 +19,17 @@ export default class GitTextUtils {
.map(hunkText => ({ diff: hunkText, diffHeader, uri, section }));
}
public static parseMergeStatus(mergeHashes: string, mergeMessage: string): MagitMergingState | undefined {
public static parseMergeStatus(mergeHashes: string, mergeMessage: string): [string[], string[]] | undefined {
const mergingBranches = mergeMessage.match(/'(.*?)'/g)
?.map(b => b.slice(1, b.length - 1));
const commits = mergeHashes
.replace(Constants.FinalLineBreakRegex, '')
.split(Constants.LineSplitterRegex)
.map(c => ({ hash: c, message: '', parents: [] }));
.split(Constants.LineSplitterRegex);
if (mergingBranches) {
return { commits, mergingBranches };
return [commits, mergingBranches];
}
}

View File

@ -14,7 +14,7 @@ export class BranchSectionView extends View {
this.addSubview(new BranchHeaderView('Head', HEAD));
if (HEAD.upstream) {
this.addSubview(new RemoteBranchHeaderView('Merge', HEAD.upstream));
this.addSubview(new RemoteBranchHeaderView('Merge', HEAD.upstreamRemote));
}
if (HEAD.pushRemote) {

View File

@ -1,15 +1,11 @@
import { TextView } from '../general/textView';
import { UpstreamRef } from '../../typings/git';
import { MagitUpstreamRef } from '../../models/magitBranch';
export class RemoteBranchHeaderView extends TextView {
constructor(name: string, upstreamRef: UpstreamRef) {
constructor(name: string, upstreamRef: MagitUpstreamRef) {
super();
// TODO: ${upstreamRef.commit.message}
// repository.state.refs INNEHOLDER commit hashen for f.eks refs: [{ name: 'origin/master', commit: 'blabla'}]
// LOSNING: MagitBranch blir egen model. Ikke noe arv fra noe den git modellen.
// GitTextUtils.shortCommitMessage(
const nameLabel = `${name}:`.padEnd(10);
this.textContent = `${nameLabel}${upstreamRef.remote}/${upstreamRef.name}`;
this.textContent = `${nameLabel}${upstreamRef.remote}/${upstreamRef.name} ${upstreamRef.commit?.message}`;
}
}

View File

@ -20,8 +20,8 @@ export class CommitSectionView extends View {
export class CommitItemView extends TextView {
constructor(public commit: Commit) {
constructor(public commit: Commit, qualifier?: string) {
super();
this.textContent = `${GitTextUtils.shortHash(commit.hash)} ${GitTextUtils.shortCommitMessage(commit.message)}`;
this.textContent = `${qualifier ? qualifier + ' ' : ''}${GitTextUtils.shortHash(commit.hash)} ${GitTextUtils.shortCommitMessage(commit.message)}`;
}
}

3
src/views/logView.ts Normal file
View File

@ -0,0 +1,3 @@
// TODO: log view

View File

@ -14,6 +14,7 @@ import { MergingSectionView } from './merging/mergingSectionView';
import { UnsourcedCommitSectionView } from './commits/unsourcedCommitsSectionView';
import { MagitRepository } from '../models/magitRepository';
import GitTextUtils from '../utils/gitTextUtils';
import { RebasingSectionView } from './rebasing/rebasingSectionView';
export default class MagitStatusView extends DocumentView {
@ -41,25 +42,8 @@ export default class MagitStatusView extends DocumentView {
this.addSubview(new MergingSectionView(magitState.mergingState));
}
// TODO: Rebasing status
// Data location:
//
// index of next commit in rebase:
// .git/rebase-apply/next
//
if (magitState.rebasingState) {
// TODO: refactor into own view
this.addSubview(
new TextView(`Rebasing ${magitState.rebasingState.origBranchName} onto ${magitState.rebasingState.ontoBranch.name}`),
...magitState.rebasingState.upcomingCommits.map(c => new CommitItemView(c)),
new TextView(`join ${GitTextUtils.shortHash(magitState.rebasingState.currentCommit.hash)} ${GitTextUtils.shortCommitMessage(magitState.rebasingState.currentCommit.message)}`),
// TODO: Wrong order, wrong hashes! fix in data steps
...magitState.rebasingState.doneCommits.map(c => new CommitItemView(c)),
new TextView(`done ${GitTextUtils.shortHash(magitState.HEAD?.commitDetails.hash)} ${GitTextUtils.shortCommitMessage(magitState.HEAD?.commitDetails.message)}`),
new TextView(`onto ${GitTextUtils.shortHash(magitState.rebasingState.ontoBranch.commit)} ${GitTextUtils.shortCommitMessage(magitState.rebasingState.ontoBranch.commitDetails?.message)}`),
new LineBreakView()
);
this.addSubview(new RebasingSectionView(magitState.rebasingState));
}
if (magitState.untrackedFiles.length) {

View File

@ -1,6 +1,4 @@
import { View } from '../general/view';
import { Section, SectionHeaderView } from '../general/sectionHeader';
import { Stash } from '../../common/gitApiExtensions';
import { TextView } from '../general/textView';
import { LineBreakView } from '../general/lineBreakView';
import { MagitMergingState } from '../../models/magitMergingState';

View File

@ -0,0 +1,23 @@
import { View } from '../general/view';
import { Section, SectionHeaderView } from '../general/sectionHeader';
import { TextView } from '../general/textView';
import { LineBreakView } from '../general/lineBreakView';
import { CommitItemView } from '../commits/commitSectionView';
import { MagitRebasingState } from '../../models/magitRebasingState';
import GitTextUtils from '../../utils/gitTextUtils';
export class RebasingSectionView extends View {
isFoldable = true;
constructor(rebasingState: MagitRebasingState) {
super();
this.subViews = [
new TextView(`Rebasing ${rebasingState.origBranchName} onto ${rebasingState.ontoBranch.name}`),
...rebasingState.upcomingCommits.map(c => new CommitItemView(c, 'pick')),
new CommitItemView(rebasingState.currentCommit, 'join'),
...rebasingState.doneCommits.map(c => new CommitItemView(c, 'done')),
new CommitItemView(rebasingState.ontoBranch.commitDetails, 'onto'),
new LineBreakView()
];
}
}