본문으로 건너뛰기

셀 단위 병합

열 단위 병합은 양쪽에서 같은 열을 변경했는지 감지합니다. 셀 단위 병합은 더 세밀하게 해당 열 내부의 콘텐츠를 병합합니다. TEXT 필드의 서로 다른 줄을 편집하거나 JSON 객체의 서로 다른 키를 편집한 두 사람의 변경 사항은 충돌 없이 병합할 수 있습니다.

PHP
use Merql\CellMerge\CellMergeConfig;
use Merql\Merge\ThreeWayMerge;

$config = CellMergeConfig::auto();
$merge = new ThreeWayMerge($config);
$result = $merge->merge($base, $ours, $theirs);

셀 단위 병합 활성화

셀 단위 병합은 선택 사항입니다. CellMergeConfig로 구성된 ThreeWayMerge에서만 실행됩니다. 이를 지정하지 않으면 양쪽에서 서로 다른 값으로 변경한 모든 열이 update_update 충돌이 됩니다.

Merql 파사드는 설정을 받지 않습니다. Merql::merge(), Merql::patch(), Merql::plan()은 설정 없이 new ThreeWayMerge()를 생성하며, CLI의 merge 명령은 Merql::merge()을 거칩니다. 항상 열 단위로 병합합니다(#7).

파사드 또는 CLI로 저장한 스냅샷을 셀 단위 병합으로 병합하려면 직접 불러와 ThreeWayMerge을 호출하세요. 결과는 일반 MergeResult이므로 파사드에서 계속 적용할 수 있으며, MergePlanBuilder은 이를 병합 계획으로 변환합니다.

PHP
use Merql\CellMerge\CellMergeConfig;
use Merql\Merge\ThreeWayMerge;
use Merql\Merql;
use Merql\Plan\MergePlanBuilder;
use Merql\Snapshot\SnapshotStore;

$base   = SnapshotStore::load('base');
$ours   = SnapshotStore::load('ours');
$theirs = SnapshotStore::load('theirs');

$result = (new ThreeWayMerge(CellMergeConfig::auto()))->merge($base, $ours, $theirs);

$plan = (new MergePlanBuilder())->build('plan-1', $base, $ours, $theirs, $result);

셀 병합기는 양쪽에서 변경한 열에 대해서만 실행됩니다. 한쪽만 열을 변경한 경우 해당 값을 그대로 사용합니다.

CellMergeConfig

CellMergeConfig은 열 또는 열 유형을 셀 병합기 구현에 매핑합니다. auto() 팩토리는 적절한 기본값을 제공합니다.

PHP
use Merql\CellMerge\CellMergeConfig;
use Merql\CellMerge\JsonCellMerger;
use Merql\CellMerge\TextCellMerger;

// Auto: column types containing "text" (TEXT, MEDIUMTEXT, LONGTEXT, ...) use
// TextCellMerger, column types containing "json" use JsonCellMerger.
$config = CellMergeConfig::auto();

// Custom: assign mergers to specific columns.
$config = (new CellMergeConfig())
    ->forColumn('posts.content', new TextCellMerger())
    ->forColumn('settings', new JsonCellMerger())
    ->forType('json', new JsonCellMerger());

병합기 확인 우선순위:

  1. 정규화된 열 이름(table.column).
  2. 정규화되지 않은 열 이름(column).
  3. 열 유형 패턴 일치(대소문자를 구분하지 않는 str_contains).
  4. 기본값: OpaqueCellMerger(값이 다르면 항상 충돌로 처리).

TextCellMerger

Myers diff를 사용해 여러 줄의 텍스트를 병합합니다. git이 파일 콘텐츠에 사용하는 것과 같은 알고리즘입니다. 각 줄을 하나의 단위로 취급하며, 서로 다른 줄의 변경 사항은 충돌 없이 병합됩니다.

PHP
use Merql\CellMerge\TextCellMerger;

$merger = new TextCellMerger();

예: 충돌 없는 텍스트 병합

코드
Base:    "line one\nline two\nline three"
Ours:    "LINE ONE\nline two\nline three"    // changed line 1
Theirs:  "line one\nline two\nLINE THREE"    // changed line 3

서로 다른 줄을 편집했습니다. 충돌 없이 "LINE ONE\nline two\nLINE THREE"이 생성됩니다.

예: 텍스트 충돌

코드
Base:    "line one\nline two"
Ours:    "changed by us\nline two"     // changed line 1
Theirs:  "changed by them\nline two"   // also changed line 1

양쪽에서 같은 줄을 서로 다른 값으로 변경했습니다. 병합기는 충돌 결과를 반환합니다. 병합된 콘텐츠에는 git과 유사한 충돌 마커가 포함됩니다.

작동 방식

TextCellMerger은 pitmaster의 ThreeWayMerge::merge()에 위임합니다. 이 구현은 base와 각 측의 차이를 Myers diff로 계산한 다음 두 편집 스크립트를 결합합니다. 같은 줄 영역에서 편집 내용이 겹치면 충돌을 보고합니다.

JsonCellMerger

JSON 객체를 최상위 키별로 비교해 병합합니다. 각 키는 표준 3방향 규칙에 따라 병합됩니다.

PHP
use Merql\CellMerge\JsonCellMerger;

$merger = new JsonCellMerger();

예: 충돌 없는 JSON 병합

JSON
// Base
{"name": "Alice", "role": "editor", "active": true}

// Ours: changed "role"
{"name": "Alice", "role": "admin", "active": true}

// Theirs: changed "name"
{"name": "Bob", "role": "editor", "active": true}

서로 다른 키를 변경했습니다. 병합 결과는 다음과 같습니다.

JSON
{"name": "Bob", "role": "admin", "active": true}

예: JSON 충돌

JSON
// Base
{"color": "red"}

// Ours
{"color": "blue"}

// Theirs
{"color": "green"}

양쪽에서 color 키를 서로 다른 값으로 변경했습니다. 충돌입니다. 기본 병합기는 ours 값을 사용하고 충돌 하나를 보고합니다.

키 단위 규칙

기준 ours theirs 결과
키 변경 없음 키 변경 없음 기준값 적용
키 변경 없음 키 변경 theirs 적용
키 변경 키 변경 없음 ours 적용
키 변경(동일) 키 변경(동일) 적용(일치)
키 변경(다름) 키 변경(다름) 충돌(기본값: ours)
키 있음 키 있음 키 제거됨 제거 적용
키 있음 키 제거됨 키 있음 제거 적용
키 없음 키 추가됨 키 없음 추가 적용
키 없음 키 없음 키 추가됨 추가 적용

중첩 객체는 재귀적으로 비교하지 않고 불투명한 JSON 문자열로 비교합니다. 중첩 객체가 다르면 하위 키가 아니라 해당 키의 전체 값을 비교합니다.

요구 사항

세 값 모두 유효한 JSON 객체 또는 배열이어야 합니다. 값 중 하나라도 유효한 JSON이 아니거나 JSON 스칼라(문자열, 숫자, 불리언)이면 병합기는 충돌 처리로 대체합니다.

OpaqueCellMerger

셀 단위 전략을 설정하지 않았을 때 사용하는 기본 병합기입니다. 열 값을 불투명한 블롭으로 취급하며 양쪽 값이 다르면 항상 충돌로 처리합니다.

PHP
use Merql\CellMerge\OpaqueCellMerger;

$merger = new OpaqueCellMerger();
$result = $merger->merge($base, $ours, $theirs);
// $result->clean is always false when $ours !== $theirs

CellMergeResult

모든 셀 병합기는 CellMergeResult을 반환합니다.

PHP
$result = $merger->merge($base, $ours, $theirs);

$result->clean;     // bool: true if merge resolved cleanly
$result->value;     // mixed: the merged value (or ours on conflict)
$result->conflicts; // int: number of conflicts within the cell

팩토리 메서드:

PHP
CellMergeResult::resolved($value);           // clean merge
CellMergeResult::conflict($oursValue, $n);   // n conflicts, defaults to ours

사용자 지정 셀 병합기

자체 병합 전략을 추가하려면 CellMerger 인터페이스를 구현하세요.

PHP
use Merql\CellMerge\CellMerger;
use Merql\CellMerge\CellMergeResult;

class YamlCellMerger implements CellMerger
{
    public function merge(mixed $base, mixed $ours, mixed $theirs): CellMergeResult
    {
        // Parse YAML, merge keys, return result.
        $merged = $this->mergeYaml($base, $ours, $theirs);

        if ($merged['clean']) {
            return CellMergeResult::resolved($merged['value']);
        }

        return CellMergeResult::conflict($ours);
    }
}

$config = (new CellMergeConfig())
    ->forColumn('config_data', new YamlCellMerger());