mirror of
https://github.com/HeyPuter/puter
synced 2024-11-15 06:15:47 +00:00
feat(git): Resolve more forms of commit reference
Specifically, we previously didn't resolve short commit hashes into the full ones. These functions provide a single place to later add more ways of selection a commit, such as `HEAD~1`.
This commit is contained in:
parent
60976b1ed6
commit
b6906bbcaa
@ -17,6 +17,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import path from 'path-browserify';
|
import path from 'path-browserify';
|
||||||
|
import git from 'isomorphic-git';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt to locate the git repository directory.
|
* Attempt to locate the git repository directory.
|
||||||
@ -137,3 +138,43 @@ export const group_positional_arguments = (arg_tokens) => {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Take some kind of reference, and resolve it to a full oid if possible.
|
||||||
|
* @param git_context Object of common parameters to isomorphic-git methods
|
||||||
|
* @param ref Reference to resolve
|
||||||
|
* @returns {Promise<string>} Full oid, or a thrown Error
|
||||||
|
*/
|
||||||
|
export const resolve_to_oid = async (git_context, ref) => {
|
||||||
|
const [ resolved_oid, expanded_oid ] = await Promise.allSettled([
|
||||||
|
git.resolveRef({ ...git_context, ref }),
|
||||||
|
git.expandOid({ ...git_context, oid: ref }),
|
||||||
|
]);
|
||||||
|
let oid;
|
||||||
|
if (resolved_oid.status === 'fulfilled') {
|
||||||
|
oid = resolved_oid.value;
|
||||||
|
} else if (expanded_oid.status === 'fulfilled') {
|
||||||
|
oid = expanded_oid.value;
|
||||||
|
} else {
|
||||||
|
throw new Error(`Unable to resolve reference '${ref}'`);
|
||||||
|
}
|
||||||
|
// TODO: Advanced revision selection, see https://git-scm.com/book/en/v2/Git-Tools-Revision-Selection
|
||||||
|
// and https://git-scm.com/docs/gitrevisions
|
||||||
|
return oid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to resolve_to_oid, but makes sure the oid refers to a commit.
|
||||||
|
* Returns the commit object because we had to retrieve it anyway.
|
||||||
|
* @param git_context Object of common parameters to isomorphic-git methods
|
||||||
|
* @param ref Reference to resolve
|
||||||
|
* @returns {Promise<ReadCommitResult>} ReadCommitResult object as returned by git.readCommit(), or a thrown Error
|
||||||
|
*/
|
||||||
|
export const resolve_to_commit = async (git_context, ref) => {
|
||||||
|
const resolved_oid = await resolve_to_oid(git_context, ref);
|
||||||
|
try {
|
||||||
|
return await git.readCommit({ ...git_context, oid: resolved_oid });
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`bad revision '${ref}'`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import git from 'isomorphic-git';
|
import git from 'isomorphic-git';
|
||||||
import { find_repo_root, shorten_hash } from '../git-helpers.js';
|
import { find_repo_root, resolve_to_commit, shorten_hash } from '../git-helpers.js';
|
||||||
import { SHOW_USAGE } from '../help.js';
|
import { SHOW_USAGE } from '../help.js';
|
||||||
|
|
||||||
const CHECKOUT = {
|
const CHECKOUT = {
|
||||||
@ -137,10 +137,11 @@ const CHECKOUT = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const old_oid = await git.resolveRef({ fs, dir, gitdir, ref: 'HEAD' });
|
const [ old_commit, commit ] = await Promise.all([
|
||||||
|
resolve_to_commit({ fs, dir, gitdir, cache }, 'HEAD'),
|
||||||
const oid = await git.resolveRef({ fs, dir, gitdir, ref: reference });
|
resolve_to_commit({ fs, dir, gitdir, cache }, reference ),
|
||||||
if (!oid)
|
]);
|
||||||
|
if (!commit)
|
||||||
throw new Error(`Reference '${reference}' not found.`);
|
throw new Error(`Reference '${reference}' not found.`);
|
||||||
|
|
||||||
await git.checkout({
|
await git.checkout({
|
||||||
@ -154,14 +155,11 @@ const CHECKOUT = {
|
|||||||
if (branches.includes(reference)) {
|
if (branches.includes(reference)) {
|
||||||
stdout(`Switched to branch '${reference}'`);
|
stdout(`Switched to branch '${reference}'`);
|
||||||
} else {
|
} else {
|
||||||
const commit = await git.readCommit({ fs, dir, gitdir, oid });
|
|
||||||
const commit_title = commit.commit.message.split('\n', 2)[0];
|
const commit_title = commit.commit.message.split('\n', 2)[0];
|
||||||
|
|
||||||
const old_commit = await git.readCommit({ fs, dir, gitdir, oid: old_oid });
|
|
||||||
const old_commit_title = old_commit.commit.message.split('\n', 2)[0];
|
const old_commit_title = old_commit.commit.message.split('\n', 2)[0];
|
||||||
|
|
||||||
stdout(`Previous HEAD position was ${shorten_hash(old_oid)} ${old_commit_title}`);
|
stdout(`Previous HEAD position was ${shorten_hash(old_commit.oid)} ${old_commit_title}`);
|
||||||
stdout(`HEAD is now at ${shorten_hash(oid)} ${commit_title}`);
|
stdout(`HEAD is now at ${shorten_hash(commit.oid)} ${commit_title}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import git, { STAGE, TREE, WORKDIR } from 'isomorphic-git';
|
import git, { STAGE, TREE, WORKDIR } from 'isomorphic-git';
|
||||||
import { find_repo_root, group_positional_arguments } from '../git-helpers.js';
|
import { find_repo_root, group_positional_arguments, resolve_to_commit, resolve_to_oid } from '../git-helpers.js';
|
||||||
import { SHOW_USAGE } from '../help.js';
|
import { SHOW_USAGE } from '../help.js';
|
||||||
import * as Diff from 'diff';
|
import * as Diff from 'diff';
|
||||||
import path from 'path-browserify';
|
import path from 'path-browserify';
|
||||||
@ -123,13 +123,14 @@ export default {
|
|||||||
const { before: commit_args, after: path_args } = group_positional_arguments(tokens);
|
const { before: commit_args, after: path_args } = group_positional_arguments(tokens);
|
||||||
|
|
||||||
// Ensure all commit_args are commit references
|
// Ensure all commit_args are commit references
|
||||||
const resolved_commit_refs = await Promise.allSettled(commit_args.map(commit_arg => {
|
const resolved_commits = await Promise.allSettled(commit_args.map(commit_arg => {
|
||||||
return git.resolveRef({ fs, dir, gitdir, ref: commit_arg });
|
return resolve_to_commit({ fs, dir, gitdir, cache }, commit_arg);
|
||||||
}));
|
}));
|
||||||
for (const [i, ref] of resolved_commit_refs.entries()) {
|
for (const [i, commit] of resolved_commits.entries()) {
|
||||||
if (ref.status === 'rejected')
|
if (commit.status === 'rejected')
|
||||||
throw new Error(`bad revision '${commit_args[i]}'`);
|
throw new Error(`bad revision '${commit_args[i]}'`);
|
||||||
}
|
}
|
||||||
|
const [ from_oid, to_oid ] = resolved_commits.map(it => it.value.oid);
|
||||||
|
|
||||||
const path_filters = path_args.map(it => path.resolve(env.PWD, it));
|
const path_filters = path_args.map(it => path.resolve(env.PWD, it));
|
||||||
const diff_ctx = {
|
const diff_ctx = {
|
||||||
@ -153,7 +154,7 @@ export default {
|
|||||||
// Show staged changes
|
// Show staged changes
|
||||||
diffs = await diff_git_trees({
|
diffs = await diff_git_trees({
|
||||||
...diff_ctx,
|
...diff_ctx,
|
||||||
a_tree: TREE({ ref: commit_args[0] ?? 'HEAD' }),
|
a_tree: TREE({ ref: from_oid ?? 'HEAD' }),
|
||||||
b_tree: STAGE(),
|
b_tree: STAGE(),
|
||||||
read_a: read_tree,
|
read_a: read_tree,
|
||||||
read_b: read_staged,
|
read_b: read_staged,
|
||||||
@ -171,7 +172,7 @@ export default {
|
|||||||
// Changes from commit to workdir
|
// Changes from commit to workdir
|
||||||
diffs = await diff_git_trees({
|
diffs = await diff_git_trees({
|
||||||
...diff_ctx,
|
...diff_ctx,
|
||||||
a_tree: TREE({ ref: commit_args[0] }),
|
a_tree: TREE({ ref: from_oid }),
|
||||||
b_tree: WORKDIR(),
|
b_tree: WORKDIR(),
|
||||||
read_a: read_tree,
|
read_a: read_tree,
|
||||||
read_b: read_tree,
|
read_b: read_tree,
|
||||||
@ -180,8 +181,8 @@ export default {
|
|||||||
// Changes from one commit to another
|
// Changes from one commit to another
|
||||||
diffs = await diff_git_trees({
|
diffs = await diff_git_trees({
|
||||||
...diff_ctx,
|
...diff_ctx,
|
||||||
a_tree: TREE({ ref: commit_args[0] }),
|
a_tree: TREE({ ref: from_oid }),
|
||||||
b_tree: TREE({ ref: commit_args[1] }),
|
b_tree: TREE({ ref: to_oid }),
|
||||||
read_a: read_tree,
|
read_a: read_tree,
|
||||||
read_b: read_tree,
|
read_b: read_tree,
|
||||||
});
|
});
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
import git from 'isomorphic-git';
|
import git from 'isomorphic-git';
|
||||||
import http from 'isomorphic-git/http/web';
|
import http from 'isomorphic-git/http/web';
|
||||||
import { determine_fetch_remote, find_repo_root, shorten_hash } from '../git-helpers.js';
|
import { determine_fetch_remote, find_repo_root, resolve_to_oid, shorten_hash } from '../git-helpers.js';
|
||||||
import { SHOW_USAGE } from '../help.js';
|
import { SHOW_USAGE } from '../help.js';
|
||||||
import { authentication_options, Authenticator } from '../auth.js';
|
import { authentication_options, Authenticator } from '../auth.js';
|
||||||
import { color_options, process_color_options } from '../color.js';
|
import { color_options, process_color_options } from '../color.js';
|
||||||
@ -209,7 +209,7 @@ export default {
|
|||||||
// TODO: Canonical git only pushes "new" branches to the remote when configured to do so, or with --set-upstream.
|
// TODO: Canonical git only pushes "new" branches to the remote when configured to do so, or with --set-upstream.
|
||||||
// So, we should show some kind of warning and stop, if that's not the case.
|
// So, we should show some kind of warning and stop, if that's not the case.
|
||||||
|
|
||||||
const source_oid = await git.resolveRef({ fs, dir, gitdir, ref: source });
|
const source_oid = await resolve_to_oid({ fs, dir, gitdir, cache }, source);
|
||||||
const old_dest_oid = remote_ref?.oid;
|
const old_dest_oid = remote_ref?.oid;
|
||||||
|
|
||||||
const is_up_to_date = source_oid === old_dest_oid;
|
const is_up_to_date = source_oid === old_dest_oid;
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import git, { TREE } from 'isomorphic-git';
|
import git, { TREE } from 'isomorphic-git';
|
||||||
import { find_repo_root } from '../git-helpers.js';
|
import { find_repo_root, resolve_to_oid } from '../git-helpers.js';
|
||||||
import {
|
import {
|
||||||
commit_formatting_options,
|
commit_formatting_options,
|
||||||
diff_formatting_options,
|
diff_formatting_options,
|
||||||
@ -108,12 +108,7 @@ export default {
|
|||||||
|
|
||||||
for (const ref of objects) {
|
for (const ref of objects) {
|
||||||
// Could be any ref, so first get the oid that's referred to.
|
// Could be any ref, so first get the oid that's referred to.
|
||||||
const oid = await git.resolveRef({
|
const oid = await resolve_to_oid({ fs, dir, gitdir, cache }, ref);
|
||||||
fs,
|
|
||||||
dir,
|
|
||||||
gitdir,
|
|
||||||
ref,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Then obtain the object and parse it.
|
// Then obtain the object and parse it.
|
||||||
const parsed_object = await git.readObject({
|
const parsed_object = await git.readObject({
|
||||||
|
Loading…
Reference in New Issue
Block a user