Skip to content

Commit 98a4b9e

Browse files
AtkinsSJKernelDeimos
authored andcommitted
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.
1 parent c86e4df commit 98a4b9e

File tree

3 files changed

+116
-0
lines changed

3 files changed

+116
-0
lines changed

packages/git/src/git-helpers.js

+26
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
* along with this program. If not, see <https://www.gnu.org/licenses/>.
1818
*/
1919
import path from 'path-browserify';
20+
import git from 'isomorphic-git';
2021

2122
export const PROXY_URL = 'https://cors.isomorphic-git.org';
2223

@@ -84,3 +85,28 @@ export const shorten_hash = (hash) => {
8485
// TODO: Ensure that whatever we produce is unique within the repo
8586
return hash.slice(0, 7);
8687
}
88+
89+
/**
90+
* Determine the remot/url parameters to pass to git.fetch(), based on a `<repository>` string.
91+
* @param remote_name_or_url Command-line parameter, either a remote name, an url, or undefined.
92+
* @param remotes List of all existing remotes, from `git.listRemotes()`
93+
* @returns {remote, url} Object with fields to pass to git.fetch() or similar.
94+
*/
95+
export const determine_fetch_remote = (remote_name_or_url, remotes) => {
96+
if (!remote_name_or_url) {
97+
// We leave `url` and `remote` blank and git.fetch() handles the default.
98+
return {};
99+
}
100+
101+
if (URL.canParse(remote_name_or_url)) {
102+
return { url: remote_name_or_url };
103+
}
104+
105+
// Named remote. First, check if the remote exists. `git.fetch` reports non-existent remotes as:
106+
// "The function requires a "remote OR url" parameter but none was provided."
107+
// ...which is not helpful to the user.
108+
const remote_data = remotes.find(it => it.remote === remote_name_or_url);
109+
if (!remote_data)
110+
throw new Error(`'${remote_name_or_url}' does not appear to be a git repository`);
111+
return remote_data;
112+
}

packages/git/src/subcommands/__exports__.js

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import module_branch from './branch.js'
2222
import module_clone from './clone.js'
2323
import module_commit from './commit.js'
2424
import module_config from './config.js'
25+
import module_fetch from './fetch.js'
2526
import module_help from './help.js'
2627
import module_init from './init.js'
2728
import module_log from './log.js'
@@ -36,6 +37,7 @@ export default {
3637
"clone": module_clone,
3738
"commit": module_commit,
3839
"config": module_config,
40+
"fetch": module_fetch,
3941
"help": module_help,
4042
"init": module_init,
4143
"log": module_log,

packages/git/src/subcommands/fetch.js

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright (C) 2024 Puter Technologies Inc.
3+
*
4+
* This file is part of Puter's Git client.
5+
*
6+
* Puter's Git client is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU Affero General Public License as published
8+
* by the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU Affero General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Affero General Public License
17+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
18+
*/
19+
import git from 'isomorphic-git';
20+
import http from 'isomorphic-git/http/web';
21+
import { determine_fetch_remote, find_repo_root, PROXY_URL } from '../git-helpers.js';
22+
import { SHOW_USAGE } from '../help.js';
23+
24+
export default {
25+
name: 'fetch',
26+
usage: [
27+
'git fetch <repository>',
28+
'git fetch --all',
29+
],
30+
description: `Download objects and refs from another repository.`,
31+
args: {
32+
allowPositionals: true,
33+
options: {
34+
all: {
35+
description: 'Fetch all remotes.',
36+
type: 'boolean',
37+
default: false,
38+
}
39+
},
40+
},
41+
execute: async (ctx) => {
42+
const { io, fs, env, args } = ctx;
43+
const { stdout, stderr } = io;
44+
const { options, positionals } = args;
45+
const cache = {};
46+
47+
const { repository_dir, git_dir } = await find_repo_root(fs, env.PWD);
48+
49+
// TODO: Support <refspec> syntax.
50+
51+
const remotes = await git.listRemotes({
52+
fs,
53+
dir: repository_dir,
54+
gitdir: git_dir,
55+
});
56+
57+
if (options.all) {
58+
for (const { remote, url } of remotes) {
59+
stdout(`Fetching ${remote}\nFrom ${url}`);
60+
await git.fetch({
61+
fs,
62+
http,
63+
cache,
64+
corsProxy: PROXY_URL,
65+
dir: repository_dir,
66+
gitdir: git_dir,
67+
remote,
68+
onMessage: (message) => { stdout(message); },
69+
});
70+
}
71+
return;
72+
}
73+
74+
const remote = positionals.shift();
75+
const remote_data = determine_fetch_remote(remote, remotes);
76+
77+
await git.fetch({
78+
fs,
79+
http,
80+
cache,
81+
corsProxy: PROXY_URL,
82+
dir: repository_dir,
83+
gitdir: git_dir,
84+
...remote_data,
85+
onMessage: (message) => { stdout(message); },
86+
});
87+
}
88+
}

0 commit comments

Comments
 (0)