Skip to content

Commit bb63619

Browse files
author
ole
authored
Allow adding tags or branch from Git History view (#419)
* Fixed scrolling in dropdown list #417 * Version 0.4.12 * Allow adding tags from git history view #418
1 parent 1764e4d commit bb63619

File tree

17 files changed

+142
-47
lines changed

17 files changed

+142
-47
lines changed

browser/src/actions/results.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,15 @@ export const actionACommit = (logEntry: LogEntry) => {
3434
};
3535
};
3636

37+
export const actionNewRef = (logEntry: LogEntry) => {
38+
// tslint:disable-next-line:no-any
39+
return async (dispatch: Dispatch<any>, getState: () => RootState) => {
40+
const state = getState();
41+
const url = getQueryUrl(state, `/branch/${logEntry.hash.full}`);
42+
return axios.post(url, logEntry);
43+
};
44+
};
45+
3746
// tslint:disable-next-line:no-any
3847
export const fetchAvatars = async (dispatch: Dispatch<any>, getState: () => RootState) => {
3948
const state = getState();

browser/src/components/LogView/LogEntry/index.tsx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@ import { gitmojify } from '../gitmojify';
99
import HeadRef from '../Refs/Head';
1010
import RemoteRef from '../Refs/Remote';
1111
import TagRef from '../Refs/Tag';
12-
import { GoGitCommit, GoClippy } from 'react-icons/lib/go';
12+
import { GoGitCommit, GoClippy, GoPlus } from 'react-icons/lib/go';
1313

1414
type ResultListPropsSentToComponent = {
1515
logEntry: LogEntry;
1616
onViewCommit(entry: LogEntry): void;
1717
onClick(entry: LogEntry): void;
18+
onNewClick(entry: LogEntry): void;
1819
};
1920

2021
type ResultListProps = ResultListPropsSentToComponent & {
@@ -47,22 +48,29 @@ function LogEntry(props: ResultListProps) {
4748
<div className='media right'>
4849
<div className='media-image'>
4950
<div className='commit-hash-container'>
51+
<div>
52+
<span className='btnx hint--left hint--rounded hint--bounce' aria-label='New branch or tag'>
53+
<a role='button' onClick={() => props.onNewClick(props.logEntry)}>
54+
<GoPlus></GoPlus>
55+
</a>
56+
</span>
57+
</div>
5058
<CopyToClipboard text={props.logEntry.hash.full}>
51-
<div className='copy-button'>
59+
<div>
5260
<span className='btnx clipboard hint--left hint--rounded hint--bounce'
5361
aria-label='Copy the full Hash'>
5462
<GoClippy></GoClippy>
5563
</span>
5664
</div>
5765
</CopyToClipboard>
58-
<div className='cherry-pick-button'>
59-
<span className='btnx hint--left hint--rounded hint--bounce' aria-label='Cherry pick, Compare, etc'><span aria-label='Cherry pick, Compare, etc' />
66+
<div>
67+
<span className='btnx hint--left hint--rounded hint--bounce' aria-label='Cherry pick, Compare, etc'>
6068
<a role='button' onClick={() => props.onClick(props.logEntry)}>
6169
<GoGitCommit></GoGitCommit>
6270
</a>
6371
</span>
6472
</div>
65-
<div role='button' className='commit-hash' onClick={() => props.onViewCommit(props.logEntry)}>
73+
<div role='button' onClick={() => props.onViewCommit(props.logEntry)}>
6674
<span className='sha-code short' aria-label={props.logEntry.hash.short}>{props.logEntry.hash.short}</span>
6775
</div>
6876
</div>

browser/src/components/LogView/LogEntryList/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ interface ResultProps {
66
logEntries: LogEntry[];
77
onViewCommit(entry: LogEntry): void;
88
onClick(entry: LogEntry): void;
9+
onNewClick(entry: LogEntry): void;
910
}
1011

1112
export default class LogEntryList extends React.Component<ResultProps> {
@@ -30,6 +31,7 @@ export default class LogEntryList extends React.Component<ResultProps> {
3031
key={entry.hash.full}
3132
logEntry={entry}
3233
onViewCommit={this.props.onViewCommit}
34+
onNewClick={this.props.onNewClick}
3335
onClick={this.props.onClick} />
3436
);
3537
return (

browser/src/components/LogView/LogView/index.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ type LogViewProps = {
1111
commitsRendered: typeof ResultActions.commitsRendered;
1212
onViewCommit: typeof ResultActions.selectCommit;
1313
actionACommit: typeof ResultActions.actionACommit;
14+
actionNewRef: typeof ResultActions.actionNewRef;
1415
};
1516

1617
// tslint:disable-next-line:no-empty-interface
@@ -47,6 +48,7 @@ class LogView extends React.Component<LogViewProps, LogViewState> {
4748
<BranchGraph></BranchGraph>
4849
<LogEntryList ref={this.ref} logEntries={this.props.logEntries.items}
4950
onClick={this.onClick}
51+
onNewClick={this.onNewClick}
5052
onViewCommit={this.onViewCommit}></LogEntryList>
5153
</div>
5254
);
@@ -58,6 +60,9 @@ class LogView extends React.Component<LogViewProps, LogViewState> {
5860
public onClick = (entry: LogEntry) => {
5961
this.props.actionACommit(entry);
6062
}
63+
public onNewClick = (entry: LogEntry) => {
64+
this.props.actionNewRef(entry);
65+
}
6166
}
6267
function mapStateToProps(state: RootState, wrapper: { logEntries: LogEntries }) {
6368
return {
@@ -71,6 +76,7 @@ function mapDispatchToProps(dispatch) {
7176
// fetchData: (pageIndex: number) => dispatch(ResultActions.fetchLogEntries(pageIndex))
7277
commitsRendered: (height: number) => dispatch(ResultActions.commitsRendered(height)),
7378
onViewCommit: (hash: string) => dispatch(ResultActions.selectCommit(hash)),
79+
actionNewRef: (logEntry: LogEntry) => dispatch(ResultActions.actionNewRef(logEntry)),
7480
actionACommit: (logEntry: LogEntry) => dispatch(ResultActions.actionACommit(logEntry))
7581
};
7682
}

browser/src/main.css

Lines changed: 5 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,6 @@ body {
5050
margin: 0 1em 0 0;
5151
}
5252

53-
.xxsss{
54-
background-image: url('../resources/icons/dark/status-added.svg')
55-
}
56-
5753
.media .media-content {
5854
flex: 1;
5955
}
@@ -86,14 +82,12 @@ div.appRoot {
8682
height: 100vh;
8783
}
8884

89-
9085
.log-entry {
9186
border-bottom-width: 1px;
9287
border-bottom-style: solid;
9388
border-bottom-color: var(--vscode-editorGroup-border);
9489
}
9590

96-
9791
#log-view.with-details {
9892
height: calc(100% - 20rem);
9993
}
@@ -241,50 +235,21 @@ div.appRoot {
241235
padding-right: 0.2em;
242236
}
243237

244-
.commit-hash-container .commit-hash,
245-
.commit-hash-container .copy-button {
238+
.commit-hash-container > div {
246239
display: inline-block;
247240
color: var(--vscode-input-foreground);
248241
padding: 0.2em;
249242
cursor: pointer;
250243
}
251244

252-
.commit-hash-container .cherry-pick-button {
253-
display: inline-block;
245+
.commit-hash-container svg {
254246
color: var(--vscode-input-foreground);
255-
padding: 0.2em;
256247
}
257248

258-
.commit-hash-container .commit-hash {
259-
padding-right: 0.4em;
260-
}
261-
262-
.cherry-pick-button a,
263-
.cherry-pick-button a:hover {
264-
color: white;
265-
}
266-
267-
.commit-hash-container .cherry-pick-button {
268-
display: inline-block;
269-
color: var(--vscode-input-foreground);
270-
padding: 0.2em;
271-
}
272-
273-
.cherry-pick-button a,
274-
.cherry-pick-button a:hover {
275-
color: var(--vscode-input-foreground);
276-
}
277-
278-
.commit-hash-container .commit-hash:hover {
249+
.commit-hash-container > div:hover {
279250
background-color: hsla(0, 0%, 100%, 0.12);
280251
}
281252

282-
.commit-hash-container .copy-button {
283-
position: relative;
284-
/* compensate for octicon margin */
285-
padding-left: 0.6em;
286-
}
287-
288253
.copy-button .clipboard {
289254
opacity: 0.8;
290255
}
@@ -468,7 +433,7 @@ div.details-view-cnt .commit-notes:empty {
468433

469434
#detail-view {
470435
display: flex;
471-
overflow-y: scroll;
436+
overflow-y: auto;
472437
flex-flow: row wrap;
473438
justify-content: space-between;
474439
max-height: 100%;
@@ -486,6 +451,7 @@ div.details-view-cnt .commit-notes:empty {
486451

487452
#detail-view .action-btn {
488453
margin-left: 1rem;
454+
margin-right: 1rem;
489455
display: inline-block;
490456
cursor: pointer;
491457
opacity: 0.8;

src/adapter/repository/git.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,9 @@ export class Git implements IGitService {
417417
public async createBranch(branchName: string, hash: string): Promise<void> {
418418
await this.exec('checkout', '-b', branchName, hash);
419419
}
420+
public async createTag(tagName: string, hash: string): Promise<void> {
421+
await this.exec('tag', '-a', tagName, '-m', tagName, hash);
422+
}
420423
public async merge(hash: string): Promise<void> {
421424
await this.exec('merge', hash);
422425
}

src/commandFactories/commitFactory.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { inject, injectable } from 'inversify';
2-
import { IGitBranchFromCommitCommandHandler, IGitCheckoutCommandHandler, IGitCherryPickCommandHandler, IGitCommitViewDetailsCommandHandler, IGitCompareCommandHandler, IGitMergeCommandHandler, IGitRebaseCommandHandler, IGitRevertCommandHandler } from '../commandHandlers/types';
2+
import { IGitBranchFromCommitCommandHandler, IGitCheckoutCommandHandler, IGitCherryPickCommandHandler, IGitCommitViewDetailsCommandHandler, IGitCompareCommandHandler, IGitMergeCommandHandler, IGitRebaseCommandHandler, IGitRevertCommandHandler, IGitTagFromCommitCommandHandler } from '../commandHandlers/types';
33
import { CheckoutCommand } from '../commands/commit/checkout';
44
import { CherryPickCommand } from '../commands/commit/cherryPick';
55
import { CompareCommand } from '../commands/commit/compare';
66
import { CreateBranchCommand } from '../commands/commit/createBranch';
7+
import { CreateTagCommand } from '../commands/commit/createTag';
78
import { MergeCommand } from '../commands/commit/merge';
89
import { RebaseCommand } from '../commands/commit/rebase';
910
import { RevertCommand } from '../commands/commit/revert';
@@ -15,6 +16,7 @@ import { ICommitCommandFactory } from './types';
1516
@injectable()
1617
export class CommitCommandFactory implements ICommitCommandFactory {
1718
constructor( @inject(IGitBranchFromCommitCommandHandler) private branchCreationCommandHandler: IGitBranchFromCommitCommandHandler,
19+
@inject(IGitTagFromCommitCommandHandler) private tagCreationCommandHandler: IGitTagFromCommitCommandHandler,
1820
@inject(IGitCherryPickCommandHandler) private cherryPickHandler: IGitCherryPickCommandHandler,
1921
@inject(IGitCheckoutCommandHandler) private checkoutHandler: IGitCheckoutCommandHandler,
2022
@inject(IGitCompareCommandHandler) private compareHandler: IGitCompareCommandHandler,
@@ -24,7 +26,6 @@ export class CommitCommandFactory implements ICommitCommandFactory {
2426
@inject(IGitCommitViewDetailsCommandHandler) private viewChangeLogHandler: IGitCommitViewDetailsCommandHandler) { }
2527
public async createCommands(commit: CommitDetails): Promise<ICommand<CommitDetails>[]> {
2628
const commands: ICommand<CommitDetails>[] = [
27-
new CreateBranchCommand(commit, this.branchCreationCommandHandler),
2829
new CherryPickCommand(commit, this.cherryPickHandler),
2930
new CheckoutCommand(commit, this.checkoutHandler),
3031
new ViewDetailsCommand(commit, this.viewChangeLogHandler),
@@ -43,4 +44,19 @@ export class CommitCommandFactory implements ICommitCommandFactory {
4344
.filter(cmd => !!cmd)
4445
.map(cmd => cmd!);
4546
}
47+
48+
public async createNewCommands(commit: CommitDetails): Promise<ICommand<CommitDetails>[]> {
49+
const commands: ICommand<CommitDetails>[] = [
50+
new CreateBranchCommand(commit, this.branchCreationCommandHandler),
51+
new CreateTagCommand(commit, this.tagCreationCommandHandler)
52+
];
53+
54+
return (await Promise.all(commands.map(async cmd => {
55+
const result = cmd.preExecute();
56+
const available = typeof result === 'boolean' ? result : await result;
57+
return available ? cmd : undefined;
58+
})))
59+
.filter(cmd => !!cmd)
60+
.map(cmd => cmd!);
61+
}
4662
}

src/commandFactories/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const ICommitCommandFactory = Symbol('ICommitCommandFactory');
1111

1212
export interface ICommitCommandFactory {
1313
createCommands(data: CommitDetails): Promise<ICommand<CommitDetails>[]>;
14+
createNewCommands(data: CommitDetails): Promise<ICommand<CommitDetails>[]>;
1415
}
1516

1617
export const IBranchCommandFactory = Symbol('IBranchCommandFactory');

src/commandHandlers/commit/gitCommit.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ export class GitCommitCommandHandler implements IGitCommitCommandHandler {
1717
return cmd.execute();
1818
}
1919
}
20+
@command('git.commit.doNewRef', IGitCommitCommandHandler)
21+
public async doNewRef(commit: CommitDetails) {
22+
const cmd = await this.serviceContainer.get<IUiService>(IUiService).newRefCommitCommandAction(commit);
23+
if (cmd) {
24+
return cmd.execute();
25+
}
26+
}
2027
@command('git.commit.selected', IGitCommitCommandHandler)
2128
public onCommitSelected(commit: CommitDetails) {
2229
const viewer = this.commitViewerFactory.getCommitViewer();
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { inject, injectable } from 'inversify';
2+
import { IApplicationShell } from '../../application/types';
3+
import { CommitDetails } from '../../common/types';
4+
import { IServiceContainer } from '../../ioc/types';
5+
import { IGitServiceFactory } from '../../types';
6+
import { ICommitViewerFactory } from '../../viewers/types';
7+
import { command } from '../registration';
8+
import { IGitTagFromCommitCommandHandler } from '../types';
9+
10+
@injectable()
11+
export class GitTagFromCommitCommandHandler implements IGitTagFromCommitCommandHandler {
12+
constructor( @inject(IServiceContainer) private serviceContainer: IServiceContainer,
13+
@inject(ICommitViewerFactory) private commitViewerFactory: ICommitViewerFactory,
14+
@inject(IApplicationShell) private applicationShell: IApplicationShell) { }
15+
16+
@command('git.commit.createTag', IGitTagFromCommitCommandHandler)
17+
public async createTagFromCommit(commit: CommitDetails, newTagName?: string) {
18+
commit = commit ? commit : this.commitViewerFactory.getCommitViewer().selectedCommit;
19+
const msg = 'Tag name';
20+
const description = 'Please provide a tag name';
21+
newTagName = (typeof newTagName !== 'string' || newTagName.trim().length === 0) ? await this.applicationShell.showInputBox({ placeHolder: msg, prompt: description }) : newTagName;
22+
23+
if (typeof newTagName !== 'string' || newTagName.length === 0) {
24+
return;
25+
}
26+
27+
const gitService = await this.serviceContainer.get<IGitServiceFactory>(IGitServiceFactory).createGitService(commit.workspaceFolder, commit.logEntry.gitRoot);
28+
gitService.createTag(newTagName, commit.logEntry.hash.full)
29+
.catch(async err => {
30+
this.applicationShell.showErrorMessage(err);
31+
});
32+
}
33+
}

0 commit comments

Comments
 (0)