Skip to content

Commit 382eadc

Browse files
author
ole
authored
Use native git api to get local branches (Also fixes #572 and #601) (#600)
* execute git show with config "core.quotePath=false" for file listings (#572) * Use native "getBranches" from gite RepositoryState * Wrap long commit message #601 * hide the Git View (chooser) command from command palette
1 parent 7b1b9ad commit 382eadc

File tree

7 files changed

+149
-166
lines changed

7 files changed

+149
-166
lines changed

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

Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -110,40 +110,44 @@ class Commit extends React.Component<CommitProps, CommitState> {
110110
>
111111
<div id="detail-view">
112112
<div className="authorAndCommitInfoContainer">
113-
<Avatar result={this.props.selectedEntry.author}></Avatar>
114-
<h1 className="commit-subject">
115-
{gitmojify(this.props.selectedEntry.subject)}
116-
&nbsp;
117-
<CopyToClipboard
118-
text={this.props.selectedEntry.subject + '\n' + this.props.selectedEntry.body}
119-
>
120-
<span
121-
className="btnx clipboard hint--right hint--rounded hint--bounce"
122-
aria-label="Copy commit text"
113+
<div style={{ minWidth: '80px' }}>
114+
<Avatar result={this.props.selectedEntry.author}></Avatar>
115+
</div>
116+
<div style={{ flexGrow: 1 }}>
117+
<h1 className="commit-subject">
118+
{gitmojify(this.props.selectedEntry.subject)}
119+
&nbsp;
120+
<CopyToClipboard
121+
text={this.props.selectedEntry.subject + '\n' + this.props.selectedEntry.body}
123122
>
124-
<GoClippy></GoClippy>
125-
</span>
126-
</CopyToClipboard>
127-
</h1>
128-
<Author result={this.props.selectedEntry.author}></Author>
129-
<div className="commit-body">{gitmojify(this.props.selectedEntry.body)}</div>
130-
<div className="commit-notes">{gitmojify(this.props.selectedEntry.notes)}</div>
131-
</div>
132-
<div className="actions">
133-
<input
134-
ref={x => {
135-
this.ref = x;
136-
}}
137-
className={'textInput'}
138-
type="text"
139-
value={this.state.searchText}
140-
placeholder="Find file"
141-
onKeyDown={this.handleKeyDown}
142-
onChange={this.handleSearchChange}
143-
/>
144-
<a role="button" className="action-btn close-btn" onClick={this.onClose}>
145-
<GoX></GoX>
146-
</a>
123+
<span
124+
className="btnx clipboard hint--right hint--rounded hint--bounce"
125+
aria-label="Copy commit text"
126+
>
127+
<GoClippy></GoClippy>
128+
</span>
129+
</CopyToClipboard>
130+
</h1>
131+
<Author result={this.props.selectedEntry.author}></Author>
132+
<div className="commit-body">{gitmojify(this.props.selectedEntry.body)}</div>
133+
<div className="commit-notes">{gitmojify(this.props.selectedEntry.notes)}</div>
134+
</div>
135+
<div className="actions">
136+
<input
137+
ref={x => {
138+
this.ref = x;
139+
}}
140+
className={'textInput'}
141+
type="text"
142+
value={this.state.searchText}
143+
placeholder="Find file"
144+
onKeyDown={this.handleKeyDown}
145+
onChange={this.handleSearchChange}
146+
/>
147+
<a role="button" className="action-btn close-btn" onClick={this.onClose}>
148+
<GoX></GoX>
149+
</a>
150+
</div>
147151
</div>
148152
<div className="comitted-files">{this.renderFileEntries()}</div>
149153
</div>

browser/src/main.css

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ div.details-view-cnt .commit-subject {
416416
div.details-view-cnt .commit-body,
417417
div.details-view-cnt .commit-notes {
418418
margin: 1em 0;
419-
white-space: pre;
419+
/*white-space: pre;*/
420420
}
421421

422422
div.details-view-cnt .commit-body:empty,
@@ -433,7 +433,9 @@ div.details-view-cnt .commit-notes:empty {
433433
}
434434

435435
#detail-view .authorAndCommitInfoContainer {
436-
flex: 1 1;
436+
flex: 1 1;
437+
display: flex;
438+
justify-content: space-between;
437439
}
438440
#detail-view .authorAndCommitInfoContainer img.avatar {
439441
height:80px;
@@ -457,12 +459,7 @@ div.details-view-cnt .commit-notes:empty {
457459
}
458460

459461
#detail-view div.actions {
460-
position: sticky;
461-
top: 0px;
462-
align-self: flex-start;
463462
white-space: nowrap;
464-
flex: 0 1;
465-
text-align: right;
466463
}
467464

468465
#detail-view .comitted-files {

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,10 @@
194194
{
195195
"command": "git.commit.compare.view.show",
196196
"when": "!git.commit.compare.view.show"
197+
},
198+
{
199+
"command": "git.viewChooser",
200+
"when": "false"
197201
}
198202
],
199203
"editor/title": [

src/adapter/repository/git.d.ts

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { CancellationToken, Event, SourceControlInputBox, Uri } from 'vscode';
6+
import { Uri, Event, Disposable, ProviderResult } from 'vscode';
7+
export { ProviderResult } from 'vscode';
78

89
export interface Git {
910
readonly path: string;
@@ -41,7 +42,10 @@ export interface Commit {
4142
readonly hash: string;
4243
readonly message: string;
4344
readonly parents: string[];
44-
readonly authorEmail?: string | undefined;
45+
readonly authorDate?: Date;
46+
readonly authorName?: string;
47+
readonly authorEmail?: string;
48+
readonly commitDate?: Date;
4549
}
4650

4751
export interface Submodule {
@@ -110,9 +114,29 @@ export interface RepositoryUIState {
110114
readonly onDidChange: Event<void>;
111115
}
112116

117+
/**
118+
* Log options.
119+
*/
113120
export interface LogOptions {
114-
// Max number of log entries to retrieve. If not specified, the default is 32.
121+
/** Max number of log entries to retrieve. If not specified, the default is 32. */
115122
readonly maxEntries?: number;
123+
readonly path?: string;
124+
}
125+
126+
export interface CommitOptions {
127+
all?: boolean | 'tracked';
128+
amend?: boolean;
129+
signoff?: boolean;
130+
signCommit?: boolean;
131+
empty?: boolean;
132+
noVerify?: boolean;
133+
}
134+
135+
export interface BranchQuery {
136+
readonly remote?: boolean;
137+
readonly pattern?: string;
138+
readonly count?: number;
139+
readonly contains?: string;
116140
}
117141

118142
export interface Repository {
@@ -153,6 +177,7 @@ export interface Repository {
153177
createBranch(name: string, checkout: boolean, ref?: string): Promise<void>;
154178
deleteBranch(name: string, force?: boolean): Promise<void>;
155179
getBranch(name: string): Promise<Branch>;
180+
getBranches(query: BranchQuery): Promise<Ref[]>;
156181
setBranchUpstream(name: string, upstream: string): Promise<void>;
157182

158183
getMergeBase(ref1: string, ref2: string): Promise<string>;
@@ -162,13 +187,48 @@ export interface Repository {
162187

163188
addRemote(name: string, url: string): Promise<void>;
164189
removeRemote(name: string): Promise<void>;
190+
renameRemote(name: string, newName: string): Promise<void>;
165191

166192
fetch(remote?: string, ref?: string, depth?: number): Promise<void>;
167193
pull(unshallow?: boolean): Promise<void>;
168194
push(remoteName?: string, branchName?: string, setUpstream?: boolean): Promise<void>;
169195

170196
blame(path: string): Promise<string>;
171197
log(options?: LogOptions): Promise<Commit[]>;
198+
199+
commit(message: string, opts?: CommitOptions): Promise<void>;
200+
}
201+
202+
export interface RemoteSource {
203+
readonly name: string;
204+
readonly description?: string;
205+
readonly url: string | string[];
206+
}
207+
208+
export interface RemoteSourceProvider {
209+
readonly name: string;
210+
readonly icon?: string; // codicon name
211+
readonly supportsQuery?: boolean;
212+
getRemoteSources(query?: string): ProviderResult<RemoteSource[]>;
213+
publishRepository?(repository: Repository): Promise<void>;
214+
}
215+
216+
export interface Credentials {
217+
readonly username: string;
218+
readonly password: string;
219+
}
220+
221+
export interface CredentialsProvider {
222+
getCredentials(host: Uri): ProviderResult<Credentials>;
223+
}
224+
225+
export interface PushErrorHandler {
226+
handlePushError(
227+
repository: Repository,
228+
remote: Remote,
229+
refspec: string,
230+
error: Error & { gitErrorCode: GitErrorCodes },
231+
): Promise<boolean>;
172232
}
173233

174234
export type APIState = 'uninitialized' | 'initialized';
@@ -180,6 +240,14 @@ export interface API {
180240
readonly repositories: Repository[];
181241
readonly onDidOpenRepository: Event<Repository>;
182242
readonly onDidCloseRepository: Event<Repository>;
243+
244+
toGitUri(uri: Uri, ref: string): Uri;
245+
getRepository(uri: Uri): Repository | null;
246+
init(root: Uri): Promise<Repository | null>;
247+
248+
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable;
249+
registerCredentialsProvider(provider: CredentialsProvider): Disposable;
250+
registerPushErrorHandler(handler: PushErrorHandler): Disposable;
183251
}
184252

185253
export interface GitExtension {
@@ -216,6 +284,7 @@ export const enum GitErrorCodes {
216284
CantOpenResource = 'CantOpenResource',
217285
GitNotFound = 'GitNotFound',
218286
CantCreatePipe = 'CantCreatePipe',
287+
PermissionDenied = 'PermissionDenied',
219288
CantAccessRemote = 'CantAccessRemote',
220289
RepositoryNotFound = 'RepositoryNotFound',
221290
RepositoryIsLocked = 'RepositoryIsLocked',

src/adapter/repository/git.ts

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,12 @@ import { ITEM_ENTRY_SEPARATOR, LOG_ENTRY_SEPARATOR, LOG_FORMAT_ARGS } from './co
1313
import { Repository, RefType } from './git.d';
1414
import { GitOriginType } from './index';
1515
import { IGitArgsService } from './types';
16-
import { GitBranchesService } from './gitBranchService';
1716
import { GitRemoteService } from './gitRemoteService';
1817
import { captureTelemetry } from '../../common/telemetry';
1918

2019
@injectable()
2120
export class Git implements IGitService {
2221
private refHashesMap: Map<string, string> = new Map<string, string>();
23-
private readonly branchesService: GitBranchesService;
2422
private readonly remotesService: GitRemoteService;
2523
constructor(
2624
private repo: Repository,
@@ -30,7 +28,6 @@ export class Git implements IGitService {
3028
@inject(IGitArgsService) private gitArgsService: IGitArgsService,
3129
) {
3230
this.remotesService = new GitRemoteService(repo, this.gitCmdExecutor);
33-
this.branchesService = new GitBranchesService(repo, this.remotesService);
3431
}
3532

3633
/**
@@ -63,9 +60,36 @@ export class Git implements IGitService {
6360
}
6461

6562
@captureTelemetry()
66-
public async getBranches(): Promise<Branch[]> {
67-
return this.branchesService.getBranches();
63+
public async getBranches(withRemote = false): Promise<Branch[]> {
64+
const gitRoot = this.getGitRoot();
65+
66+
// get only the local branches
67+
const branches = await this.repo.getBranches({ remote: withRemote });
68+
69+
return Promise.all(
70+
branches.map(async r => {
71+
let remoteUrl: string | undefined = undefined;
72+
73+
// fetch the remote from local branch using upstreamRef (received only with getBranch)
74+
const remoteName = (await this.repo.getBranch(r.name || 'master')).upstream?.remote;
75+
76+
if (remoteName) {
77+
remoteUrl = this.repo.state.remotes.find(x => x.name === remoteName)?.fetchUrl;
78+
}
79+
80+
const originType = await this.remotesService.getOriginType(remoteUrl);
81+
82+
return {
83+
gitRoot: gitRoot,
84+
name: r.name,
85+
remote: remoteUrl,
86+
remoteType: originType,
87+
current: this.getCurrentBranch() === r.name,
88+
} as Branch;
89+
}),
90+
);
6891
}
92+
6993
public getCurrentBranch(): string {
7094
return this.repo.state.HEAD!.name || '';
7195
}

src/adapter/repository/gitArgsService.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ export class GitArgsService implements IGitArgsService {
1616
return ['show', '--numstat', '--format=', '-M', hash];
1717
}
1818
public getCommitNameStatusArgs(hash: string): string[] {
19-
return ['show', '--name-status', '--format=', '-M', hash];
19+
return ['-c', 'core.quotePath=false', 'show', '--name-status', '--format=', '-M', hash];
2020
}
2121
public getCommitWithNumStatArgsForMerge(hash: string) {
22-
return ['show', '--numstat', '--format=', '-M', '--first-parent', hash];
22+
return ['-c', 'core.quotePath=false', 'show', '--numstat', '--format=', '-M', '--first-parent', hash];
2323
}
2424
public getCommitNameStatusArgsForMerge(hash: string): string[] {
25-
return ['show', '--name-status', '--format=', '-M', '--first-parent', hash];
25+
return ['-c', 'core.quotePath=false', 'show', '--name-status', '--format=', '-M', '--first-parent', hash];
2626
}
2727
public getObjectHashArgs(object: string): string[] {
2828
return ['show', `--format=${Helpers.GetCommitInfoFormatCode(CommitInfo.FullHash)}`, '--shortstat', object];

0 commit comments

Comments
 (0)