Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow amending of the initial commit in the repository #5451

Merged
merged 1 commit into from
Oct 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions packages/git/src/browser/git-scm-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ export class GitAmendSupport implements ScmAmendSupport {

constructor(protected readonly repository: Repository, protected readonly git: Git) { }

public async getInitialAmendingCommits(amendingHeadCommitSha: string, latestCommitSha: string): Promise<ScmCommit[]> {
public async getInitialAmendingCommits(amendingHeadCommitSha: string, latestCommitSha: string | undefined): Promise<ScmCommit[]> {
const commits = await this.git.log(
this.repository,
{
Expand All @@ -414,7 +414,16 @@ export class GitAmendSupport implements ScmAmendSupport {
}

public async reset(commit: string): Promise<void> {
await this.git.exec(this.repository, ['reset', commit, '--soft']);
if (commit === 'HEAD~' && await this.isHeadInitialCommit()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In VS Code they seem to be able to handle this without using the filesystem.
https://github.com/microsoft/vscode/blob/master/extensions/git/src/commands.ts#L1464

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In VS Code they seem to be able to handle this without using the filesystem

@svenefftinge you are totally correct. git update-ref -d HEAD does the trick perfectly. Thanks for pointing that out. It tests fine. I have amended the commit accordingly.

await this.git.exec(this.repository, ['update-ref', '-d', 'HEAD']);
} else {
await this.git.exec(this.repository, ['reset', commit, '--soft']);
}
}

protected async isHeadInitialCommit(): Promise<boolean> {
const result = await this.git.revParse(this.repository, { ref: 'HEAD~' });
return !result;
}

public async getLastCommit(): Promise<ScmCommit | undefined> {
Expand Down
74 changes: 46 additions & 28 deletions packages/scm/src/browser/scm-amend-component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ export class ScmAmendComponent extends React.Component<ScmAmendComponentProps, S
}

async fetchStatusAndSetState(): Promise<void> {
const storageKey = this.getStorageKey();

const nextCommit = await this.getLastCommit();
if (nextCommit && this.state.lastCommit && nextCommit.commit.id === this.state.lastCommit.commit.id) {
// No change here
Expand All @@ -119,32 +121,37 @@ export class ScmAmendComponent extends React.Component<ScmAmendComponentProps, S
// the view just updates without transition.
this.setState({ amendingCommits: [], lastCommit: nextCommit });
} else {
const amendingCommits = this.state.amendingCommits.concat([]); // copy the array

const direction: 'up' | 'down' = this.transitionHint === 'amend' ? 'up' : 'down';
switch (this.transitionHint) {
case 'amend':
if (this.state.lastCommit) {
amendingCommits.push(this.state.lastCommit);

const serializedState = JSON.stringify({
amendingHeadCommitSha: amendingCommits[0].commit.id,
latestCommitSha: nextCommit ? nextCommit.commit.id : undefined
});
this.props.storageService.setData<string | undefined>(storageKey, serializedState);
}
break;
case 'unamend':
amendingCommits.pop();
if (amendingCommits.length === 0) {
this.props.storageService.setData<string | undefined>(storageKey, undefined);
} else {
const serializedState = JSON.stringify({
amendingHeadCommitSha: amendingCommits[0].commit.id,
latestCommitSha: nextCommit ? nextCommit.commit.id : undefined
});
this.props.storageService.setData<string | undefined>(storageKey, serializedState);
}
break;
}

if (this.state.lastCommit && nextCommit) {
const direction: 'up' | 'down' = this.transitionHint === 'amend' ? 'up' : 'down';
const transitionData = { direction, previousLastCommit: this.state.lastCommit };
const amendingCommits = this.state.amendingCommits.concat([]); // copy the array
switch (this.transitionHint) {
case 'amend':
if (this.state.lastCommit) {
amendingCommits.push(this.state.lastCommit);

const storageKey = this.getStorageKey();
const serializedState = JSON.stringify({
amendingHeadCommitSha: amendingCommits[0].commit.id,
latestCommitSha: nextCommit.commit.id
});
this.props.storageService.setData<string | undefined>(storageKey, serializedState);
}
break;
case 'unamend':
amendingCommits.pop();
if (amendingCommits.length === 0) {
const storageKey = this.getStorageKey();
this.props.storageService.setData<string | undefined>(storageKey, undefined);
}
break;
}

this.setState({ lastCommit: nextCommit, amendingCommits, transition: { ...transitionData, state: 'start' } });
this.onNextFrame(() => {
this.setState({ transition: { ...transitionData, state: 'transitioning' } });
Expand All @@ -157,7 +164,7 @@ export class ScmAmendComponent extends React.Component<ScmAmendComponentProps, S
TRANSITION_TIME_MS);
} else {
// No previous last commit so no transition
this.setState({ transition: { state: 'none' }, lastCommit: nextCommit });
this.setState({ transition: { state: 'none' }, amendingCommits, lastCommit: nextCommit });
}
}

Expand All @@ -176,14 +183,14 @@ export class ScmAmendComponent extends React.Component<ScmAmendComponentProps, S
// Restore list of commits from saved amending head commit up through parents until the
// current commit. (If we don't reach the current commit, the repository has been changed in such
// a way then unamending commits can no longer be done).
if (storedState && lastCommit) {
if (storedState) {
const { amendingHeadCommitSha, latestCommitSha } = JSON.parse(storedState);
if (lastCommit.id !== latestCommitSha) {
if (!this.commitsAreEqual(lastCommit, latestCommitSha)) {
// The head commit in the repository has changed. It is not the same commit that was the
// head commit after the last 'amend'.
return [];
}
const commits = await this.props.scmAmendSupport.getInitialAmendingCommits(amendingHeadCommitSha, lastCommit.id);
const commits = await this.props.scmAmendSupport.getInitialAmendingCommits(amendingHeadCommitSha, lastCommit ? lastCommit.id : undefined);

const amendingCommitPromises = commits.map(async commit => {
const avatar = await this.props.avatarService.getAvatar(commit.authorEmail);
Expand All @@ -199,6 +206,17 @@ export class ScmAmendComponent extends React.Component<ScmAmendComponentProps, S
return REPOSITORY_STORAGE_KEY + ':' + this.props.repository.provider.rootUri;
}

/**
* Commits are equal if the ids are equal or if both are undefined.
* (If a commit is undefined, it represents the initial empty state of a repository,
* before the inital commit).
*/
private commitsAreEqual(lastCommit: ScmCommit | undefined, savedLastCommitId: string | undefined): boolean {
return lastCommit
? lastCommit.id === savedLastCommitId
: savedLastCommitId === undefined;
}

/**
* This function will update the 'model' (lastCommit, amendingCommits) only
* when the repository sees the last commit change.
Expand Down
2 changes: 1 addition & 1 deletion packages/scm/src/browser/scm-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export interface ScmCommit {
}

export interface ScmAmendSupport {
getInitialAmendingCommits(amendingHeadCommitSha: string, latestCommitSha: string): Promise<ScmCommit[]>
getInitialAmendingCommits(amendingHeadCommitId: string, latestCommitId: string | undefined): Promise<ScmCommit[]>
getMessage(commit: string): Promise<string>;
reset(commit: string): Promise<void>;
getLastCommit(): Promise<ScmCommit | undefined>;
Expand Down