Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

reconcile: harden replayLocalOntoRemote against merge commits + empty commits

オープン
#88 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
68/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
静か
技術スタック
git, typescript
領域
api, backend, testing

調査の方向性

apps/api/src/store/reconcile.ts の replayLocalOntoRemote から始め、次に apps/api/tests/data-repo-reconcile.test.ts にある既存の調整ケースを読みます。提案された結果を使って、ローカルのマージコミットと空のコミットのカバレッジを追加し、そのテストファイルを実行します。マージコミットがハッシュとともに競合ログに残り、空のコミットが書き換え後の履歴に存在しなければ完了です。

索引モデルが issue の本文から書いたものです。

説明

Background

PR #86's replayLocalOntoRemote (in apps/api/src/store/reconcile.ts) replaces git rebase with a per-commit merge-tree --write-tree + commit-tree loop. The plan's Risks section flags two edge cases that the implementation accepts silently rather than guarding against:

1. Merge commits get silently flattened
'commit-tree', mergedTreeHash, '-p', newTip, '-m', message

Single -p parent. If a multi-parent commit ever appears in localCommits, the replay collapses it into a single-parent commit. git rebase by default refuses to replay merges (errors out); --rebase-merges preserves them.

Risk: the data repo today is all programmatic single-parent gitsheets transacts, so this never fires. But if a human or a future automation ever lands a merge commit on the data repo, reconcile would silently drop one of the parents and rewrite history in a misleading way.

2. Empty commits are preserved instead of dropped

git commit-tree <existing-tree> happily writes a commit even when the resulting tree equals the parent's tree (no diff). Modern git rebase drops empty commits by default (--no-empty). The replay loop currently writes them through.

Risk: an empty commit (programmatically possible if a gitsheets transact does nothing but still commits) gets preserved across reconcile, making history noisier than git rebase would produce.

Proposed hardening

In replayLocalOntoRemote:

  1. Reject merge commits early. Before the loop, check each commit's parent count via git rev-list --parents or git cat-file -p <commit> | head -1. If any commit has >1 parent, throw RebaseReplayConflictError (routes to the escape-hatch) with a clear message. Operator can then investigate the unexpected merge commit on conflicts/<UTC>.
  2. Skip empty commits. Before calling commit-tree, compare mergedTreeHash to the new tip's tree (git show <newTip> --format=%T -s). If equal, skip the commit and continue with newTip unchanged.

Both checks are cheap. The merge-commit guard is the more important one (data loss avoidance); the empty-commit skip is purely a history-cleanliness improvement.

Tests to add

In apps/api/tests/data-repo-reconcile.test.ts:

  • New case: diverged-with-merge-commit-on-local → expect outcome: 'conflict-escaped' with the merge commit's hash mentioned in the conflict log.
  • New case: diverged-with-empty-commit-on-local → expect outcome: 'rebased', empty commit dropped from the rewritten history.

Why post-cutover

Neither edge case is reachable from current code paths (no merge commits, no empty-commit-producing transacts). Filing as hardening rather than a bug fix.

Filed as follow-up from PR #86.

主要言語
TypeScript
スター
1
フォーク
1
平均マージ
1日 20時間
マージ済み PR(30日)
25

コントリビューションガイド

このリポジトリのコントリビューションガイドは索引されていません

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

CodeForPhilly/codeforphilly-ng のほかの issue

CodeForPhilly/codeforphilly-ng の issue をすべて見る

似ている issue

TypeScript の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。