diff --git a/packages/git/src/git-helpers.js b/packages/git/src/git-helpers.js
index fa8c64e06..28ac74a41 100644
--- a/packages/git/src/git-helpers.js
+++ b/packages/git/src/git-helpers.js
@@ -17,6 +17,7 @@
* along with this program. If not, see .
*/
import path from 'path-browserify';
+import git from 'isomorphic-git';
/**
* Attempt to locate the git repository directory.
@@ -137,3 +138,43 @@ export const group_positional_arguments = (arg_tokens) => {
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} 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 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}'`);
+ }
+}
diff --git a/packages/git/src/subcommands/checkout.js b/packages/git/src/subcommands/checkout.js
index 28b2ad29a..f8e8853e5 100644
--- a/packages/git/src/subcommands/checkout.js
+++ b/packages/git/src/subcommands/checkout.js
@@ -17,7 +17,7 @@
* along with this program. If not, see .
*/
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';
const CHECKOUT = {
@@ -137,10 +137,11 @@ const CHECKOUT = {
return;
}
- const old_oid = await git.resolveRef({ fs, dir, gitdir, ref: 'HEAD' });
-
- const oid = await git.resolveRef({ fs, dir, gitdir, ref: reference });
- if (!oid)
+ const [ old_commit, commit ] = await Promise.all([
+ resolve_to_commit({ fs, dir, gitdir, cache }, 'HEAD'),
+ resolve_to_commit({ fs, dir, gitdir, cache }, reference ),
+ ]);
+ if (!commit)
throw new Error(`Reference '${reference}' not found.`);
await git.checkout({
@@ -154,14 +155,11 @@ const CHECKOUT = {
if (branches.includes(reference)) {
stdout(`Switched to branch '${reference}'`);
} else {
- const commit = await git.readCommit({ fs, dir, gitdir, oid });
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];
- stdout(`Previous HEAD position was ${shorten_hash(old_oid)} ${old_commit_title}`);
- stdout(`HEAD is now at ${shorten_hash(oid)} ${commit_title}`);
+ stdout(`Previous HEAD position was ${shorten_hash(old_commit.oid)} ${old_commit_title}`);
+ stdout(`HEAD is now at ${shorten_hash(commit.oid)} ${commit_title}`);
}
}
}
diff --git a/packages/git/src/subcommands/diff.js b/packages/git/src/subcommands/diff.js
index 0775de202..d4a9d2915 100644
--- a/packages/git/src/subcommands/diff.js
+++ b/packages/git/src/subcommands/diff.js
@@ -17,7 +17,7 @@
* along with this program. If not, see .
*/
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 * as Diff from 'diff';
import path from 'path-browserify';
@@ -123,13 +123,14 @@ export default {
const { before: commit_args, after: path_args } = group_positional_arguments(tokens);
// Ensure all commit_args are commit references
- const resolved_commit_refs = await Promise.allSettled(commit_args.map(commit_arg => {
- return git.resolveRef({ fs, dir, gitdir, ref: commit_arg });
+ const resolved_commits = await Promise.allSettled(commit_args.map(commit_arg => {
+ return resolve_to_commit({ fs, dir, gitdir, cache }, commit_arg);
}));
- for (const [i, ref] of resolved_commit_refs.entries()) {
- if (ref.status === 'rejected')
+ for (const [i, commit] of resolved_commits.entries()) {
+ if (commit.status === 'rejected')
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 diff_ctx = {
@@ -153,7 +154,7 @@ export default {
// Show staged changes
diffs = await diff_git_trees({
...diff_ctx,
- a_tree: TREE({ ref: commit_args[0] ?? 'HEAD' }),
+ a_tree: TREE({ ref: from_oid ?? 'HEAD' }),
b_tree: STAGE(),
read_a: read_tree,
read_b: read_staged,
@@ -171,7 +172,7 @@ export default {
// Changes from commit to workdir
diffs = await diff_git_trees({
...diff_ctx,
- a_tree: TREE({ ref: commit_args[0] }),
+ a_tree: TREE({ ref: from_oid }),
b_tree: WORKDIR(),
read_a: read_tree,
read_b: read_tree,
@@ -180,8 +181,8 @@ export default {
// Changes from one commit to another
diffs = await diff_git_trees({
...diff_ctx,
- a_tree: TREE({ ref: commit_args[0] }),
- b_tree: TREE({ ref: commit_args[1] }),
+ a_tree: TREE({ ref: from_oid }),
+ b_tree: TREE({ ref: to_oid }),
read_a: read_tree,
read_b: read_tree,
});
diff --git a/packages/git/src/subcommands/push.js b/packages/git/src/subcommands/push.js
index fe0f30b12..76a052fb2 100644
--- a/packages/git/src/subcommands/push.js
+++ b/packages/git/src/subcommands/push.js
@@ -18,7 +18,7 @@
*/
import git from 'isomorphic-git';
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 { authentication_options, Authenticator } from '../auth.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.
// 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 is_up_to_date = source_oid === old_dest_oid;
diff --git a/packages/git/src/subcommands/show.js b/packages/git/src/subcommands/show.js
index 8799a8014..10d860a5c 100644
--- a/packages/git/src/subcommands/show.js
+++ b/packages/git/src/subcommands/show.js
@@ -17,7 +17,7 @@
* along with this program. If not, see .
*/
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 {
commit_formatting_options,
diff_formatting_options,
@@ -108,12 +108,7 @@ export default {
for (const ref of objects) {
// Could be any ref, so first get the oid that's referred to.
- const oid = await git.resolveRef({
- fs,
- dir,
- gitdir,
- ref,
- });
+ const oid = await resolve_to_oid({ fs, dir, gitdir, cache }, ref);
// Then obtain the object and parse it.
const parsed_object = await git.readObject({