feat(git): Implement git fetch

Sporadically we hang while trying to fetch. I haven't been able to
identify why but it seems like a race condition in isomorphic-git
somewhere.
This commit is contained in:
Sam Atkins 2024-06-04 14:20:40 +01:00 committed by Eric Dubé
parent c86e4dfdce
commit 98a4b9ede3
3 changed files with 116 additions and 0 deletions

View File

@ -17,6 +17,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import path from 'path-browserify';
import git from 'isomorphic-git';
export const PROXY_URL = 'https://cors.isomorphic-git.org';
@ -84,3 +85,28 @@ export const shorten_hash = (hash) => {
// TODO: Ensure that whatever we produce is unique within the repo
return hash.slice(0, 7);
}
/**
* Determine the remot/url parameters to pass to git.fetch(), based on a `<repository>` string.
* @param remote_name_or_url Command-line parameter, either a remote name, an url, or undefined.
* @param remotes List of all existing remotes, from `git.listRemotes()`
* @returns {remote, url} Object with fields to pass to git.fetch() or similar.
*/
export const determine_fetch_remote = (remote_name_or_url, remotes) => {
if (!remote_name_or_url) {
// We leave `url` and `remote` blank and git.fetch() handles the default.
return {};
}
if (URL.canParse(remote_name_or_url)) {
return { url: remote_name_or_url };
}
// Named remote. First, check if the remote exists. `git.fetch` reports non-existent remotes as:
// "The function requires a "remote OR url" parameter but none was provided."
// ...which is not helpful to the user.
const remote_data = remotes.find(it => it.remote === remote_name_or_url);
if (!remote_data)
throw new Error(`'${remote_name_or_url}' does not appear to be a git repository`);
return remote_data;
}

View File

@ -22,6 +22,7 @@ import module_branch from './branch.js'
import module_clone from './clone.js'
import module_commit from './commit.js'
import module_config from './config.js'
import module_fetch from './fetch.js'
import module_help from './help.js'
import module_init from './init.js'
import module_log from './log.js'
@ -36,6 +37,7 @@ export default {
"clone": module_clone,
"commit": module_commit,
"config": module_config,
"fetch": module_fetch,
"help": module_help,
"init": module_init,
"log": module_log,

View File

@ -0,0 +1,88 @@
/*
* Copyright (C) 2024 Puter Technologies Inc.
*
* This file is part of Puter's Git client.
*
* Puter's Git client is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import git from 'isomorphic-git';
import http from 'isomorphic-git/http/web';
import { determine_fetch_remote, find_repo_root, PROXY_URL } from '../git-helpers.js';
import { SHOW_USAGE } from '../help.js';
export default {
name: 'fetch',
usage: [
'git fetch <repository>',
'git fetch --all',
],
description: `Download objects and refs from another repository.`,
args: {
allowPositionals: true,
options: {
all: {
description: 'Fetch all remotes.',
type: 'boolean',
default: false,
}
},
},
execute: async (ctx) => {
const { io, fs, env, args } = ctx;
const { stdout, stderr } = io;
const { options, positionals } = args;
const cache = {};
const { repository_dir, git_dir } = await find_repo_root(fs, env.PWD);
// TODO: Support <refspec> syntax.
const remotes = await git.listRemotes({
fs,
dir: repository_dir,
gitdir: git_dir,
});
if (options.all) {
for (const { remote, url } of remotes) {
stdout(`Fetching ${remote}\nFrom ${url}`);
await git.fetch({
fs,
http,
cache,
corsProxy: PROXY_URL,
dir: repository_dir,
gitdir: git_dir,
remote,
onMessage: (message) => { stdout(message); },
});
}
return;
}
const remote = positionals.shift();
const remote_data = determine_fetch_remote(remote, remotes);
await git.fetch({
fs,
http,
cache,
corsProxy: PROXY_URL,
dir: repository_dir,
gitdir: git_dir,
...remote_data,
onMessage: (message) => { stdout(message); },
});
}
}