diff --git a/dist/index.js b/dist/index.js index b555e94..9d609ef 100644 --- a/dist/index.js +++ b/dist/index.js @@ -55,7 +55,6 @@ exports.tryFetch = tryFetch; exports.buildBranchCommits = buildBranchCommits; exports.createOrUpdateBranch = createOrUpdateBranch; const core = __importStar(__nccwpck_require__(7484)); -const uuid_1 = __nccwpck_require__(2048); const utils = __importStar(__nccwpck_require__(9277)); const CHERRYPICK_EMPTY = 'The previous cherry-pick is now empty, possibly due to conflict resolution.'; const NOTHING_TO_COMMIT = 'nothing to commit, working tree clean'; @@ -178,13 +177,27 @@ function createOrUpdateBranch(git, commitMessage, base, branch, branchRemoteName if (workingBaseType == WorkingBaseType.Commit && !base) { throw new Error(`When in 'detached HEAD' state, 'base' must be supplied.`); } - // If the base is not specified it is assumed to be the working base. - base = base ? base : workingBase; + let action = 'none'; + let hasDiffWithBase = false; const baseRemote = 'origin'; - // Save the working base changes to a temporary branch - const tempBranch = (0, uuid_1.v4)(); - yield git.checkout(tempBranch, 'HEAD'); - // Commit any uncommitted changes + if (workingBase != branch) { + if (!(yield tryFetch(git, branchRemoteName, branch, 0))) { + // The pull request branch does not exist + core.info(`Pull request branch '${branch}' does not exist yet.`); + // Create the pull request branch + yield git.checkout(branch, base); + action = 'created'; + core.info(`Created branch '${branch}'`); + // Check if the pull request branch is ahead of the base + } + else { + // The pull request branch exists + core.info(`Pull request branch '${branch}' already exists as remote branch '${branchRemoteName}/${branch}'`); + // Checkout the pull request branch + yield git.checkout(branch); + } + } + // Commit any changes if (yield git.isDirty(true, addPaths)) { core.info('Uncommitted changes found. Adding a commit.'); const aopts = ['add']; @@ -206,128 +219,34 @@ function createOrUpdateBranch(git, commitMessage, base, branch, branchRemoteName throw new Error(`Unexpected error: ${commitResult.stderr}`); } } - // Stash any uncommitted tracked and untracked changes - const stashed = yield git.stashPush(['--include-untracked']); - // Reset the working base - // Commits made during the workflow will be removed - if (workingBaseType == WorkingBaseType.Branch) { - core.info(`Resetting working base branch '${workingBase}'`); - yield git.checkout(workingBase); - yield git.exec(['reset', '--hard', `${baseRemote}/${workingBase}`]); - } - // If the working base is not the base, rebase the temp branch commits - // This will also be true if the working base type is a commit - if (workingBase != base) { - core.info(`Rebasing commits made to ${workingBaseType} '${workingBase}' on to base branch '${base}'`); - const fetchArgs = ['--force']; - if (branchRemoteName != 'fork') { - // If pushing to a fork we cannot shallow fetch otherwise the 'shallow update not allowed' error occurs - fetchArgs.push('--depth=1'); - } - // Checkout the actual base - yield git.fetch([`${base}:${base}`], baseRemote, fetchArgs); - yield git.checkout(base); - // Cherrypick commits from the temporary branch starting from the working base - const commits = yield git.revList([`${workingBase}..${tempBranch}`, '.'], ['--reverse']); - for (const commit of splitLines(commits)) { - const result = yield git.cherryPick(['--strategy=recursive', '--strategy-option=theirs', commit], true); - if (result.exitCode != 0 && !result.stderr.includes(CHERRYPICK_EMPTY)) { - throw new Error(`Unexpected error: ${result.stderr}`); - } - } - // Reset the temp branch to the working index - yield git.checkout(tempBranch, 'HEAD'); - // Reset the base - yield git.fetch([`${base}:${base}`], baseRemote, fetchArgs); - } - // Determine the fetch depth for the pull request branch (best effort) - const tempBranchCommitsAhead = yield commitsAhead(git, base, tempBranch); - const fetchDepth = tempBranchCommitsAhead > 0 - ? tempBranchCommitsAhead + FETCH_DEPTH_MARGIN - : FETCH_DEPTH_MARGIN; - let action = 'none'; - let hasDiffWithBase = false; - // Try to fetch the pull request branch - if (!(yield tryFetch(git, branchRemoteName, branch, fetchDepth))) { - // The pull request branch does not exist - core.info(`Pull request branch '${branch}' does not exist yet.`); - // Create the pull request branch - yield git.checkout(branch, tempBranch); - // Check if the pull request branch is ahead of the base - hasDiffWithBase = yield isAhead(git, base, branch); - if (hasDiffWithBase) { - action = 'created'; - core.info(`Created branch '${branch}'`); - } - else { - core.info(`Branch '${branch}' is not ahead of base '${base}' and will not be created`); - } - } - else { - // The pull request branch exists - core.info(`Pull request branch '${branch}' already exists as remote branch '${branchRemoteName}/${branch}'`); - // Checkout the pull request branch - yield git.checkout(branch); - // Reset the branch if one of the following conditions is true. - // - If the branch differs from the recreated temp branch. - // - If the number of commits ahead of the base branch differs between the branch and - // temp branch. This catches a case where the base branch has been force pushed to - // a new commit. - // - If the recreated temp branch is not ahead of the base. This means there will be - // no pull request diff after the branch is reset. This will reset any undeleted - // branches after merging. In particular, it catches a case where the branch was - // squash merged but not deleted. We need to reset to make sure it doesn't appear - // to have a diff with the base due to different commits for the same changes. - // - If the diff of the commits ahead of the base branch differs between the branch and - // temp branch. This catches a case where changes have been partially merged to the - // base. The overall diff is the same, but the branch needs to be rebased to show - // the correct diff. - // - // For changes on base this reset is equivalent to a rebase of the pull request branch. - const branchCommitsAhead = yield commitsAhead(git, base, branch); - if ((yield git.hasDiff([`${branch}..${tempBranch}`])) || - branchCommitsAhead != tempBranchCommitsAhead || - !(tempBranchCommitsAhead > 0) || // !isAhead - (yield commitsHaveDiff(git, branch, tempBranch, tempBranchCommitsAhead))) { - core.info(`Resetting '${branch}'`); - // Alternatively, git switch -C branch tempBranch - yield git.checkout(branch, tempBranch); - } - // Check if the pull request branch has been updated - // If the branch was reset or updated it will be ahead - // It may be behind if a reset now results in no diff with the base - if (!(yield isEven(git, `${branchRemoteName}/${branch}`, branch))) { - action = 'updated'; - core.info(`Updated branch '${branch}'`); - } - else { - action = 'not-updated'; - core.info(`Branch '${branch}' is even with its remote and will not be updated`); - } - // Check if the pull request branch is ahead of the base - hasDiffWithBase = yield isAhead(git, base, branch); + // Check if the pull request branch is behind the base branch + let wasRebased = false; + yield git.exec(['fetch', baseRemote, base]); + if (yield isBehind(git, base, branch)) { + // Rebase the current branch onto the base branch + core.info(`Pull request branch '${branch}' is behind base branch '${base}'.`); + yield git.exec(['pull', '--rebase', baseRemote, base]); + core.info(`Rebased '${branch}' commits ontop of '${base}'.`); + wasRebased = true; } + hasDiffWithBase = yield isAhead(git, base, branch); + // If the base is not specified it is assumed to be the working base. + base = base ? base : workingBase; // Get the base and head SHAs const baseSha = yield git.revParse(base); const baseCommit = yield git.getCommit(baseSha); const headSha = yield git.revParse(branch); let branchCommits = []; if (hasDiffWithBase) { + action = 'updated'; // Build the branch commits branchCommits = yield buildBranchCommits(git, base, branch); } - // Delete the temporary branch - yield git.exec(['branch', '--delete', '--force', tempBranch]); - // Checkout the working base to leave the local repository as it was found - yield git.checkout(workingBase); - // Restore any stashed changes - if (stashed) { - yield git.stashPop(); - } return { action: action, base: base, hasDiffWithBase: hasDiffWithBase, + wasRebased: wasRebased, baseCommit: baseCommit, headSha: headSha, branchCommits: branchCommits @@ -526,11 +445,7 @@ function createPullRequest(inputs) { } } else { - yield git.push([ - '--force-with-lease', - branchRemoteName, - `${inputs.branch}:refs/heads/${inputs.branch}` - ]); + yield git.push(result.wasRebased ? [`--force-with-lease`, branchRemoteName, inputs.branch] : []); } core.endGroup(); } @@ -29022,687 +28937,6 @@ if (process.env.NODE_DEBUG && /\btunnel\b/.test(process.env.NODE_DEBUG)) { exports.debug = debug; // for test -/***/ }), - -/***/ 2048: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -Object.defineProperty(exports, "NIL", ({ - enumerable: true, - get: function () { - return _nil.default; - } -})); -Object.defineProperty(exports, "parse", ({ - enumerable: true, - get: function () { - return _parse.default; - } -})); -Object.defineProperty(exports, "stringify", ({ - enumerable: true, - get: function () { - return _stringify.default; - } -})); -Object.defineProperty(exports, "v1", ({ - enumerable: true, - get: function () { - return _v.default; - } -})); -Object.defineProperty(exports, "v3", ({ - enumerable: true, - get: function () { - return _v2.default; - } -})); -Object.defineProperty(exports, "v4", ({ - enumerable: true, - get: function () { - return _v3.default; - } -})); -Object.defineProperty(exports, "v5", ({ - enumerable: true, - get: function () { - return _v4.default; - } -})); -Object.defineProperty(exports, "validate", ({ - enumerable: true, - get: function () { - return _validate.default; - } -})); -Object.defineProperty(exports, "version", ({ - enumerable: true, - get: function () { - return _version.default; - } -})); - -var _v = _interopRequireDefault(__nccwpck_require__(6415)); - -var _v2 = _interopRequireDefault(__nccwpck_require__(1697)); - -var _v3 = _interopRequireDefault(__nccwpck_require__(4676)); - -var _v4 = _interopRequireDefault(__nccwpck_require__(9771)); - -var _nil = _interopRequireDefault(__nccwpck_require__(7723)); - -var _version = _interopRequireDefault(__nccwpck_require__(5868)); - -var _validate = _interopRequireDefault(__nccwpck_require__(6200)); - -var _stringify = _interopRequireDefault(__nccwpck_require__(7597)); - -var _parse = _interopRequireDefault(__nccwpck_require__(7267)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -/***/ }), - -/***/ 216: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _crypto = _interopRequireDefault(__nccwpck_require__(6982)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function md5(bytes) { - if (Array.isArray(bytes)) { - bytes = Buffer.from(bytes); - } else if (typeof bytes === 'string') { - bytes = Buffer.from(bytes, 'utf8'); - } - - return _crypto.default.createHash('md5').update(bytes).digest(); -} - -var _default = md5; -exports["default"] = _default; - -/***/ }), - -/***/ 4221: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _crypto = _interopRequireDefault(__nccwpck_require__(6982)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -var _default = { - randomUUID: _crypto.default.randomUUID -}; -exports["default"] = _default; - -/***/ }), - -/***/ 7723: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; -var _default = '00000000-0000-0000-0000-000000000000'; -exports["default"] = _default; - -/***/ }), - -/***/ 7267: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _validate = _interopRequireDefault(__nccwpck_require__(6200)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function parse(uuid) { - if (!(0, _validate.default)(uuid)) { - throw TypeError('Invalid UUID'); - } - - let v; - const arr = new Uint8Array(16); // Parse ########-....-....-....-............ - - arr[0] = (v = parseInt(uuid.slice(0, 8), 16)) >>> 24; - arr[1] = v >>> 16 & 0xff; - arr[2] = v >>> 8 & 0xff; - arr[3] = v & 0xff; // Parse ........-####-....-....-............ - - arr[4] = (v = parseInt(uuid.slice(9, 13), 16)) >>> 8; - arr[5] = v & 0xff; // Parse ........-....-####-....-............ - - arr[6] = (v = parseInt(uuid.slice(14, 18), 16)) >>> 8; - arr[7] = v & 0xff; // Parse ........-....-....-####-............ - - arr[8] = (v = parseInt(uuid.slice(19, 23), 16)) >>> 8; - arr[9] = v & 0xff; // Parse ........-....-....-....-############ - // (Use "/" to avoid 32-bit truncation when bit-shifting high-order bytes) - - arr[10] = (v = parseInt(uuid.slice(24, 36), 16)) / 0x10000000000 & 0xff; - arr[11] = v / 0x100000000 & 0xff; - arr[12] = v >>> 24 & 0xff; - arr[13] = v >>> 16 & 0xff; - arr[14] = v >>> 8 & 0xff; - arr[15] = v & 0xff; - return arr; -} - -var _default = parse; -exports["default"] = _default; - -/***/ }), - -/***/ 7879: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; -var _default = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i; -exports["default"] = _default; - -/***/ }), - -/***/ 2973: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = rng; - -var _crypto = _interopRequireDefault(__nccwpck_require__(6982)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -const rnds8Pool = new Uint8Array(256); // # of random values to pre-allocate - -let poolPtr = rnds8Pool.length; - -function rng() { - if (poolPtr > rnds8Pool.length - 16) { - _crypto.default.randomFillSync(rnds8Pool); - - poolPtr = 0; - } - - return rnds8Pool.slice(poolPtr, poolPtr += 16); -} - -/***/ }), - -/***/ 507: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _crypto = _interopRequireDefault(__nccwpck_require__(6982)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function sha1(bytes) { - if (Array.isArray(bytes)) { - bytes = Buffer.from(bytes); - } else if (typeof bytes === 'string') { - bytes = Buffer.from(bytes, 'utf8'); - } - - return _crypto.default.createHash('sha1').update(bytes).digest(); -} - -var _default = sha1; -exports["default"] = _default; - -/***/ }), - -/***/ 7597: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; -exports.unsafeStringify = unsafeStringify; - -var _validate = _interopRequireDefault(__nccwpck_require__(6200)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -/** - * Convert array of 16 byte values to UUID string format of the form: - * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX - */ -const byteToHex = []; - -for (let i = 0; i < 256; ++i) { - byteToHex.push((i + 0x100).toString(16).slice(1)); -} - -function unsafeStringify(arr, offset = 0) { - // Note: Be careful editing this code! It's been tuned for performance - // and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434 - return byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + '-' + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + '-' + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + '-' + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + '-' + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]; -} - -function stringify(arr, offset = 0) { - const uuid = unsafeStringify(arr, offset); // Consistency check for valid UUID. If this throws, it's likely due to one - // of the following: - // - One or more input array values don't map to a hex octet (leading to - // "undefined" in the uuid) - // - Invalid input values for the RFC `version` or `variant` fields - - if (!(0, _validate.default)(uuid)) { - throw TypeError('Stringified UUID is invalid'); - } - - return uuid; -} - -var _default = stringify; -exports["default"] = _default; - -/***/ }), - -/***/ 6415: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _rng = _interopRequireDefault(__nccwpck_require__(2973)); - -var _stringify = __nccwpck_require__(7597); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -// **`v1()` - Generate time-based UUID** -// -// Inspired by https://github.com/LiosK/UUID.js -// and http://docs.python.org/library/uuid.html -let _nodeId; - -let _clockseq; // Previous uuid creation time - - -let _lastMSecs = 0; -let _lastNSecs = 0; // See https://github.com/uuidjs/uuid for API details - -function v1(options, buf, offset) { - let i = buf && offset || 0; - const b = buf || new Array(16); - options = options || {}; - let node = options.node || _nodeId; - let clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq; // node and clockseq need to be initialized to random values if they're not - // specified. We do this lazily to minimize issues related to insufficient - // system entropy. See #189 - - if (node == null || clockseq == null) { - const seedBytes = options.random || (options.rng || _rng.default)(); - - if (node == null) { - // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1) - node = _nodeId = [seedBytes[0] | 0x01, seedBytes[1], seedBytes[2], seedBytes[3], seedBytes[4], seedBytes[5]]; - } - - if (clockseq == null) { - // Per 4.2.2, randomize (14 bit) clockseq - clockseq = _clockseq = (seedBytes[6] << 8 | seedBytes[7]) & 0x3fff; - } - } // UUID timestamps are 100 nano-second units since the Gregorian epoch, - // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so - // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs' - // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00. - - - let msecs = options.msecs !== undefined ? options.msecs : Date.now(); // Per 4.2.1.2, use count of uuid's generated during the current clock - // cycle to simulate higher resolution clock - - let nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1; // Time since last uuid creation (in msecs) - - const dt = msecs - _lastMSecs + (nsecs - _lastNSecs) / 10000; // Per 4.2.1.2, Bump clockseq on clock regression - - if (dt < 0 && options.clockseq === undefined) { - clockseq = clockseq + 1 & 0x3fff; - } // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new - // time interval - - - if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) { - nsecs = 0; - } // Per 4.2.1.2 Throw error if too many uuids are requested - - - if (nsecs >= 10000) { - throw new Error("uuid.v1(): Can't create more than 10M uuids/sec"); - } - - _lastMSecs = msecs; - _lastNSecs = nsecs; - _clockseq = clockseq; // Per 4.1.4 - Convert from unix epoch to Gregorian epoch - - msecs += 12219292800000; // `time_low` - - const tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000; - b[i++] = tl >>> 24 & 0xff; - b[i++] = tl >>> 16 & 0xff; - b[i++] = tl >>> 8 & 0xff; - b[i++] = tl & 0xff; // `time_mid` - - const tmh = msecs / 0x100000000 * 10000 & 0xfffffff; - b[i++] = tmh >>> 8 & 0xff; - b[i++] = tmh & 0xff; // `time_high_and_version` - - b[i++] = tmh >>> 24 & 0xf | 0x10; // include version - - b[i++] = tmh >>> 16 & 0xff; // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant) - - b[i++] = clockseq >>> 8 | 0x80; // `clock_seq_low` - - b[i++] = clockseq & 0xff; // `node` - - for (let n = 0; n < 6; ++n) { - b[i + n] = node[n]; - } - - return buf || (0, _stringify.unsafeStringify)(b); -} - -var _default = v1; -exports["default"] = _default; - -/***/ }), - -/***/ 1697: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _v = _interopRequireDefault(__nccwpck_require__(2930)); - -var _md = _interopRequireDefault(__nccwpck_require__(216)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -const v3 = (0, _v.default)('v3', 0x30, _md.default); -var _default = v3; -exports["default"] = _default; - -/***/ }), - -/***/ 2930: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports.URL = exports.DNS = void 0; -exports["default"] = v35; - -var _stringify = __nccwpck_require__(7597); - -var _parse = _interopRequireDefault(__nccwpck_require__(7267)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function stringToBytes(str) { - str = unescape(encodeURIComponent(str)); // UTF8 escape - - const bytes = []; - - for (let i = 0; i < str.length; ++i) { - bytes.push(str.charCodeAt(i)); - } - - return bytes; -} - -const DNS = '6ba7b810-9dad-11d1-80b4-00c04fd430c8'; -exports.DNS = DNS; -const URL = '6ba7b811-9dad-11d1-80b4-00c04fd430c8'; -exports.URL = URL; - -function v35(name, version, hashfunc) { - function generateUUID(value, namespace, buf, offset) { - var _namespace; - - if (typeof value === 'string') { - value = stringToBytes(value); - } - - if (typeof namespace === 'string') { - namespace = (0, _parse.default)(namespace); - } - - if (((_namespace = namespace) === null || _namespace === void 0 ? void 0 : _namespace.length) !== 16) { - throw TypeError('Namespace must be array-like (16 iterable integer values, 0-255)'); - } // Compute hash of namespace and value, Per 4.3 - // Future: Use spread syntax when supported on all platforms, e.g. `bytes = - // hashfunc([...namespace, ... value])` - - - let bytes = new Uint8Array(16 + value.length); - bytes.set(namespace); - bytes.set(value, namespace.length); - bytes = hashfunc(bytes); - bytes[6] = bytes[6] & 0x0f | version; - bytes[8] = bytes[8] & 0x3f | 0x80; - - if (buf) { - offset = offset || 0; - - for (let i = 0; i < 16; ++i) { - buf[offset + i] = bytes[i]; - } - - return buf; - } - - return (0, _stringify.unsafeStringify)(bytes); - } // Function#name is not settable on some platforms (#270) - - - try { - generateUUID.name = name; // eslint-disable-next-line no-empty - } catch (err) {} // For CommonJS default export support - - - generateUUID.DNS = DNS; - generateUUID.URL = URL; - return generateUUID; -} - -/***/ }), - -/***/ 4676: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _native = _interopRequireDefault(__nccwpck_require__(4221)); - -var _rng = _interopRequireDefault(__nccwpck_require__(2973)); - -var _stringify = __nccwpck_require__(7597); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function v4(options, buf, offset) { - if (_native.default.randomUUID && !buf && !options) { - return _native.default.randomUUID(); - } - - options = options || {}; - - const rnds = options.random || (options.rng || _rng.default)(); // Per 4.4, set bits for version and `clock_seq_hi_and_reserved` - - - rnds[6] = rnds[6] & 0x0f | 0x40; - rnds[8] = rnds[8] & 0x3f | 0x80; // Copy bytes to buffer, if provided - - if (buf) { - offset = offset || 0; - - for (let i = 0; i < 16; ++i) { - buf[offset + i] = rnds[i]; - } - - return buf; - } - - return (0, _stringify.unsafeStringify)(rnds); -} - -var _default = v4; -exports["default"] = _default; - -/***/ }), - -/***/ 9771: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _v = _interopRequireDefault(__nccwpck_require__(2930)); - -var _sha = _interopRequireDefault(__nccwpck_require__(507)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -const v5 = (0, _v.default)('v5', 0x50, _sha.default); -var _default = v5; -exports["default"] = _default; - -/***/ }), - -/***/ 6200: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _regex = _interopRequireDefault(__nccwpck_require__(7879)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function validate(uuid) { - return typeof uuid === 'string' && _regex.default.test(uuid); -} - -var _default = validate; -exports["default"] = _default; - -/***/ }), - -/***/ 5868: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _validate = _interopRequireDefault(__nccwpck_require__(6200)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function version(uuid) { - if (!(0, _validate.default)(uuid)) { - throw TypeError('Invalid UUID'); - } - - return parseInt(uuid.slice(14, 15), 16); -} - -var _default = version; -exports["default"] = _default; - /***/ }), /***/ 2613: diff --git a/src/create-or-update-branch.ts b/src/create-or-update-branch.ts index 2eecd45..3cf4629 100644 --- a/src/create-or-update-branch.ts +++ b/src/create-or-update-branch.ts @@ -161,6 +161,7 @@ interface CreateOrUpdateBranchResult { action: string base: string hasDiffWithBase: boolean + wasRebased: boolean baseCommit: Commit headSha: string branchCommits: Commit[] @@ -184,14 +185,30 @@ export async function createOrUpdateBranch( throw new Error(`When in 'detached HEAD' state, 'base' must be supplied.`) } - // If the base is not specified it is assumed to be the working base. - base = base ? base : workingBase + let action = 'none' + let hasDiffWithBase = false const baseRemote = 'origin' - // Save the working base changes to a temporary branch - const tempBranch = uuidv4() - await git.checkout(tempBranch, 'HEAD') - // Commit any uncommitted changes + if (workingBase != branch) { + if (!(await tryFetch(git, branchRemoteName, branch, 0))) { + // The pull request branch does not exist + core.info(`Pull request branch '${branch}' does not exist yet.`) + // Create the pull request branch + await git.checkout(branch, base) + action = 'created' + core.info(`Created branch '${branch}'`) + // Check if the pull request branch is ahead of the base + } else { + // The pull request branch exists + core.info( + `Pull request branch '${branch}' already exists as remote branch '${branchRemoteName}/${branch}'` + ) + // Checkout the pull request branch + await git.checkout(branch) + } + } + + // Commit any changes if (await git.isDirty(true, addPaths)) { core.info('Uncommitted changes found. Adding a commit.') const aopts = ['add'] @@ -215,129 +232,21 @@ export async function createOrUpdateBranch( } } - // Stash any uncommitted tracked and untracked changes - const stashed = await git.stashPush(['--include-untracked']) - - // Reset the working base - // Commits made during the workflow will be removed - if (workingBaseType == WorkingBaseType.Branch) { - core.info(`Resetting working base branch '${workingBase}'`) - await git.checkout(workingBase) - await git.exec(['reset', '--hard', `${baseRemote}/${workingBase}`]) + // Check if the pull request branch is behind the base branch + let wasRebased = false; + await git.exec(['fetch', baseRemote, base]) + if (await isBehind(git, base, branch)) { + // Rebase the current branch onto the base branch + core.info(`Pull request branch '${branch}' is behind base branch '${base}'.`) + await git.exec(['pull', '--rebase', baseRemote, base]) + core.info(`Rebased '${branch}' commits ontop of '${base}'.`) + wasRebased = true; } - // If the working base is not the base, rebase the temp branch commits - // This will also be true if the working base type is a commit - if (workingBase != base) { - core.info( - `Rebasing commits made to ${workingBaseType} '${workingBase}' on to base branch '${base}'` - ) - const fetchArgs = ['--force'] - if (branchRemoteName != 'fork') { - // If pushing to a fork we cannot shallow fetch otherwise the 'shallow update not allowed' error occurs - fetchArgs.push('--depth=1') - } - // Checkout the actual base - await git.fetch([`${base}:${base}`], baseRemote, fetchArgs) - await git.checkout(base) - // Cherrypick commits from the temporary branch starting from the working base - const commits = await git.revList( - [`${workingBase}..${tempBranch}`, '.'], - ['--reverse'] - ) - for (const commit of splitLines(commits)) { - const result = await git.cherryPick( - ['--strategy=recursive', '--strategy-option=theirs', commit], - true - ) - if (result.exitCode != 0 && !result.stderr.includes(CHERRYPICK_EMPTY)) { - throw new Error(`Unexpected error: ${result.stderr}`) - } - } - // Reset the temp branch to the working index - await git.checkout(tempBranch, 'HEAD') - // Reset the base - await git.fetch([`${base}:${base}`], baseRemote, fetchArgs) - } + hasDiffWithBase = await isAhead(git, base, branch) - // Determine the fetch depth for the pull request branch (best effort) - const tempBranchCommitsAhead = await commitsAhead(git, base, tempBranch) - const fetchDepth = - tempBranchCommitsAhead > 0 - ? tempBranchCommitsAhead + FETCH_DEPTH_MARGIN - : FETCH_DEPTH_MARGIN - - let action = 'none' - let hasDiffWithBase = false - - // Try to fetch the pull request branch - if (!(await tryFetch(git, branchRemoteName, branch, fetchDepth))) { - // The pull request branch does not exist - core.info(`Pull request branch '${branch}' does not exist yet.`) - // Create the pull request branch - await git.checkout(branch, tempBranch) - // Check if the pull request branch is ahead of the base - hasDiffWithBase = await isAhead(git, base, branch) - if (hasDiffWithBase) { - action = 'created' - core.info(`Created branch '${branch}'`) - } else { - core.info( - `Branch '${branch}' is not ahead of base '${base}' and will not be created` - ) - } - } else { - // The pull request branch exists - core.info( - `Pull request branch '${branch}' already exists as remote branch '${branchRemoteName}/${branch}'` - ) - // Checkout the pull request branch - await git.checkout(branch) - - // Reset the branch if one of the following conditions is true. - // - If the branch differs from the recreated temp branch. - // - If the number of commits ahead of the base branch differs between the branch and - // temp branch. This catches a case where the base branch has been force pushed to - // a new commit. - // - If the recreated temp branch is not ahead of the base. This means there will be - // no pull request diff after the branch is reset. This will reset any undeleted - // branches after merging. In particular, it catches a case where the branch was - // squash merged but not deleted. We need to reset to make sure it doesn't appear - // to have a diff with the base due to different commits for the same changes. - // - If the diff of the commits ahead of the base branch differs between the branch and - // temp branch. This catches a case where changes have been partially merged to the - // base. The overall diff is the same, but the branch needs to be rebased to show - // the correct diff. - // - // For changes on base this reset is equivalent to a rebase of the pull request branch. - const branchCommitsAhead = await commitsAhead(git, base, branch) - if ( - (await git.hasDiff([`${branch}..${tempBranch}`])) || - branchCommitsAhead != tempBranchCommitsAhead || - !(tempBranchCommitsAhead > 0) || // !isAhead - (await commitsHaveDiff(git, branch, tempBranch, tempBranchCommitsAhead)) - ) { - core.info(`Resetting '${branch}'`) - // Alternatively, git switch -C branch tempBranch - await git.checkout(branch, tempBranch) - } - - // Check if the pull request branch has been updated - // If the branch was reset or updated it will be ahead - // It may be behind if a reset now results in no diff with the base - if (!(await isEven(git, `${branchRemoteName}/${branch}`, branch))) { - action = 'updated' - core.info(`Updated branch '${branch}'`) - } else { - action = 'not-updated' - core.info( - `Branch '${branch}' is even with its remote and will not be updated` - ) - } - - // Check if the pull request branch is ahead of the base - hasDiffWithBase = await isAhead(git, base, branch) - } + // If the base is not specified it is assumed to be the working base. + base = base ? base : workingBase // Get the base and head SHAs const baseSha = await git.revParse(base) @@ -346,25 +255,16 @@ export async function createOrUpdateBranch( let branchCommits: Commit[] = [] if (hasDiffWithBase) { + action = 'updated' // Build the branch commits branchCommits = await buildBranchCommits(git, base, branch) } - // Delete the temporary branch - await git.exec(['branch', '--delete', '--force', tempBranch]) - - // Checkout the working base to leave the local repository as it was found - await git.checkout(workingBase) - - // Restore any stashed changes - if (stashed) { - await git.stashPop() - } - return { action: action, base: base, hasDiffWithBase: hasDiffWithBase, + wasRebased: wasRebased, baseCommit: baseCommit, headSha: headSha, branchCommits: branchCommits diff --git a/src/create-pull-request.ts b/src/create-pull-request.ts index 9076b7d..27c5679 100644 --- a/src/create-pull-request.ts +++ b/src/create-pull-request.ts @@ -227,11 +227,9 @@ export async function createPullRequest(inputs: Inputs): Promise { await git.stashPop() } } else { - await git.push([ - '--force-with-lease', - branchRemoteName, - `${inputs.branch}:refs/heads/${inputs.branch}` - ]) + await git.push( + result.wasRebased ? [`--force-with-lease`, branchRemoteName, inputs.branch] : [] + ) } core.endGroup() }