Skip to content

Commit 74107c2

Browse files
ole1986DonJayamanne
authored andcommitted
Cherrypick (DonJayamanne#141)
* added new method "getGitBranch" to read current branch name * fixed refspec containing slashes (/) in local repo * git.viewHistory per branch, cache results and force reload when reexecute command * support for "cherrypick" in log viewer
1 parent 50d5b7b commit 74107c2

File tree

5 files changed

+114
-0
lines changed

5 files changed

+114
-0
lines changed

resources/main.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,17 @@ body {
153153
color: white;
154154
padding: 0.4em;
155155
}
156+
157+
.commit-hash-container .cherry-pick-button {
158+
display: inline-block;
159+
color: white;
160+
padding: 0.4em;
161+
}
162+
163+
.cherry-pick-button a, .cherry-pick-button a:hover {
164+
color: white;
165+
}
166+
156167
.commit-hash-container .commit-hash:hover {
157168
background-color: hsla(0,0%,100%,0.12);
158169
}

src/contracts.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ export interface LogEntry {
2323
isHead: boolean;
2424
}
2525

26+
export interface CherryPickEntry {
27+
branch: string;
28+
sha: string;
29+
}
30+
2631
export interface Sha1 {
2732
full: string;
2833
short: string;

src/helpers/gitCherryPick.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { window } from 'vscode';
2+
import { spawn } from 'child_process';
3+
import { getGitPath, getGitBranch } from './gitPaths';
4+
import { CherryPickEntry } from '../contracts';
5+
import * as logger from '../logger';
6+
7+
export async function CherryPick(rootDir: string, branch: string, sha: string): Promise<CherryPickEntry> {
8+
const args = ['cherry-pick', sha];
9+
// This is how you can view the log across all branches
10+
const gitPath = await getGitPath();
11+
let newBranch = await getGitBranch(rootDir);
12+
13+
const yesNo = await window.showQuickPick(['Yes', 'No'], { placeHolder: 'Cherry pick ' + sha.substr(0, 7) + ' into ' + newBranch + '?' });
14+
return new Promise<CherryPickEntry>((resolve, reject) => {
15+
const options = { cwd: rootDir };
16+
if (yesNo === undefined || yesNo === 'No') {
17+
return;
18+
}
19+
if (newBranch === branch) {
20+
reject('Cannot cherry-pick into same branch (' + newBranch + '). Please checkout a different branch first');
21+
return;
22+
}
23+
let error = '';
24+
let entry = {} as CherryPickEntry;
25+
26+
logger.logInfo('git ' + args.join(' '));
27+
let ls = spawn(gitPath, args, options);
28+
29+
ls.stdout.setEncoding('utf8');
30+
ls.stdout.on('data', (data: string) => {
31+
let m = data.match(/\[(\w+) ([0-9a-z]{7})\]/);
32+
if (m) {
33+
entry.branch = m[1];
34+
entry.sha = m[2];
35+
}
36+
});
37+
38+
ls.stderr.setEncoding('utf8');
39+
ls.stderr.on('data', (data: string) => {
40+
error = data;
41+
});
42+
43+
ls.on('error', function (error) {
44+
logger.logError(error);
45+
reject(error);
46+
return;
47+
});
48+
49+
ls.on('close', () => {
50+
if (error.length > 0 || entry.branch.length <= 0) {
51+
CherryPickAbort(rootDir);
52+
reject(error);
53+
return;
54+
}
55+
resolve(entry);
56+
});
57+
});
58+
}
59+
60+
export async function CherryPickAbort(rootDir: string): Promise<null> {
61+
const args = ['cherry-pick', '--abort'];
62+
// This is how you can view the log across all branches
63+
const gitPath = await getGitPath();
64+
65+
return new Promise<null>((resolve, reject) => {
66+
const options = { cwd: rootDir };
67+
68+
logger.logInfo('git ' + args.join(' '));
69+
let ls = spawn(gitPath, args, options);
70+
71+
ls.on('error', function (error) {
72+
logger.logError(error);
73+
reject(error);
74+
return;
75+
});
76+
77+
ls.on('close', () => {
78+
resolve();
79+
});
80+
});
81+
}

src/logViewer/htmlGenerator.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,13 @@ export function generateHistoryHtmlView(entries: LogEntry[], canGoPrevious: bool
135135
<i class="octicon octicon-clippy"></i>
136136
</span>
137137
</div>
138+
<div class="cherry-pick-button">
139+
<span class="btn hint--bottom hint--rounded hint--bounce" aria-label="Cherry pick into branch"><span aria-label="Cherry pick into branch">
140+
<a href="${encodeURI('command:git.cherry-pick-into?' + JSON.stringify([entry.headRef, entry.sha1.full]))}">
141+
<i class="octicon octicon-git-pull-request"></i>
142+
</a>
143+
</span>
144+
</div>
138145
<div class="commit-hash">
139146
<span class="sha-code short" data-entry-index="${entryIndex}" aria-label="${entry.sha1.short}">${entry.sha1.short}</span>
140147
</div>

src/logViewer/logViewer.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as vscode from 'vscode';
22
import * as htmlGenerator from './htmlGenerator';
33
import * as gitHistory from '../helpers/gitHistory';
4+
import * as gitCherryPick from '../helpers/gitCherryPick';
45
import { LogEntry } from '../contracts';
56
import * as path from 'path';
67
import * as gitPaths from '../helpers/gitPaths';
@@ -146,6 +147,15 @@ export function activate(context: vscode.ExtensionContext) {
146147
});
147148
context.subscriptions.push(disposable, registration);
148149

150+
disposable = vscode.commands.registerCommand('git.cherry-pick-into', (branch: string, sha: string) => {
151+
gitCherryPick.CherryPick(vscode.workspace.rootPath, branch, sha).then((value) => {
152+
vscode.window.showInformationMessage('Cherry picked into ' + value.branch + ' (' + value.sha + ')');
153+
}, (reason) => {
154+
vscode.window.showErrorMessage(reason);
155+
});
156+
});
157+
context.subscriptions.push(disposable);
158+
149159
disposable = vscode.commands.registerCommand('git.logNavigate', (direction: string) => {
150160
pageIndex = pageIndex + (direction === 'next' ? 1 : -1);
151161
provider.update(previewUri);

0 commit comments

Comments
 (0)