feat(git): Add helpers for platform-native commit (#13955)

Co-authored-by: Rhys Arkins <rhys@arkins.net>
This commit is contained in:
Sergei Zharinov 2022-02-10 11:58:30 +03:00 committed by GitHub
parent 5ab9faf589
commit 9809ba476b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 85 additions and 4 deletions

View file

@ -753,4 +753,29 @@ describe('util/git/index', () => {
});
});
});
describe('pushCommitAsRef', () => {
it('creates non-branch ref', async () => {
const commit = git.getBranchCommit('develop');
await git.pushCommitAsRef(commit, 'refs/foo/bar');
const repo = Git(tmpDir.path);
const res = (await repo.raw(['ls-remote'])).split(/\s+/);
expect(res).toContain('refs/foo/bar');
});
});
describe('listCommitTree', () => {
it('creates non-branch ref', async () => {
const commit = git.getBranchCommit('develop');
const res = await git.listCommitTree(commit);
expect(res).toEqual([
{
mode: '100644',
path: 'past_file',
sha: '913705ab2ca79368053a476efa48aa6912d052c5',
type: 'blob',
},
]);
});
});
});

View file

@ -40,6 +40,7 @@ import type {
LocalConfig,
StatusResult,
StorageConfig,
TreeItem,
} from './types';
export { setNoVerify } from './config';
@ -704,11 +705,12 @@ export async function prepareCommit({
}: CommitFilesConfig): Promise<CommitResult | null> {
const { localDir } = GlobalConfig.get();
await syncGit();
logger.debug(`Preparing files for commiting to branch ${branchName}`);
logger.debug(`Preparing files for committing to branch ${branchName}`);
await handleCommitAuth(localDir);
try {
await git.reset(ResetMode.HARD);
await git.raw(['clean', '-fd']);
const parentCommitSha = config.currentBranchSha;
await git.checkout(['-B', branchName, 'origin/' + config.currentBranch]);
const deletedFiles: string[] = [];
const addedModifiedFiles: string[] = [];
@ -787,7 +789,7 @@ export async function prepareCommit({
{ deletedFiles, ignoredFiles, result: commitRes },
`git commit`
);
const commit = commitRes?.commit || 'unknown';
const commitSha = commitRes?.commit || 'unknown';
if (!force && !(await hasDiff(`origin/${branchName}`))) {
logger.debug(
{ branchName, deletedFiles, addedModifiedFiles, ignoredFiles },
@ -797,7 +799,8 @@ export async function prepareCommit({
}
const result: CommitResult = {
sha: commit,
parentCommitSha,
commitSha,
files: files.filter((fileChange) => {
if (fileChange.type === 'deletion') {
return deletedFiles.includes(fileChange.path);
@ -898,3 +901,48 @@ export function getUrl({
pathname: repository + '.git',
});
}
export async function pushCommitAsRef(
commitSha: string,
refName: string
): Promise<void> {
await git.raw(['update-ref', refName, commitSha]);
await git.raw(['push', '--force', 'origin', refName]);
}
const treeItemRegex = regEx(
/^(?<mode>\d{6})\s+(?<type>blob|tree)\s+(?<sha>[0-9a-f]{40})\s+(?<path>.*)$/
);
const treeShaRegex = regEx(/tree\s+(?<treeSha>[0-9a-f]{40})\s*/);
/**
*
* $ git cat-file -p <commit-sha>
*
* > tree <tree-sha>
* > parent 59b8b0e79319b7dc38f7a29d618628f3b44c2fd7
* > ...
*
* $ git cat-file -p <tree-sha>
*
* > 040000 tree 389400684d1f004960addc752be13097fe85d776 .devcontainer
* > 100644 blob 7d2edde437ad4e7bceb70dbfe70e93350d99c98b .editorconfig
* > ...
*
*/
export async function listCommitTree(commitSha: string): Promise<TreeItem[]> {
const commitOutput = await git.raw(['cat-file', '-p', commitSha]);
const { treeSha } = treeShaRegex.exec(commitOutput)?.groups ?? {};
const contents = await git.raw(['cat-file', '-p', treeSha]);
const lines = contents.split(newlineRegex);
const result: TreeItem[] = [];
for (const line of lines) {
const matchGroups = treeItemRegex.exec(line)?.groups;
if (matchGroups) {
const { path, mode, type, sha } = matchGroups;
result.push({ path, mode, type, sha });
}
}
return result;
}

View file

@ -93,10 +93,18 @@ export interface SourceBranchConflict {
}
export interface CommitResult {
sha: string;
parentCommitSha: string;
commitSha: string;
files: FileChange[];
}
export interface TreeItem {
path: string;
mode: string;
type: string;
sha: string;
}
/**
* Represents a git authentication rule in the form of e.g.:
* git config --global url."https://api@github.com/".insteadOf "https://github.com/"