forgedock 1.18.0 → 1.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/bin/engine/outcome-codes.mjs +13 -0
- package/bin/engine/phases.mjs +54 -4
- package/bin/engine/runlog.mjs +10 -2
- package/bin/engine/telemetry-seam.mjs +3 -0
- package/bin/engine/telemetry.test.mjs +3 -2
- package/bin/engine-cli.mjs +32 -2
- package/bin/engine.mjs +187 -10
- package/bin/runner.mjs +89 -13
- package/bin/stats/aggregate.mjs +7 -3
- package/bin/stats/cache.mjs +7 -3
- package/bin/stats/github-source.mjs +136 -28
- package/bin/stats/index.mjs +46 -9
- package/bin/stats/render.mjs +1 -1
- package/bin/stats/stats.test.mjs +201 -40
- package/commands/orchestrate/phase-4-execution.md +1 -1
- package/commands/review-pr-agents/protocols.md +19 -0
- package/commands/review-pr-staging.md +4 -4
- package/commands/review-pr.md +31 -20
- package/commands/work-on/build/architect.md +54 -1
- package/commands/work-on/build/implement.md +28 -1
- package/commands/work-on/build/validate.md +321 -29
- package/commands/work-on/build.md +184 -17
- package/commands/work-on/close.md +9 -4
- package/commands/work-on/investigate.md +7 -0
- package/commands/work-on/remediate.md +43 -17
- package/commands/work-on/review.md +43 -22
- package/commands/work-on.md +4 -4
- package/docs/CONFIG.md +93 -2
- package/docs/OUTCOME-CODES.md +5 -1
- package/docs/RELEASE-NOTES.md +15 -0
- package/docs/STATS.md +11 -4
- package/docs/TELEMETRY.md +5 -2
- package/docs/WORK-ON-RUNTIME.md +29 -3
- package/package.json +2 -2
- package/packages/protocol/src/types.js +1 -1
- package/scripts/acceptance-red-green.sh +90 -0
- package/scripts/acceptance-red-green.test.sh +74 -0
- package/scripts/architect-resume-match.test.sh +12 -3
- package/scripts/build-knowledge-index.mjs +8 -5
- package/scripts/builder-resume-match.test.sh +12 -3
- package/scripts/calibration.mjs +5 -2
- package/scripts/cap-findings.test.sh +2 -0
- package/scripts/check-contract-scope.test.sh +2 -0
- package/scripts/check-edge-cases.sh +143 -0
- package/scripts/check-edge-cases.test.sh +175 -0
- package/scripts/check-spec-markers.sh +4 -0
- package/scripts/check-test-tmp-cleanup.sh +56 -0
- package/scripts/check-test-tmp-cleanup.test.sh +69 -0
- package/scripts/ci-local/adapters/azure-pipelines.mjs +113 -0
- package/scripts/ci-local/adapters/bitbucket.mjs +64 -0
- package/scripts/ci-local/adapters/buildkite.mjs +58 -0
- package/scripts/ci-local/adapters/circleci.mjs +101 -0
- package/scripts/ci-local/adapters/common.mjs +115 -0
- package/scripts/ci-local/adapters/explicit.mjs +21 -0
- package/scripts/ci-local/adapters/github-actions.mjs +349 -0
- package/scripts/ci-local/adapters/gitlab-ci.mjs +184 -0
- package/scripts/ci-local/adapters/portability.mjs +104 -0
- package/scripts/ci-local/adapters/task-runners.mjs +67 -0
- package/scripts/ci-local/cli.mjs +50 -0
- package/scripts/ci-local/lib.mjs +379 -0
- package/scripts/ci-local/plan.mjs +186 -0
- package/scripts/ci-local/run.mjs +451 -0
- package/scripts/ci-local/scoping.mjs +531 -0
- package/scripts/ci-local.sh +98 -0
- package/scripts/ci-local.test.sh +1108 -0
- package/scripts/classify-finding.sh +33 -7
- package/scripts/classify-finding.test.sh +23 -0
- package/scripts/classify-test-run.sh +69 -0
- package/scripts/classify-test-run.test.sh +90 -0
- package/scripts/close-parent-c3.test.sh +2 -0
- package/scripts/consumer-sweep.sh +569 -0
- package/scripts/consumer-sweep.test.sh +357 -0
- package/scripts/detect-append-registries.sh +106 -0
- package/scripts/detect-append-registries.test.sh +64 -0
- package/scripts/file-deferred-followups.sh +91 -30
- package/scripts/file-deferred-followups.test.sh +101 -8
- package/scripts/fixtures/ci-local/azure/azure-pipelines.yml +11 -0
- package/scripts/fixtures/ci-local/bitbucket/bitbucket-pipelines.yml +12 -0
- package/scripts/fixtures/ci-local/buildkite/.buildkite/pipeline.yml +10 -0
- package/scripts/fixtures/ci-local/circleci/.circleci/config.yml +29 -0
- package/scripts/fixtures/ci-local/gha-basic/.github/workflows/ci.yml +59 -0
- package/scripts/fixtures/ci-local/gha-basic/.github/workflows/docs.yml +10 -0
- package/scripts/fixtures/ci-local/gha-basic/.github/workflows/nightly.yml +10 -0
- package/scripts/fixtures/ci-local/gha-basic/docs/index.md +1 -0
- package/scripts/fixtures/ci-local/gha-basic/src/greet.sh +2 -0
- package/scripts/fixtures/ci-local/gha-basic/tests/greet.test.sh +3 -0
- package/scripts/fixtures/ci-local/gha-dorny/.github/workflows/ci.yml +51 -0
- package/scripts/fixtures/ci-local/gha-dorny/backend/app.py +1 -0
- package/scripts/fixtures/ci-local/gha-dorny/docs/a.md +1 -0
- package/scripts/fixtures/ci-local/gha-dorny/frontend/app.js +1 -0
- package/scripts/fixtures/ci-local/gha-run/.github/workflows/ci.yml +39 -0
- package/scripts/fixtures/ci-local/gha-run/src/x.txt +1 -0
- package/scripts/fixtures/ci-local/gitlab/.gitlab-ci.yml +43 -0
- package/scripts/fixtures/ci-local/gitlab/docs/d.md +1 -0
- package/scripts/fixtures/ci-local/gitlab/src/a.txt +1 -0
- package/scripts/fixtures/ci-local/monorepo/.github/workflows/ci.yml +13 -0
- package/scripts/fixtures/ci-local/monorepo/package-lock.json +1 -0
- package/scripts/fixtures/ci-local/monorepo/packages/a/src/a.sh +2 -0
- package/scripts/fixtures/ci-local/monorepo/packages/a/tests/a.test.sh +3 -0
- package/scripts/fixtures/ci-local/monorepo/packages/b/src/b.sh +2 -0
- package/scripts/fixtures/ci-local/monorepo/packages/b/tests/b.test.sh +3 -0
- package/scripts/fixtures/ci-local/no-ci/Makefile +6 -0
- package/scripts/fixtures/ci-local/no-ci/package.json +1 -0
- package/scripts/fixtures/ci-local/no-ci/src/a.js +1 -0
- package/scripts/fixtures/ci-local/no-ci/src/b.js +1 -0
- package/scripts/fixtures/ci-local/no-ci/tests/a.test.js +2 -0
- package/scripts/fixtures/ci-local/no-ci/tests/b.test.js +2 -0
- package/scripts/forge-root.test.sh +1 -1
- package/scripts/lib/telemetry.sh +3 -0
- package/scripts/remediate-approved-head.test.sh +2 -0
- package/scripts/remediate-basesync.test.sh +1 -1
- package/scripts/review-acceptance-gate.sh +2 -2
- package/scripts/review-acceptance-gate.test.sh +20 -0
- package/scripts/review-delta.sh +8 -1
- package/scripts/review-delta.test.sh +19 -0
- package/scripts/review-finalize.sh +294 -53
- package/scripts/review-finalize.test.sh +347 -9
- package/scripts/review-pr-delta-wiring.test.sh +13 -0
- package/scripts/review-prepare.sh +5 -3
- package/scripts/review-prepare.test.sh +18 -2
- package/scripts/review-roster.sh +18 -5
- package/scripts/review-roster.test.sh +57 -1
- package/scripts/run-shell-tests.sh +5 -0
- package/scripts/selfreview-plan.sh +86 -0
- package/scripts/selfreview-plan.test.sh +90 -0
- package/scripts/spawn-depth-check.sh +5 -4
- package/scripts/spawn-depth-check.test.sh +3 -0
- package/scripts/telemetry.test.sh +10 -0
- package/scripts/transition-label.sh +26 -0
- package/scripts/trusted-comments.test.sh +2 -1
- package/scripts/verify-phase-trail.test.sh +14 -0
- package/scripts/wait-ci-green.sh +61 -9
- package/scripts/wait-ci-green.test.sh +41 -3
- package/scripts/with-run-tmpdir.sh +20 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
d
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
a
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
pull_request:
|
|
4
|
+
jobs:
|
|
5
|
+
test-a:
|
|
6
|
+
runs-on: ubuntu-latest
|
|
7
|
+
steps:
|
|
8
|
+
- run: bash packages/a/tests/a.test.sh
|
|
9
|
+
- run: bash packages/b/tests/b.test.sh
|
|
10
|
+
lint:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- run: shellcheck -S error packages/a/src/a.sh packages/b/src/b.sh
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"lockfileVersion":1}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "name": "noci", "version": "1.0.0", "scripts": { "test": "node --test tests/", "lint": "echo lint-ok" } }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = 1
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = 2
|
|
@@ -464,7 +464,7 @@ for f in $STUB_SITES; do
|
|
|
464
464
|
expect "$f: forge_root calls without a preceding locator in the same fence" 0 "$(awk '/^[ ]*```/{loc=0} /^[ ]*type forge_resolve >\/dev\/null/{loc=1} /FORGE_ROOT="\$\(forge_root /&&!loc{bad++} END{print bad+0}' "$ROOT/$f")"
|
|
465
465
|
done
|
|
466
466
|
# the spec-count of stub resolutions pins the conversion (a copy dropped or re-inlined changes it)
|
|
467
|
-
for pair in work-on.md:3 work-on/review.md:2 work-on/build/validate.md:
|
|
467
|
+
for pair in work-on.md:3 work-on/review.md:2 work-on/build/validate.md:5 work-on/investigate.md:1 work-on/decompose.md:1 work-on/close.md:1 work-on/build.md:1 quality-gate.md:4 orchestrate/phase-1-resolve.md:2 orchestrate/phase-4-execution.md:8; do
|
|
468
468
|
f="commands/${pair%%:*}"; n="${pair##*:}"
|
|
469
469
|
expect "$f: FORGE_ROOT stub resolutions" "$n" "$(grep -cF "$FR_LINE" "$ROOT/$f" || true)"
|
|
470
470
|
done
|
package/scripts/lib/telemetry.sh
CHANGED
|
@@ -51,6 +51,9 @@ _forge_tel_record() { # event k=v... -> one JSON line on stdout
|
|
|
51
51
|
run: ($run | str), phase: ($phase | str), event: $ev, eid: $eid} + $p' --args "${args[@]}"
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
# build_selfreview (forge#3971: tier, agents, findings, fixed, declined, skipped=<reason>; ids and counts only) has no review-side
|
|
55
|
+
# routing: it defaults to the issue target below, like diff_size.
|
|
56
|
+
# finding_disposition payload is generic key=value (incl. scope=introduced|pre-existing|unknown); no per-field allowlist applies.
|
|
54
57
|
# Prints "<number> <repo>" of the comment target (issue or PR) for this event, or nothing when none is usable.
|
|
55
58
|
_forge_tel_target() {
|
|
56
59
|
local ev="$1" repo="$2" n
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
6
6
|
|
|
7
7
|
set -uo pipefail
|
|
8
|
+
# Every mktemp below lands in one private dir that the EXIT trap removes (forge#4016).
|
|
9
|
+
_SUITE_TMP=$(mktemp -d); export TMPDIR="$_SUITE_TMP"; trap 'rm -rf "$_SUITE_TMP"' EXIT
|
|
8
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
9
11
|
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
10
12
|
SPEC="$ROOT/commands/work-on/remediate.md"
|
|
@@ -66,7 +66,7 @@ eq "MERGE_RC/MERGE_OUT read only in the fence that sets them" "$FENCE_USE" "$FEN
|
|
|
66
66
|
has "refused park posts needs-human from the sync fence" 'echo "BLOCKED: base-sync: merge refused'
|
|
67
67
|
|
|
68
68
|
# Behavioural: dirty tree + base change -> refused (rc!=0, no MERGE_HEAD, empty U-list); real conflict -> MERGE_HEAD present.
|
|
69
|
-
T="$(mktemp -d)"; trap 'rm -rf "$T"' EXIT
|
|
69
|
+
T="$(mktemp -d)"; trap 'rm -rf "$T" "$CAPDIR"' EXIT
|
|
70
70
|
(
|
|
71
71
|
cd "$T" && git init -q -b main . && git config user.email t@t && git config user.name t
|
|
72
72
|
echo a > f && git add f && git commit -qm base
|
|
@@ -79,12 +79,12 @@ while IFS= read -r check_line; do
|
|
|
79
79
|
ERR=$(eval "$TARGET" 2>&1 >/dev/null); RC=$?
|
|
80
80
|
# grep exit >= 2 is a usage error, not a verdict; only a positively identified usage error (classify-grep-exit.sh) is malformed; a missing operand stays a real FAIL even with stderr suppressed.
|
|
81
81
|
[ "$RC" -ge 2 ] && MALFORMED=$(bash "$GREP_CLS" "$RC" "$ERR" "$TARGET" 2>/dev/null)
|
|
82
|
-
[ "$RC" -eq 127 ] && [ -z "$MALFORMED" ] && printf '%s' "$ERR" | grep -
|
|
82
|
+
[ "$RC" -eq 127 ] && [ -z "$MALFORMED" ] && { FIRST=${TARGET#"${TARGET%%[![:space:]]*}"}; FIRST=${FIRST%%[[:space:]]*}; [ -n "$FIRST" ] && [ "${FIRST#*/}" = "$FIRST" ] && ! command -v -- "$FIRST" >/dev/null 2>&1 && printf '%s\n' "$ERR" | grep -qE "(^|: )$(printf '%s' "$FIRST" | sed 's/[][\.*^$(){}?+|/]/\\&/g'):"; } && MALFORMED="command not found (rc 127): the target is not an executable command"
|
|
83
83
|
[ "$RC" -eq 0 ] || [ -n "$MALFORMED" ] || { RESULT="FAIL"; DETAIL="command exited non-zero: $TARGET"; }
|
|
84
84
|
else
|
|
85
85
|
ERRF=$(mktemp); OUTPUT=$(eval "$TARGET" 2>"$ERRF"); RC=$?; ERR=$(cat "$ERRF"); rm -f "$ERRF"; [ -z "$ERR" ] || OUTPUT="${OUTPUT}${OUTPUT:+$'\n'}${ERR}" # classify on stderr only, match on both
|
|
86
86
|
[ "$RC" -ge 2 ] && MALFORMED=$(bash "$GREP_CLS" "$RC" "$ERR" "$TARGET" 2>/dev/null)
|
|
87
|
-
[ "$RC" -eq 127 ] && [ -z "$MALFORMED" ] && printf '%s' "$ERR" | grep -
|
|
87
|
+
[ "$RC" -eq 127 ] && [ -z "$MALFORMED" ] && { FIRST=${TARGET#"${TARGET%%[![:space:]]*}"}; FIRST=${FIRST%%[[:space:]]*}; [ -n "$FIRST" ] && [ "${FIRST#*/}" = "$FIRST" ] && ! command -v -- "$FIRST" >/dev/null 2>&1 && printf '%s\n' "$ERR" | grep -qE "(^|: )$(printf '%s' "$FIRST" | sed 's/[][\.*^$(){}?+|/]/\\&/g'):"; } && MALFORMED="command not found (rc 127): the target is not an executable command"
|
|
88
88
|
if [ -n "$MALFORMED" ]; then :
|
|
89
89
|
else
|
|
90
90
|
printf '%s\n' "$OUTPUT" | grep -qE "$MATCHER" || printf '%s\n' "$OUTPUT" | grep -qF -- "$MATCHER" \
|
|
@@ -90,6 +90,23 @@ inv "$(chk ac-1 true)" "$(chk ac-7 "echo 'unbalanced")"; rc=$(run); eq "mixed pa
|
|
|
90
90
|
grep -q ':PASSED:' "$TMP/out" && grep -qi 'malformed' "$TMP/out" && ok "mixed: PASSED with malformed listed" || bad "mixed: wrong output"
|
|
91
91
|
inv "$(chk ac-1 false)" "$(chk ac-7 "echo 'unbalanced")"; rc=$(run); eq "mixed fail+malformed: fails closed" "$rc" 1
|
|
92
92
|
|
|
93
|
+
# rc 127 downgrade only when the target's own first word is the missing command (forge#3842).
|
|
94
|
+
printf '#!/bin/bash\nno-such-tool-xyzzy\n' > "$TMP/wt/inner.sh"
|
|
95
|
+
inv "$(chk ac-8 'no-such-cmd-xyzzy --help')"; rc=$(run); eq "first word missing: malformed, rc 0" "$rc" 0
|
|
96
|
+
grep -qi 'malformed' "$TMP/out" && ok "first word missing reported malformed" || bad "first word missing not reported"
|
|
97
|
+
inv "$(chk ac-8 'bash inner.sh')"; rc=$(run); eq "script with missing inner tool is a real fail" "$rc" 1
|
|
98
|
+
inv "$(chk ac-8 'true && no-such-cmd-xyzzy')"; rc=$(run); eq "missing command not first word is a real fail" "$rc" 1
|
|
99
|
+
export LANG=de_DE.UTF-8 LC_MESSAGES=de_DE.UTF-8
|
|
100
|
+
inv "$(chk ac-8 'no-such-cmd-xyzzy --help')"; rc=$(run); eq "non-English locale: first word missing still malformed" "$rc" 0
|
|
101
|
+
grep -qi 'malformed' "$TMP/out" && ok "locale: reported malformed" || bad "locale: not reported malformed"
|
|
102
|
+
unset LANG LC_MESSAGES
|
|
103
|
+
# target runs in its native locale: a locale-dependent verdict (wc -m on multibyte) must not be forced to C
|
|
104
|
+
export LC_ALL=C.UTF-8
|
|
105
|
+
inv "$(chk ac-8 'printf "\303\251" | wc -m | grep -qx 1')"; rc=$(run); eq "target keeps native locale (wc -m multibyte)" "$rc" 0
|
|
106
|
+
unset LC_ALL
|
|
107
|
+
inv 'ACCEPTANCE_CHECK: id=ac-9 type=command target="no-such-cmd-xyzzy" matcher="QQQ" phase=review description=d'; rc=$(run); eq "matcher branch: first word missing malformed" "$rc" 0
|
|
108
|
+
inv 'ACCEPTANCE_CHECK: id=ac-9 type=command target="bash inner.sh" matcher="QQQ" phase=review description=d'; rc=$(run); eq "matcher branch: inner missing tool is a real fail" "$rc" 1
|
|
109
|
+
|
|
93
110
|
# Selection parity with B6.5 (forge#3809): non-review checks are selected by the `gh pr` target only, never by a phase= text match.
|
|
94
111
|
bt() { printf 'ACCEPTANCE_CHECK: id=%s type=command target="%s" matcher="exit_0" phase=%s description=%s' "$1" "$2" "$3" "$4"; }
|
|
95
112
|
inv "$(bt ac-1 'gh pr view 1 && false' build d)"; rc=$(run); eq "phase=build gh pr check is evaluated (fails)" "$rc" 1
|
|
@@ -99,5 +116,8 @@ inv "$(bt ac-1 'false' build d)"; rc=$(run); eq "phase=build non-gh-pr check is
|
|
|
99
116
|
inv "$(bt ac-1 'gh pr view 1 && false' build 'x phase=review y')"; rc=$(run); eq "phase=review in description does not change selection" "$rc" 1
|
|
100
117
|
inv "$(bt ac-1 'false' build 'x phase=review y')"; rc=$(run); eq "phase=review in description does not select a non-gh-pr check" "$rc" 0
|
|
101
118
|
inv 'ACCEPTANCE_CHECK: id=ac-1 type=command target="gh pr view 1 && false" matcher="exit_0" description=untagged'; rc=$(run); eq "untagged gh pr check is evaluated" "$rc" 1
|
|
119
|
+
# test= tolerance (forge#3630): a test="..." token between phase= and description= does not change phase handling or selection.
|
|
120
|
+
inv 'ACCEPTANCE_CHECK: id=ac-1 type=command target="gh pr view 1 && false" matcher="exit_0" phase=review test="node --test x.mjs" description=d'; rc=$(run); eq "test= token on a phase=review check is tolerated (evaluated, fails)" "$rc" 1
|
|
121
|
+
inv 'ACCEPTANCE_CHECK: id=ac-1 type=command target="false" matcher="exit_0" phase=build test="node --test x.mjs" description=d'; rc=$(run); eq "test= token on a phase=build non-gh-pr check is not selected" "$rc" 0
|
|
102
122
|
|
|
103
123
|
echo "$PASS passed, $FAILN failed"; [ "$FAILN" -eq 0 ]
|
package/scripts/review-delta.sh
CHANGED
|
@@ -172,7 +172,14 @@ while IFS= read -r c; do
|
|
|
172
172
|
if [ "$rc" -ne 0 ]; then SYNC=0; break; fi # second parent is not base history
|
|
173
173
|
CC=$(G diff-tree --cc --no-commit-id -r "$c" 2>/dev/null); rc=$?
|
|
174
174
|
[ "$rc" -eq 0 ] || full
|
|
175
|
-
if [ -n "$CC" ]; then
|
|
175
|
+
if [ -n "$CC" ]; then
|
|
176
|
+
# Non-empty --cc is either hand-written resolution content (not mechanical) or a merge=union result (forge#3969).
|
|
177
|
+
# Accept only when re-running the merge mechanically reproduces the committed tree exactly: a clean
|
|
178
|
+
# `merge-tree` (rc 0) whose tree equals the merge commit's. A hand-resolved or hand-edited merge never matches.
|
|
179
|
+
MT=$(G merge-tree --write-tree "$2" "$3" 2>/dev/null); rc=$?
|
|
180
|
+
MT=$(printf '%s\n' "$MT" | sed -n 1p)
|
|
181
|
+
if [ "$rc" -ne 0 ] || [ -z "$MT" ] || [ "$MT" != "$(G rev-parse "${c}^{tree}" 2>/dev/null)" ]; then SYNC=0; break; fi
|
|
182
|
+
fi
|
|
176
183
|
done <<< "$CHAIN"
|
|
177
184
|
|
|
178
185
|
# per-path shape of a diff: modes, status and path for every file, plus both blob ids for binary files
|
|
@@ -83,6 +83,25 @@ commit shared.txt "feat side" "feat edits shared"; R=$(sha HEAD); panel "$R"
|
|
|
83
83
|
g merge -q --no-ff origin/main >/dev/null 2>&1; printf 'resolved\n' > "$W/shared.txt"; g add -A >/dev/null; g commit -q -m "merge main (resolved)" >/dev/null; HEAD=$(sha HEAD)
|
|
84
84
|
out=$(run_rd | sed -n 1p); case "$out" in BASE_SYNC_ONLY*) bad "conflict-resolution merge must not be BASE_SYNC_ONLY (got $out)" ;; *) ok ;; esac
|
|
85
85
|
|
|
86
|
+
# 5b. union-merged base merge (forge#3969): both sides append a different line to a union-attributed registry.
|
|
87
|
+
# merge=union makes the same-spot append conflict-free, so the sync merge has no resolution edit and stays BASE_SYNC_ONLY.
|
|
88
|
+
mkrepo; g checkout -q main; printf 'e1\ne2\n' > "$W/list.txt"; g add -A >/dev/null; g commit -q -m "registry" >/dev/null; g push -q origin main
|
|
89
|
+
g checkout -q feat; g merge -q --no-ff -m "sync" main >/dev/null 2>&1
|
|
90
|
+
printf 'list.txt merge=union\n' > "$W/.git/info/attributes"
|
|
91
|
+
printf 'e-pr\n' >> "$W/list.txt"; g add -A >/dev/null; g commit -q -m "pr appends" >/dev/null; R=$(sha HEAD); panel "$R"
|
|
92
|
+
g checkout -q main; printf 'e-base\n' >> "$W/list.txt"; g add -A >/dev/null; g commit -q -m "base appends" >/dev/null; g push -q origin main; g checkout -q feat; g fetch -q origin
|
|
93
|
+
if g merge -q --no-ff -m "merge main" origin/main >/dev/null 2>&1; then
|
|
94
|
+
HEAD=$(sha HEAD)
|
|
95
|
+
grep -qxF e-pr "$W/list.txt" && grep -qxF e-base "$W/list.txt" && ok || bad "union-merged base merge lost a line"
|
|
96
|
+
expect "union-merged base merge" "BASE_SYNC_ONLY $R" 1
|
|
97
|
+
else
|
|
98
|
+
bad "union-merged base merge should be conflict-free"
|
|
99
|
+
fi
|
|
100
|
+
# same union merge, then hand-edited before the merge commit: the mechanical re-merge no longer matches -> not BASE_SYNC_ONLY
|
|
101
|
+
g reset -q --hard "$R"; g merge -q --no-ff --no-commit origin/main >/dev/null 2>&1; printf 'e-hand\n' >> "$W/list.txt"; g add -A >/dev/null; g commit -q -m "merge main (edited)" >/dev/null; HEAD=$(sha HEAD)
|
|
102
|
+
out=$(run_rd | sed -n 1p); case "$out" in BASE_SYNC_ONLY*) bad "hand-edited union merge must not be BASE_SYNC_ONLY (got $out)" ;; *) ok ;; esac
|
|
103
|
+
rm -f "$W/.git/info/attributes"
|
|
104
|
+
|
|
86
105
|
# 6. mixed: base merge plus a regular commit -> DELTA
|
|
87
106
|
mkrepo; R=$(sha HEAD); panel "$R"; base_advance b.txt "b1"; g merge -q --no-ff -m "merge main" origin/main >/dev/null 2>&1; commit pr.txt "pr2" "pr2"; HEAD=$(sha HEAD)
|
|
88
107
|
expect "base merge plus regular commit" "DELTA $R..$HEAD" 1
|