Skip to content

Commit

Permalink
WIP: git restore
Browse files Browse the repository at this point in the history
  • Loading branch information
AtkinsSJ committed Jun 20, 2024
1 parent 156627e commit 470adf6
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 0 deletions.
2 changes: 2 additions & 0 deletions packages/git/src/subcommands/__exports__.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import module_log from './log.js'
import module_pull from './pull.js'
import module_push from './push.js'
import module_remote from './remote.js'
import module_restore from './restore.js'
import module_show from './show.js'
import module_status from './status.js'
import module_version from './version.js'
Expand All @@ -50,6 +51,7 @@ export default {
"pull": module_pull,
"push": module_push,
"remote": module_remote,
"restore": module_restore,
"show": module_show,
"status": module_status,
"version": module_version,
Expand Down
122 changes: 122 additions & 0 deletions packages/git/src/subcommands/restore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* 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, { STAGE, TREE, WORKDIR } from 'isomorphic-git';
import { find_repo_root } from '../git-helpers.js';
import path from 'path-browserify';

export default {
name: 'restore',
usage: 'git restore [--staged] [--worktree] [--] [<pathspec>...]',
description: 'Add file contents to the index.',
args: {
allowPositionals: true,
options: {
'staged': {
description: 'Restore the file in the index.',
type: 'boolean',
short: 'S',
},
'worktree': {
description: 'Restore the file in the working tree.',
type: 'boolean',
short: 'W',
},
},
},
execute: async (ctx) => {
const { io, fs, env, args } = ctx;
const { stdout, stderr } = io;
const { options, positionals } = args;
const cache = {};

if (!options.staged && !options.worktree)
options.worktree = true;

const pathspecs = positionals.map(it => path.resolve(env.PWD, it));
if (pathspecs.length === 0)
throw new Error(`you must specify path(s) to restore`);

const source = 'HEAD';

const { dir, gitdir } = await find_repo_root(fs, env.PWD);

const operations = await git.walk({
fs, dir, gitdir, cache,
trees: [
TREE({ ref: source }),
STAGE(),
WORKDIR(),
],
map: async (filepath, [ source, staged, workdir]) => {
// Reject paths that don't match pathspecs.
const abs_filepath = path.resolve(env.PWD, filepath);
if (!pathspecs.some(abs_path =>
(filepath === '.') || (abs_filepath.startsWith(abs_path)) || (path.dirname(abs_filepath) === abs_path),
)) {
return null;
}

if (await git.isIgnored({ fs, dir, gitdir, filepath }))
return null;

const [
source_type, staged_type, workdir_type
] = await Promise.all([
source?.type(), staged?.type(), workdir?.type()
]);

// Exclude directories from results, but still iterate them.
if ((!source_type || source_type === 'tree')
&& (!staged_type || staged_type === 'tree')
&& (!workdir_type || workdir_type === 'tree'))
return;

// We need to modify the index or working tree if their oid doesn't match the source's.
const [
source_oid, staged_oid, workdir_oid
] = await Promise.all([
source_type === 'blob' ? source.oid() : undefined,
staged_type === 'blob' ? staged.oid() : undefined,
workdir_type === 'blob' ? workdir.oid() : undefined,
]);
const work_to_do = (options.staged && staged_oid !== source_oid) || (options.worktree && workdir_oid !== source_oid);
if (!work_to_do)
return null;
return Promise.all([
// TODO: Split into two things because they don't overlap
async () => {
// Update the index
if (options.staged && staged_oid !== source_oid) {
await git.updateIndex({
fs, dir, gitdir, cache,
// TODO: STUFF
});
}
// Update the working tree
if (options.worktree && workdir_oid !== source_oid) {
const source_content = await source.content();
await fs.promises.writeFile(filepath, source_content);
}
},
]);
},
});
await Promise.all(operations);
}
}

0 comments on commit 470adf6

Please sign in to comment.