Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions docs/merging.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ forkpress branch tree --format json

In wp-admin, use the ForkPress branch manager page or the branch switcher and
choose **Show merge history** to load the same source-to-target run list. Runs
with conflicts can jump directly into the conflict review queue from the
switcher.
with conflicts can jump directly into the conflict review queue.

Show recent runs, decisions, conflicts, and resolutions:

Expand Down
6 changes: 6 additions & 0 deletions tests/cow/branch_ui.php
Original file line number Diff line number Diff line change
Expand Up @@ -1198,6 +1198,12 @@ function decode_branch_ui_payload(array $result): array {
assert_true(str_contains($admin_page_html, 'nonce-forkpress_branch_history'), 'branch manager admin page renders merge history nonce');
assert_true(str_contains($admin_page_html, 'forkpress-branch-history-list'), 'branch manager admin page renders merge history list target');
assert_true(str_contains($admin_page_html, "source + ' -> ' + target"), 'branch manager admin page renders source-to-target history rows');
assert_true(str_contains($admin_page_html, 'forkpress_branch_conflicts'), 'branch manager admin page renders conflict audit action');
assert_true(str_contains($admin_page_html, 'nonce-forkpress_branch_conflicts'), 'branch manager admin page renders conflict audit nonce');
assert_true(str_contains($admin_page_html, 'forkpress-branch-review-conflicts'), 'branch manager admin page renders conflict drilldown buttons');
assert_true(str_contains($admin_page_html, 'function fetchConflicts'), 'branch manager admin page renders conflict drilldown fetch handler');
assert_true(str_contains($admin_page_html, 'function renderConflicts'), 'branch manager admin page renders conflict drilldown display handler');
assert_true(str_contains($admin_page_html, 'forkpress-branch-conflict-list'), 'branch manager admin page renders conflict list target');
assert_same($admin_page_menus[0]['menu_slug'] ?? null, 'forkpress-branches', 'branch manager registers a wp-admin menu page');

$forbidden = run_branch_ui_action(
Expand Down
78 changes: 78 additions & 0 deletions wp-plugin/forkpress-wp.php
Original file line number Diff line number Diff line change
Expand Up @@ -1821,6 +1821,71 @@ function rowText(run) {
run && run.finished_at ? 'finished: ' + String(run.finished_at) : ''
].filter(Boolean).join(' / ');
}
function conflictTitle(record) {
if (record && record.conflict_key) {
return String(record.conflict_key);
}
if (record && record.path) {
return String(record.path);
}
if (record && record.table_name) {
return String(record.table_name) + (record.column_name ? '.' + String(record.column_name) : '');
}
return 'Conflict #' + String(record && record.id ? record.id : '');
}
function renderConflicts(payload) {
var records = Array.isArray(payload.records) ? payload.records : [];
results.innerHTML = '';
var heading = document.createElement('p');
heading.textContent = payload.message || ('Loaded ' + String(records.length) + ' conflict records.');
results.appendChild(heading);
var list = document.createElement('ul');
list.className = 'forkpress-branch-conflict-list';
records.slice(0, 10).forEach(function (record) {
var item = document.createElement('li');
item.textContent = [
conflictTitle(record),
record && record.conflict_type ? 'type: ' + String(record.conflict_type) : '',
record && record.lifecycle_state ? 'state: ' + String(record.lifecycle_state) : '',
record && record.next_action ? 'next: ' + String(record.next_action) : ''
].filter(Boolean).join(' / ');
list.appendChild(item);
});
if (records.length) {
results.appendChild(list);
}
}
function fetchConflicts(runId) {
var body = new FormData();
body.append('action', 'forkpress_branch_conflicts');
body.append('_wpnonce', '<?php echo esc_js(function_exists('wp_create_nonce') ? wp_create_nonce('forkpress_branch_conflicts') : ''); ?>');
body.append('run', String(runId));
results.textContent = 'Loading merge conflicts...';
fetch('<?php echo esc_js($action_url); ?>', {
method: 'POST',
body: body,
credentials: 'same-origin',
headers: {
'Accept': 'application/json',
'X-ForkPress-Async': '1'
}
}).then(function (response) {
return response.text().then(function (text) {
var payload = null;
try {
payload = text ? JSON.parse(text) : null;
} catch (error) {
payload = null;
}
if (!response.ok || !payload || payload.success === false) {
throw new Error(payload && payload.message ? payload.message : (text || 'ForkPress conflict audit failed.'));
}
return payload;
});
}).then(renderConflicts).catch(function (error) {
results.textContent = error && error.message ? error.message : 'ForkPress conflict audit failed.';
});
}
function renderHistory(payload) {
var records = Array.isArray(payload.records) ? payload.records : [];
results.innerHTML = '';
Expand All @@ -1829,6 +1894,19 @@ function renderHistory(payload) {
records.slice(0, 10).forEach(function (run) {
var item = document.createElement('li');
item.textContent = rowText(run);
if (run && run.id && Number(run.conflict_count || 0) > 0) {
var review = document.createElement('button');
review.className = 'button button-small forkpress-branch-review-conflicts';
review.type = 'button';
review.textContent = 'Review conflicts';
review.addEventListener('click', function (runId) {
return function () {
fetchConflicts(runId);
};
}(run.id));
item.appendChild(document.createTextNode(' '));
item.appendChild(review);
}
list.appendChild(item);
});
if (!records.length) {
Expand Down
Loading