Skip to content

Commit

Permalink
Improve support for empty Git repository in Git view
Browse files Browse the repository at this point in the history
Signed-off-by: Nigel Westbury <nigelipse@miegel.org>
  • Loading branch information
westbury committed Jun 16, 2019
1 parent 6b8e776 commit 4ae30d6
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 18 deletions.
21 changes: 21 additions & 0 deletions packages/git/src/common/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,18 @@ export namespace Git {

}

/**
* Options for the `git rev-parse` command.
*/
export interface RevParse {

/**
* The reference to parse.
*/
readonly ref: string;

}

}
}

Expand Down Expand Up @@ -797,6 +809,15 @@ export interface Git extends Disposable {
*/
log(repository: Repository, options?: Git.Options.Log): Promise<CommitWithChanges[]>;

/**
* Returns the commit SHA of the given ref if the ref exists, or returns 'undefined' if the
* given ref does not exist.
*
* @param repository the repository where the ref may be found.
* @param options configuration containing the ref and optionally other properties for further refining the `git rev-parse` command execution.
*/
revParse(repository: Repository, options: Git.Options.RevParse): Promise<string | undefined>;

/**
* Returns the annotations of each line in the given file.
*
Expand Down
12 changes: 10 additions & 2 deletions packages/git/src/node/dugite-git.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -772,10 +772,18 @@ describe('log', function () {
const repository = { localUri };
const git = await createGit();
const result = await git.log(repository, { uri: localUri });
expect(result.length === 1).to.be.true;
expect(result[0].author.email === 'jon@doe.com').to.be.true;
expect(result.length).to.be.equal(1);
expect(result[0].author.email).to.be.equal('jon@doe.com');
});

it('should not fail when executed against an empty repository', async () => {
const root = await initRepository(track.mkdirSync('empty-log-test'));
const localUri = FileUri.create(root).toString();
const repository = { localUri };
const git = await createGit();
const result = await git.log(repository, { uri: localUri });
expect(result.length).to.be.equal(0);
});
});

function toPathSegment(repository: Repository, uri: string): string {
Expand Down
26 changes: 25 additions & 1 deletion packages/git/src/node/dugite-git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -657,13 +657,37 @@ export class DugiteGit implements Git {
const file = Path.relative(this.getFsPath(repository), this.getFsPath(options.uri)) || '.';
args.push(...[file]);
}
const result = await this.exec(repository, args);

const successExitCodes = new Set([0, 128]);
let result = await this.exec(repository, args, { successExitCodes });
if (result.exitCode !== 0) {
// The 'log' command could potentially be valid when no HEAD if the revision range does not involve HEAD */
if ((!options || !options.range || options.range.toRevision === 'HEAD') && !await this.revParse(repository, { ref: 'HEAD' })) {
// The range involves HEAD but there is no HEAD. 'no head' most likely means a newly created repository with
// no commits, but could potentially have commits with no HEAD. This is effectively an empty repository.
return [];
}
// Either the range did not involve HEAD or HEAD exists. The error must be something else,
// so re-run but this time we don't ignore the error.
result = await this.exec(repository, args);
}

return this.commitDetailsParser.parse(
repository.localUri, result.stdout.trim()
.split(CommitDetailsParser.COMMIT_CHUNK_DELIMITER)
.filter(item => item && item.length > 0));
}

async revParse(repository: Repository, options: Git.Options.RevParse): Promise<string | undefined> {
const ref = options.ref;
const successExitCodes = new Set([0, 128]);
const result = await this.exec(repository, ['rev-parse', ref], { successExitCodes });
if (result.exitCode === 0) {
const sha = result.stdout;
return sha;
}
}

async blame(repository: Repository, uri: string, options?: Git.Options.Blame): Promise<GitFileBlame | undefined> {
await this.ready.promise;
const args = ['blame', '--root', '--incremental'];
Expand Down
29 changes: 14 additions & 15 deletions packages/scm/src/browser/scm-amend-component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,21 +104,20 @@ export class ScmAmendComponent extends React.Component<ScmAmendComponentProps, S
} else if (nextCommit === undefined && this.state.lastCommit === undefined) {
// No change here
} else if (this.transitionHint === 'none') {
if (this.state.lastCommit) {
// If the 'last' commit changes, but we are not expecting an 'amend'
// or 'unamend' to occur, then we clear out the list of amended commits.
// This is because an unexpected change has happened to the repoistory,
// perhaps the user commited, merged, or something. The amended commits
// will no longer be valid.
await this.clearAmendingCommits();
// There is a change to the last commit, but no transition hint so
// the view just updates without transition.
this.setState({ amendingCommits: [], lastCommit: nextCommit });
} else {
// There should always be a previous 'last commit'
throw new Error('unexpected state');
// this.setState({ amendingCommits: await this.buildAmendingList(), lastCommit: nextCommit });
}
// If the 'last' commit changes, but we are not expecting an 'amend'
// or 'unamend' to occur, then we clear out the list of amended commits.
// This is because an unexpected change has happened to the repoistory,
// perhaps the user commited, merged, or something. The amended commits
// will no longer be valid.

// Note that there may or may not have been a previous lastCommit (if the
// repository was previously empty with no initial commit then lastCommit
// will be undefined). Either way we clear the amending commits.
await this.clearAmendingCommits();

// There is a change to the last commit, but no transition hint so
// the view just updates without transition.
this.setState({ amendingCommits: [], lastCommit: nextCommit });
} else {
if (this.state.lastCommit && nextCommit) {
const direction: 'up' | 'down' = this.transitionHint === 'amend' ? 'up' : 'down';
Expand Down

0 comments on commit 4ae30d6

Please sign in to comment.