refactor(git): lazy sync git (#6984)

Co-authored-by: Jamie Magee <JamieMagee@users.noreply.github.com>
This commit is contained in:
Rhys Arkins 2020-08-17 13:31:53 +02:00 committed by GitHub
parent b45502cd28
commit 062045168a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 53 additions and 29 deletions

View file

@ -150,7 +150,7 @@ export async function initRepo({
const url =
defaults.endpoint +
`${encodeURIComponent(projectName)}/_git/${encodeURIComponent(repoName)}`;
await git.initRepo({
git.initRepo({
...config,
localDir,
url,

View file

@ -179,7 +179,7 @@ export async function initRepo({
repository,
});
await git.initRepo({
git.initRepo({
...config,
localDir,
url: gitUrl,

View file

@ -153,7 +153,7 @@ export async function initRepo({
repository,
});
await git.initRepo({
git.initRepo({
...config,
localDir,
url,

View file

@ -306,7 +306,7 @@ const platform: Platform = {
gitEndpoint.auth = opts.token;
// Initialize Git storage
await git.initRepo({
git.initRepo({
...config,
url: URL.format(gitEndpoint),
gitAuthorName: global.gitAuthor?.name,

View file

@ -413,7 +413,7 @@ export async function initRepo({
);
parsedEndpoint.pathname = config.repository + '.git';
const url = URL.format(parsedEndpoint);
await git.initRepo({
git.initRepo({
...config,
url,
gitAuthorName: global.gitAuthor?.name,

View file

@ -224,7 +224,7 @@ export async function initRepo({
repoUrl.auth = 'oauth2:' + opts.token;
url = URL.format(repoUrl);
}
await git.initRepo({
git.initRepo({
...config,
url,
gitAuthorName: global.gitAuthor?.name,

View file

@ -60,7 +60,7 @@ describe('platform/git', () => {
const repo = Git(origin.path);
await repo.clone(base.path, '.', ['--bare']);
tmpDir = await tmp.dir({ unsafeCleanup: true });
await git.initRepo({
git.initRepo({
localDir: tmpDir.path,
url: origin.path,
extraCloneOpts: {
@ -100,10 +100,11 @@ describe('platform/git', () => {
const repo = Git(base.path).silent(true);
await repo.submoduleAdd(base.path, 'submodule');
await repo.commit('Add submodule');
await git.initRepo({
git.initRepo({
localDir: tmpDir.path,
url: base.path,
});
await git.syncGit();
expect(await fs.exists(tmpDir.path + '/.gitmodules')).toBeTruthy();
expect(await git.getFileList()).toMatchSnapshot();
await repo.reset(['--hard', 'HEAD^']);
@ -129,6 +130,7 @@ describe('platform/git', () => {
});
describe('isBranchStale()', () => {
beforeEach(async () => {
await git.syncGit();
await git.setBranch('master');
});
it('should return false if same SHA as master', async () => {
@ -369,7 +371,7 @@ describe('platform/git', () => {
await git.setBranch('develop');
await git.initRepo({
git.initRepo({
localDir: tmpDir.path,
url: base.path,
});
@ -391,7 +393,7 @@ describe('platform/git', () => {
await repo.commit('past message2');
await repo.checkout('master');
await git.initRepo({
git.initRepo({
localDir: tmpDir.path,
url: base.path,
});
@ -400,7 +402,7 @@ describe('platform/git', () => {
expect(await git.branchExists('renovate/test')).toBe(true);
const cid = await git.getBranchCommit('renovate/test');
await git.initRepo({
git.initRepo({
localDir: tmpDir.path,
url: base.path,
});
@ -429,10 +431,11 @@ describe('platform/git', () => {
'test',
]);
await repo.commit('Add submodule');
await git.initRepo({
git.initRepo({
localDir: tmpDir.path,
url: base.path,
});
await git.syncGit();
expect(await fs.exists(tmpDir.path + '/.gitmodules')).toBeTruthy();
await repo.reset(['--hard', 'HEAD^']);
});

View file

@ -114,6 +114,13 @@ let git: SimpleGit | undefined;
let privateKeySet = false;
export function initRepo(args: StorageConfig): void {
config = { ...args } as any;
config.branchExists = {};
config.branchIsModified = {};
git = undefined;
}
async function resetToBranch(branchName: string): Promise<void> {
logger.debug(`resetToBranch(${branchName})`);
await git.raw(['reset', '--hard']);
@ -143,14 +150,17 @@ async function cleanLocalBranches(): Promise<void> {
* By calling this function once the repo's branchPrefix is known, we can fetch all of Renovate's branches in one command.
*/
export async function setBranchPrefix(branchPrefix: string): Promise<void> {
logger.debug('Setting branchPrefix: ' + branchPrefix);
config.branchPrefix = branchPrefix;
const ref = `refs/heads/${branchPrefix}*:refs/remotes/origin/${branchPrefix}*`;
try {
await git.fetch(['origin', ref, '--depth=2', '--force']);
} catch (err) /* istanbul ignore next */ {
checkForPlatformFailure(err);
throw err;
// If the repo is already cloned then set branchPrefix now, otherwise it will be called again during syncGit()
if (git) {
logger.debug('Setting branchPrefix: ' + branchPrefix);
const ref = `refs/heads/${branchPrefix}*:refs/remotes/origin/${branchPrefix}*`;
try {
await git.fetch(['origin', ref, '--depth=2', '--force']);
} catch (err) /* istanbul ignore next */ {
checkForPlatformFailure(err);
throw err;
}
}
}
@ -258,20 +268,15 @@ export async function syncGit(): Promise<void> {
logger.debug({ err }, 'Error setting git author config');
throw new Error(REPOSITORY_TEMPORARY_ERROR);
}
config.currentBranch = config.currentBranch || (await getDefaultBranch(git));
}
export async function initRepo(args: StorageConfig): Promise<void> {
config = { ...args } as any;
config.branchExists = {};
config.branchIsModified = {};
git = undefined;
await syncGit();
if (config.branchPrefix) {
await setBranchPrefix(config.branchPrefix);
}
}
// istanbul ignore next
export function getRepoStatus(): Promise<StatusResult> {
export async function getRepoStatus(): Promise<StatusResult> {
await syncGit();
return git.status();
}
@ -279,6 +284,7 @@ export async function createBranch(
branchName: string,
sha: string
): Promise<void> {
await syncGit();
logger.debug(`createBranch(${branchName})`);
await git.reset(ResetMode.HARD);
await git.raw(['clean', '-fd']);
@ -289,6 +295,7 @@ export async function createBranch(
}
export async function branchExists(branchName: string): Promise<boolean> {
await syncGit();
// First check cache
if (config.branchExists[branchName] !== undefined) {
return config.branchExists[branchName];
@ -315,6 +322,7 @@ export async function branchExists(branchName: string): Promise<boolean> {
// Return the commit SHA for a branch
export async function getBranchCommit(branchName: string): Promise<string> {
await syncGit();
if (!(await branchExists(branchName))) {
throw Error(
'Cannot fetch commit for branch that does not exist: ' + branchName
@ -325,6 +333,7 @@ export async function getBranchCommit(branchName: string): Promise<string> {
}
export async function getCommitMessages(): Promise<string[]> {
await syncGit();
logger.debug('getCommitMessages');
const res = await git.log({
n: 10,
@ -334,6 +343,7 @@ export async function getCommitMessages(): Promise<string[]> {
}
export async function setBranch(branchName: string): Promise<string> {
await syncGit();
if (!(await branchExists(branchName))) {
throwBranchValidationError(branchName);
}
@ -365,6 +375,7 @@ export async function setBranch(branchName: string): Promise<string> {
}
export async function getFileList(): Promise<string[]> {
await syncGit();
const branch = config.currentBranch;
const submodules = await getSubmodules();
const files: string = await git.raw(['ls-tree', '-r', branch]);
@ -385,6 +396,7 @@ export async function getFileList(): Promise<string[]> {
export async function getAllRenovateBranches(
branchPrefix: string
): Promise<string[]> {
await syncGit();
const branches = await git.branch(['--remotes', '--verbose']);
return branches.all
.map(localName)
@ -392,6 +404,7 @@ export async function getAllRenovateBranches(
}
export async function isBranchStale(branchName: string): Promise<boolean> {
await syncGit();
if (!(await branchExists(branchName))) {
throw Error(
'Cannot check staleness for branch that does not exist: ' + branchName
@ -407,6 +420,7 @@ export async function isBranchStale(branchName: string): Promise<boolean> {
}
export async function isBranchModified(branchName: string): Promise<boolean> {
await syncGit();
// First check cache
if (config.branchIsModified[branchName] !== undefined) {
return config.branchIsModified[branchName];
@ -438,6 +452,7 @@ export async function isBranchModified(branchName: string): Promise<boolean> {
}
export async function deleteBranch(branchName: string): Promise<void> {
await syncGit();
try {
await git.raw(['push', '--delete', 'origin', branchName]);
logger.debug({ branchName }, 'Deleted remote branch');
@ -457,6 +472,7 @@ export async function deleteBranch(branchName: string): Promise<void> {
}
export async function mergeBranch(branchName: string): Promise<void> {
await syncGit();
await git.reset(ResetMode.HARD);
await git.checkout(['-B', branchName, 'origin/' + branchName]);
await git.checkout(config.currentBranch);
@ -468,6 +484,7 @@ export async function mergeBranch(branchName: string): Promise<void> {
export async function getBranchLastCommitTime(
branchName: string
): Promise<Date> {
await syncGit();
try {
const time = await git.show(['-s', '--format=%ai', 'origin/' + branchName]);
return new Date(Date.parse(time));
@ -478,6 +495,7 @@ export async function getBranchLastCommitTime(
}
export async function getBranchFiles(branchName: string): Promise<string[]> {
await syncGit();
try {
const diff = await git.diffSummary([branchName, config.currentBranch]);
return diff.files.map((file) => file.file);
@ -491,6 +509,7 @@ export async function getFile(
filePath: string,
branchName?: string
): Promise<string | null> {
await syncGit();
if (branchName) {
const exists = await branchExists(branchName);
if (!exists) {
@ -510,6 +529,7 @@ export async function getFile(
}
export async function hasDiff(branchName: string): Promise<boolean> {
await syncGit();
try {
return (await git.diff(['HEAD', branchName])) !== '';
} catch (err) {
@ -545,6 +565,7 @@ export async function commitFiles({
message,
force = false,
}: CommitFilesConfig): Promise<string | null> {
await syncGit();
logger.debug(`Committing files to branch ${branchName}`);
if (!privateKeySet) {
await writePrivateKey(config.localDir);