diff --git a/action.yml b/action.yml index 465f4ff..344a5d0 100644 --- a/action.yml +++ b/action.yml @@ -40,7 +40,7 @@ outputs: sha: description: > The object name for the commit itself. - ex) 2414721 + ex) 2414721230345897234093645897124245324623 short-sha: description: > The object name for the commit itself. diff --git a/core/fetch_total_commit.ts b/core/fetch_total_commit.ts index 942a968..9bd1d09 100644 --- a/core/fetch_total_commit.ts +++ b/core/fetch_total_commit.ts @@ -9,7 +9,7 @@ interface FetchTotalCommitArgs { export async function fetchTotalCommit({ owner, repo, host, sha }: FetchTotalCommitArgs) { const stdout = await gh.graphql({ host })` - { + query { repository(owner: "${owner}", name: "${repo}") { object(expression: "${sha}") { ... on Commit { @@ -21,5 +21,5 @@ export async function fetchTotalCommit({ owner, repo, host, sha }: FetchTotalCom } }`; const repository = JSON.parse(stdout); - return repository.object.history.totalCount; + return repository.data.repository.object.history.totalCount; } diff --git a/core/gh_describe.ts b/core/gh_describe.ts index 6d73e3f..de991b4 100644 --- a/core/gh_describe.ts +++ b/core/gh_describe.ts @@ -1,6 +1,7 @@ import { fetchHistory } from "./fetch_history.ts"; import { fetchSha } from "./fetch_sha.ts"; import { fetchTags } from "./fetch_tags.ts"; +import { fetchTotalCommit } from "./fetch_total_commit.ts"; import { GhDescribeError } from "./gh_describe_error.ts"; import { resolveRepo } from "./resolve_repo.ts"; import { searchTag } from "./search_tags.ts"; @@ -72,11 +73,26 @@ export interface GhDescribeOutput { shortSha: string; } -export function createDescribe(tag: string, distance: number, sha: string) { +// Return the slot of the most-significant bit set in x. +export function MSB(x: number) { + let r = 0; + while (x > 1) { + x >>= 1; + r++; + } + return r; +} + +export function createDescribe( + tag: string, + distance: number, + sha: string, + shortShaChars: number, +): string { if (distance === 0) { return tag; } else { - return `${tag}-${distance}-g${sha.substring(0, 7)}`; + return `${tag}-${distance}-g${sha.substring(0, shortShaChars)}`; } } @@ -92,12 +108,22 @@ export async function ghDescribe(options?: GhDescribeOptions): Promise { const sha = await fetchSha({ owner, repo, host, sha: commitish }); const histories = fetchHistory({ owner, repo, host, sha }); - return { sha, histories }; + + // Emulate https://github.com/git/git/blob/e9356ba3ea2a6754281ff7697b3e5a1697b21e24/object-name.c#L829-L847 + const commitCount = await fetchTotalCommit({ owner, repo, host, sha }); + // fetch highest set bit in commitCount + const distance = MSB(commitCount) + 1; + // calculate how many chars to use for short sha + // 7 is the default for git describe + // https://git-scm.com/docs/git-describe#_examples + const shortShaChars = Math.max(7, Math.round((distance + 1) / 2)); + + return { sha, histories, shortShaChars }; })(), ]); @@ -110,12 +136,12 @@ export async function ghDescribe(options?: GhDescribeOptions): Promise Number(v)); function nop() { } @@ -6671,6 +6800,9 @@ var require_util = __commonJS({ const m = val.toString().match(KEEPALIVE_TIMEOUT_EXPR); return m ? parseInt(m[1], 10) * 1e3 : null; } + function headerNameToString(value) { + return headerNameLowerCasedRecord[value] || value.toLowerCase(); + } function parseHeaders(headers, obj = {}) { if (!Array.isArray(headers)) return headers; @@ -6874,6 +7006,7 @@ var require_util = __commonJS({ isIterable, isAsyncIterable, isDestroyed, + headerNameToString, parseRawHeaders, parseHeaders, parseKeepAliveTimeout, @@ -7330,7 +7463,7 @@ var require_Dicer = __commonJS({ if (this._headerFirst && this._isPreamble) { if (!this._part) { this._part = new PartStream(this._partOpts); - if (this._events.preamble) { + if (this.listenerCount("preamble") !== 0) { this.emit("preamble", this._part); } else { this._ignore(); @@ -7393,7 +7526,7 @@ var require_Dicer = __commonJS({ } } if (this._dashes === 2) { - if (start + i < end && this._events.trailer) { + if (start + i < end && this.listenerCount("trailer") !== 0) { this.emit("trailer", data.slice(start + i, end)); } this.reset(); @@ -7416,9 +7549,9 @@ var require_Dicer = __commonJS({ this._part._read = function(n) { self._unpause(); }; - if (this._isPreamble && this._events.preamble) { + if (this._isPreamble && this.listenerCount("preamble") !== 0) { this.emit("preamble", this._part); - } else if (this._isPreamble !== true && this._events.part) { + } else if (this._isPreamble !== true && this.listenerCount("part") !== 0) { this.emit("part", this._part); } else { this._ignore(); @@ -7580,7 +7713,7 @@ var require_decodeText = __commonJS({ if (textDecoders.has(exports2.toString())) { try { return textDecoders.get(exports2).decode(data); - } catch (e) { + } catch { } } return typeof data === "string" ? data : data.toString(); @@ -8363,7 +8496,7 @@ var require_multipart = __commonJS({ return skipPart(part); } ++nfiles; - if (!boy._events.file) { + if (boy.listenerCount("file") === 0) { self.parser._ignore(); return; } @@ -8969,7 +9102,7 @@ var require_main2 = __commonJS({ }); // dist/dnt/node_modules/undici/lib/fetch/constants.js -var require_constants = __commonJS({ +var require_constants2 = __commonJS({ "dist/dnt/node_modules/undici/lib/fetch/constants.js"(exports2, module2) { "use strict"; var { MessageChannel, receiveMessageOnPort } = require("worker_threads"); @@ -9207,15 +9340,18 @@ var require_global = __commonJS({ var require_util2 = __commonJS({ "dist/dnt/node_modules/undici/lib/fetch/util.js"(exports2, module2) { "use strict"; - var { redirectStatusSet, referrerPolicySet: referrerPolicyTokens, badPortsSet } = require_constants(); + var { redirectStatusSet, referrerPolicySet: referrerPolicyTokens, badPortsSet } = require_constants2(); var { getGlobalOrigin } = require_global(); var { performance: performance2 } = require("perf_hooks"); var { isBlobLike, toUSVString, ReadableStreamFrom } = require_util(); var assert2 = require("assert"); var { isUint8Array } = require("util/types"); + var supportedHashes = []; var crypto4; try { crypto4 = require("crypto"); + const possibleRelevantHashes = ["sha256", "sha384", "sha512"]; + supportedHashes = crypto4.getHashes().filter((hash) => possibleRelevantHashes.includes(hash)); } catch { } function responseURL(response) { @@ -9491,45 +9627,37 @@ var require_util2 = __commonJS({ if (parsedMetadata.length === 0) { return true; } - const list = parsedMetadata.sort((c, d) => d.algo.localeCompare(c.algo)); - const strongest = list[0].algo; - const metadata = list.filter((item) => item.algo === strongest); + const strongest = getStrongestMetadata(parsedMetadata); + const metadata = filterMetadataListByAlgorithm(parsedMetadata, strongest); for (const item of metadata) { const algorithm = item.algo; - let expectedValue = item.hash; - if (expectedValue.endsWith("==")) { - expectedValue = expectedValue.slice(0, -2); - } + const expectedValue = item.hash; let actualValue = crypto4.createHash(algorithm).update(bytes).digest("base64"); - if (actualValue.endsWith("==")) { - actualValue = actualValue.slice(0, -2); - } - if (actualValue === expectedValue) { - return true; - } - let actualBase64URL = crypto4.createHash(algorithm).update(bytes).digest("base64url"); - if (actualBase64URL.endsWith("==")) { - actualBase64URL = actualBase64URL.slice(0, -2); + if (actualValue[actualValue.length - 1] === "=") { + if (actualValue[actualValue.length - 2] === "=") { + actualValue = actualValue.slice(0, -2); + } else { + actualValue = actualValue.slice(0, -1); + } } - if (actualBase64URL === expectedValue) { + if (compareBase64Mixed(actualValue, expectedValue)) { return true; } } return false; } - var parseHashWithOptions = /((?sha256|sha384|sha512)-(?[A-z0-9+/]{1}.*={0,2}))( +[\x21-\x7e]?)?/i; + var parseHashWithOptions = /(?sha256|sha384|sha512)-((?[A-Za-z0-9+/]+|[A-Za-z0-9_-]+)={0,2}(?:\s|$)( +[!-~]*)?)?/i; function parseMetadata(metadata) { const result = []; let empty = true; - const supportedHashes = crypto4.getHashes(); for (const token of metadata.split(" ")) { empty = false; const parsedToken = parseHashWithOptions.exec(token); - if (parsedToken === null || parsedToken.groups === void 0) { + if (parsedToken === null || parsedToken.groups === void 0 || parsedToken.groups.algo === void 0) { continue; } - const algorithm = parsedToken.groups.algo; - if (supportedHashes.includes(algorithm.toLowerCase())) { + const algorithm = parsedToken.groups.algo.toLowerCase(); + if (supportedHashes.includes(algorithm)) { result.push(parsedToken.groups); } } @@ -9538,6 +9666,51 @@ var require_util2 = __commonJS({ } return result; } + function getStrongestMetadata(metadataList) { + let algorithm = metadataList[0].algo; + if (algorithm[3] === "5") { + return algorithm; + } + for (let i = 1; i < metadataList.length; ++i) { + const metadata = metadataList[i]; + if (metadata.algo[3] === "5") { + algorithm = "sha512"; + break; + } else if (algorithm[3] === "3") { + continue; + } else if (metadata.algo[3] === "3") { + algorithm = "sha384"; + } + } + return algorithm; + } + function filterMetadataListByAlgorithm(metadataList, algorithm) { + if (metadataList.length === 1) { + return metadataList; + } + let pos = 0; + for (let i = 0; i < metadataList.length; ++i) { + if (metadataList[i].algo === algorithm) { + metadataList[pos++] = metadataList[i]; + } + } + metadataList.length = pos; + return metadataList; + } + function compareBase64Mixed(actualValue, expectedValue) { + if (actualValue.length !== expectedValue.length) { + return false; + } + for (let i = 0; i < actualValue.length; ++i) { + if (actualValue[i] !== expectedValue[i]) { + if (actualValue[i] === "+" && expectedValue[i] === "-" || actualValue[i] === "/" && expectedValue[i] === "_") { + continue; + } + return false; + } + } + return true; + } function tryUpgradeRequestToAPotentiallyTrustworthyURL(request) { } function sameOrigin(A, B) { @@ -9761,7 +9934,8 @@ var require_util2 = __commonJS({ urlHasHttpsScheme, urlIsHttpHttpsScheme, readAllBytes, - normalizeMethodRecord + normalizeMethodRecord, + parseMetadata }; } }); @@ -10798,7 +10972,7 @@ var require_body = __commonJS({ var { FormData } = require_formdata(); var { kState } = require_symbols2(); var { webidl } = require_webidl(); - var { DOMException: DOMException2, structuredClone } = require_constants(); + var { DOMException: DOMException2, structuredClone } = require_constants2(); var { Blob: Blob2, File: NativeFile } = require("buffer"); var { kBodyUsed } = require_symbols(); var assert2 = require("assert"); @@ -11894,7 +12068,7 @@ var require_utils2 = __commonJS({ }); // dist/dnt/node_modules/undici/lib/llhttp/constants.js -var require_constants2 = __commonJS({ +var require_constants3 = __commonJS({ "dist/dnt/node_modules/undici/lib/llhttp/constants.js"(exports2) { "use strict"; Object.defineProperty(exports2, "__esModule", { value: true }); @@ -12329,7 +12503,17 @@ var require_RedirectHandler = __commonJS({ } } function shouldRemoveHeader(header, removeContent, unknownOrigin) { - return header.length === 4 && header.toString().toLowerCase() === "host" || removeContent && header.toString().toLowerCase().indexOf("content-") === 0 || unknownOrigin && header.length === 13 && header.toString().toLowerCase() === "authorization" || unknownOrigin && header.length === 6 && header.toString().toLowerCase() === "cookie"; + if (header.length === 4) { + return util.headerNameToString(header) === "host"; + } + if (removeContent && util.headerNameToString(header).startsWith("content-")) { + return true; + } + if (unknownOrigin && (header.length === 13 || header.length === 6 || header.length === 19)) { + const name = util.headerNameToString(header); + return name === "authorization" || name === "cookie" || name === "proxy-authorization"; + } + return false; } function cleanRequestHeaders(headers, removeContent, unknownOrigin) { const ret = []; @@ -12774,7 +12958,7 @@ var require_client = __commonJS({ ); resume(client); } - var constants3 = require_constants2(); + var constants3 = require_constants3(); var createRedirectInterceptor = require_redirectInterceptor(); var EMPTY_BUF = Buffer.alloc(0); async function lazyllhttp() { @@ -17478,7 +17662,7 @@ var require_response = __commonJS({ redirectStatusSet, nullBodyStatus, DOMException: DOMException2 - } = require_constants(); + } = require_constants2(); var { kState, kHeaders, kGuard, kRealm } = require_symbols2(); var { webidl } = require_webidl(); var { FormData } = require_formdata(); @@ -17860,7 +18044,7 @@ var require_request2 = __commonJS({ requestCredentials, requestCache, requestDuplex - } = require_constants(); + } = require_constants2(); var { kEnumerableProperty } = util; var { kHeaders, kSignal, kState, kGuard, kRealm } = require_symbols2(); var { webidl } = require_webidl(); @@ -18529,7 +18713,7 @@ var require_fetch = __commonJS({ requestBodyHeader, subresourceSet, DOMException: DOMException2 - } = require_constants(); + } = require_constants2(); var { kHeadersList } = require_symbols(); var EE = require("events"); var { Readable, pipeline } = require("stream"); @@ -19893,7 +20077,7 @@ var require_util4 = __commonJS({ } = require_symbols3(); var { ProgressEvent } = require_progressevent(); var { getEncoding } = require_encoding(); - var { DOMException: DOMException2 } = require_constants(); + var { DOMException: DOMException2 } = require_constants2(); var { serializeAMimeType, parseMIMEType } = require_dataURL(); var { types } = require("util"); var { StringDecoder } = require("string_decoder"); @@ -21021,7 +21205,7 @@ var require_cachestorage = __commonJS({ }); // dist/dnt/node_modules/undici/lib/cookies/constants.js -var require_constants3 = __commonJS({ +var require_constants4 = __commonJS({ "dist/dnt/node_modules/undici/lib/cookies/constants.js"(exports2, module2) { "use strict"; var maxAttributeValueSize = 1024; @@ -21196,7 +21380,7 @@ var require_util6 = __commonJS({ var require_parse = __commonJS({ "dist/dnt/node_modules/undici/lib/cookies/parse.js"(exports2, module2) { "use strict"; - var { maxNameValuePairSize, maxAttributeValueSize } = require_constants3(); + var { maxNameValuePairSize, maxAttributeValueSize } = require_constants4(); var { isCTLExcludingHtab } = require_util6(); var { collectASequenceOfCodePointsFast } = require_dataURL(); var assert2 = require("assert"); @@ -21461,7 +21645,7 @@ var require_cookies = __commonJS({ }); // dist/dnt/node_modules/undici/lib/websocket/constants.js -var require_constants4 = __commonJS({ +var require_constants5 = __commonJS({ "dist/dnt/node_modules/undici/lib/websocket/constants.js"(exports2, module2) { "use strict"; var uid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; @@ -21778,7 +21962,7 @@ var require_util7 = __commonJS({ "dist/dnt/node_modules/undici/lib/websocket/util.js"(exports2, module2) { "use strict"; var { kReadyState, kController, kResponse, kBinaryType, kWebSocketURL } = require_symbols5(); - var { states, opcodes } = require_constants4(); + var { states, opcodes } = require_constants5(); var { MessageEvent, ErrorEvent } = require_events(); function isEstablished(ws) { return ws[kReadyState] === states.OPEN; @@ -21868,7 +22052,7 @@ var require_connection = __commonJS({ "dist/dnt/node_modules/undici/lib/websocket/connection.js"(exports2, module2) { "use strict"; var diagnosticsChannel = require("diagnostics_channel"); - var { uid, states } = require_constants4(); + var { uid, states } = require_constants5(); var { kReadyState, kSentClose, @@ -22015,7 +22199,7 @@ var require_connection = __commonJS({ var require_frame = __commonJS({ "dist/dnt/node_modules/undici/lib/websocket/frame.js"(exports2, module2) { "use strict"; - var { maxUnsigned16Bit } = require_constants4(); + var { maxUnsigned16Bit } = require_constants5(); var crypto4; try { crypto4 = require("crypto"); @@ -22074,7 +22258,7 @@ var require_receiver = __commonJS({ "use strict"; var { Writable } = require("stream"); var diagnosticsChannel = require("diagnostics_channel"); - var { parserStates, opcodes, states, emptyBuffer } = require_constants4(); + var { parserStates, opcodes, states, emptyBuffer } = require_constants5(); var { kReadyState, kSentClose, kResponse, kReceivedClose } = require_symbols5(); var { isValidStatusCode, failWebsocketConnection, websocketMessageReceived } = require_util7(); var { WebsocketFrameSend } = require_frame(); @@ -22315,10 +22499,10 @@ var require_websocket = __commonJS({ "dist/dnt/node_modules/undici/lib/websocket/websocket.js"(exports2, module2) { "use strict"; var { webidl } = require_webidl(); - var { DOMException: DOMException2 } = require_constants(); + var { DOMException: DOMException2 } = require_constants2(); var { URLSerializer } = require_dataURL(); var { getGlobalOrigin } = require_global(); - var { staticPropertyDescriptors, states, opcodes, emptyBuffer } = require_constants4(); + var { staticPropertyDescriptors, states, opcodes, emptyBuffer } = require_constants5(); var { kWebSocketURL, kReadyState, @@ -23358,7 +23542,7 @@ var require_lib2 = __commonJS({ if (this._keepAlive && useProxy) { agent = this._proxyAgent; } - if (this._keepAlive && !useProxy) { + if (!useProxy) { agent = this._agent; } if (agent) { @@ -23387,14 +23571,11 @@ var require_lib2 = __commonJS({ agent = tunnelAgent(agentOptions); this._proxyAgent = agent; } - if (this._keepAlive && !agent) { + if (!agent) { const options = { keepAlive: this._keepAlive, maxSockets }; agent = usingSsl ? new https.Agent(options) : new http.Agent(options); this._agent = agent; } - if (!agent) { - agent = usingSsl ? https.globalAgent : http.globalAgent; - } if (usingSsl && this._ignoreSslError) { agent.options = Object.assign(agent.options || {}, { rejectUnauthorized: false @@ -23412,7 +23593,7 @@ var require_lib2 = __commonJS({ } const usingSsl = parsedUrl.protocol === "https:"; proxyAgent = new undici_1.ProxyAgent(Object.assign({ uri: proxyUrl.href, pipelining: !this._keepAlive ? 0 : 1 }, (proxyUrl.username || proxyUrl.password) && { - token: `${proxyUrl.username}:${proxyUrl.password}` + token: `Basic ${Buffer.from(`${proxyUrl.username}:${proxyUrl.password}`).toString("base64")}` })); this._proxyAgentDispatcher = proxyAgent; if (usingSsl && this._ignoreSslError) { @@ -24375,6 +24556,19 @@ ${stderr}`); } }; +// dist/dnt/esm/gh-wrapper/graphql.js +function graphql({ host, jq } = {}) { + return async function graphqlTag(...[template, ...substitutions]) { + const query = String.raw(template, ...substitutions); + const args = ["api", "graphql", "-f", `query=${query}`]; + if (host) + args.push("--hostname", host); + if (jq) + args.push("-q", jq); + return await exec(args); + }; +} + // dist/dnt/esm/gh-wrapper/list_commits.js function createUrl({ owner, repo, sha, perPage, page }) { const param = new URLSearchParams(); @@ -24854,6 +25048,24 @@ async function fetchTags({ owner, repo, host, match, exclude }) { return new Map(tags); } +// dist/dnt/esm/core/fetch_total_commit.js +async function fetchTotalCommit({ owner, repo, host, sha }) { + const stdout = await graphql({ host })` + query { + repository(owner: "${owner}", name: "${repo}") { + object(expression: "${sha}") { + ... on Commit { + history(first: 0) { + totalCount + } + } + } + } + }`; + const repository = JSON.parse(stdout); + return repository.data.repository.object.history.totalCount; +} + // dist/dnt/esm/core/ghrepo.js var GitHubRepository = class { constructor(owner, repo, host) { @@ -24960,22 +25172,33 @@ async function searchTag(tags, histories) { } // dist/dnt/esm/core/gh_describe.js -function createDescribe(tag, distance, sha) { +function MSB(x) { + let r = 0; + while (x > 1) { + x >>= 1; + r++; + } + return r; +} +function createDescribe(tag, distance, sha, shortShaChars) { if (distance === 0) { return tag; } else { - return `${tag}-${distance}-g${sha.substring(0, 7)}`; + return `${tag}-${distance}-g${sha.substring(0, shortShaChars)}`; } } async function ghDescribe(options) { const { commitish, defaultTag, match, exclude } = options ?? {}; const { owner, repo, host } = await resolveRepo(options?.repo); - const [tags, { sha, histories }] = await Promise.all([ + const [tags, { sha, histories, shortShaChars }] = await Promise.all([ fetchTags({ owner, repo, host, match, exclude }), (async () => { const sha2 = await fetchSha({ owner, repo, host, sha: commitish }); const histories2 = fetchHistory({ owner, repo, host, sha: sha2 }); - return { sha: sha2, histories: histories2 }; + const commitCount = await fetchTotalCommit({ owner, repo, host, sha: sha2 }); + const distance2 = MSB(commitCount) + 1; + const shortShaChars2 = Math.max(7, Math.round((distance2 + 1) / 2)); + return { sha: sha2, histories: histories2, shortShaChars: shortShaChars2 }; })() ]); const { distance, tag } = await searchTag(tags, histories) || { @@ -24985,13 +25208,13 @@ async function ghDescribe(options) { if (!tag) { throw new GhDescribeError("No names found, cannot describe anything."); } - const describe = createDescribe(tag, distance, sha); + const describe = createDescribe(tag, distance, sha, shortShaChars); return { describe, tag, distance, sha, - shortSha: sha.substring(0, 7) + shortSha: sha.substring(0, shortShaChars) }; } diff --git a/dist/cli.js b/dist/cli.js index 19a7a6e..1850abe 100644 --- a/dist/cli.js +++ b/dist/cli.js @@ -10778,6 +10778,19 @@ ${stderr}`); } }; +// dist/dnt/esm/gh-wrapper/graphql.js +function graphql({ host, jq } = {}) { + return async function graphqlTag(...[template, ...substitutions]) { + const query = String.raw(template, ...substitutions); + const args = ["api", "graphql", "-f", `query=${query}`]; + if (host) + args.push("--hostname", host); + if (jq) + args.push("-q", jq); + return await exec2(args); + }; +} + // dist/dnt/esm/gh-wrapper/list_commits.js function createUrl({ owner, repo, sha, perPage, page }) { const param = new URLSearchParams(); @@ -10895,6 +10908,24 @@ async function fetchTags({ owner, repo, host, match, exclude }) { return new Map(tags); } +// dist/dnt/esm/core/fetch_total_commit.js +async function fetchTotalCommit({ owner, repo, host, sha }) { + const stdout = await graphql({ host })` + query { + repository(owner: "${owner}", name: "${repo}") { + object(expression: "${sha}") { + ... on Commit { + history(first: 0) { + totalCount + } + } + } + } + }`; + const repository = JSON.parse(stdout); + return repository.data.repository.object.history.totalCount; +} + // dist/dnt/esm/core/ghrepo.js var GitHubRepository = class { constructor(owner, repo, host) { @@ -11001,22 +11032,33 @@ async function searchTag(tags, histories) { } // dist/dnt/esm/core/gh_describe.js -function createDescribe(tag, distance2, sha) { +function MSB(x) { + let r = 0; + while (x > 1) { + x >>= 1; + r++; + } + return r; +} +function createDescribe(tag, distance2, sha, shortShaChars) { if (distance2 === 0) { return tag; } else { - return `${tag}-${distance2}-g${sha.substring(0, 7)}`; + return `${tag}-${distance2}-g${sha.substring(0, shortShaChars)}`; } } async function ghDescribe(options) { const { commitish, defaultTag, match, exclude } = options ?? {}; const { owner, repo, host } = await resolveRepo(options?.repo); - const [tags, { sha, histories }] = await Promise.all([ + const [tags, { sha, histories, shortShaChars }] = await Promise.all([ fetchTags({ owner, repo, host, match, exclude }), (async () => { const sha2 = await fetchSha({ owner, repo, host, sha: commitish }); const histories2 = fetchHistory({ owner, repo, host, sha: sha2 }); - return { sha: sha2, histories: histories2 }; + const commitCount = await fetchTotalCommit({ owner, repo, host, sha: sha2 }); + const distance3 = MSB(commitCount) + 1; + const shortShaChars2 = Math.max(7, Math.round((distance3 + 1) / 2)); + return { sha: sha2, histories: histories2, shortShaChars: shortShaChars2 }; })() ]); const { distance: distance2, tag } = await searchTag(tags, histories) || { @@ -11026,13 +11068,13 @@ async function ghDescribe(options) { if (!tag) { throw new GhDescribeError("No names found, cannot describe anything."); } - const describe2 = createDescribe(tag, distance2, sha); + const describe2 = createDescribe(tag, distance2, sha, shortShaChars); return { describe: describe2, tag, distance: distance2, sha, - shortSha: sha.substring(0, 7) + shortSha: sha.substring(0, shortShaChars) }; }