Zlúčenie
Pitmaster vykonáva zlúčenie vetiev trojcestným zlúčením, podobne ako git merge. Rozpoznáva možnosti rýchleho posunu, vypočítava základne zlúčenia, vykonáva zlúčenia na úrovni stromov aj obsahu a pri konfliktoch v súboroch vytvára štandardné značky konfliktov.
Základné zlúčenie
$result = $repo->merge('feature/login');
if ($result->clean) {
echo "Merged successfully: {$result->commitId->hex}\n";
} else {
echo "Conflicts:\n";
foreach ($result->conflictPaths as $path) {
echo " {$path}\n";
}
}
MergeResult
Trieda MergeResult je objekt hodnoty iba na čítanie:
use Pitmaster\Merge\MergeResult;
$result->clean; // bool: true if merge completed without conflicts
$result->commitId; // ?ObjectId: the merge commit (null if conflicts)
$result->conflictPaths; // array: list of file paths with conflicts
$result->mergedContents; // array: path => content with conflict markers (if conflicts)
Keď sa zlúčenie zastaví pre konflikty, stav konfliktov sa zapíše do indexu a pracovného stromu. Vyriešte konflikty v súboroch, pridajte add() do indexu a zavolajte $repo->mergeContinue() alebo zavolajte $repo->mergeAbort(), aby ste obnovili stav pred zlúčením.
Pred zlúčením potvrďte alebo odložte miestne zmeny: zlúčenie znovu zostaví pracovný strom z výsledku a prepíše nepotvrdené zmeny v sledovaných súboroch (#2).
Zlúčenia rýchlym posunom
Keď je aktuálny HEAD predkom cieľovej vetvy, Pitmaster vykoná zlúčenie rýchlym posunom: jednoducho presunie HEAD na cieľový commit bez vytvorenia commitu zlúčenia.
Before: After fast-forward:
A---B (main) A---B---C---D (main)
\
C---D (feature)
$result = $repo->merge('feature');
// $result->clean === true
// $result->commitId points to D
// No merge commit created
Opačný prípad sa zatiaľ nerozpoznáva: zlúčením vetvy, ktorú HEAD už obsahuje, sa vytvorí nový commit zlúčenia s nezmeneným stromom namiesto oznámenia „Už aktuálne“ (#6).
Trojstranné zlúčenie
Keď sa vetvy rozídu, Pitmaster vykoná trojcestné zlúčenie:
- Nájdite základňu zlúčenia (najnižšieho spoločného predka)
- Vypočítajte rozdiely medzi základňou a našou vetvou a medzi základňou a ich vetvou na úrovni stromov
- Pri každom súbore zmenenom v oboch vetvách vykonajte zlúčenie na úrovni obsahu
- Vytvorte commit zlúčenia s dvoma rodičmi
Before: After merge:
C---D (feature) C---D
/ / \
A---B---E---F (main) A---B---E---F---G (main)
Základňa zlúčenia
Základňa zlúčenia je najnovší spoločný predok dvoch commitov. Pitmaster ju nájde prechádzaním grafu commitov. Ak existuje viacero základní zlúčenia (história s kríženiami), merge() ich najprv zlúči a výsledok použije ako základňu.
$base = $repo->mergeBase(
$repo->resolve('main'),
$repo->resolve('feature/login'),
);
if ($base !== null) {
echo "Common ancestor: {$base->hex}\n";
}
Priamy prístup k algoritmu MergeBase:
use Pitmaster\Merge\MergeBase;
$finder = new MergeBase($repo->objectDatabase());
$base = $finder->find($commitA, $commitB);
// All merge bases (criss-cross histories can have more than one)
$bases = $finder->findAll($commitA, $commitB);
// Check ancestry
$isAncestor = $finder->isAncestor($oldCommit, $newCommit);
Kontrola zlúčenia vetvy
if ($repo->isBranchMerged('feature/done')) {
// All commits in feature/done are reachable from default branch
$repo->deleteBranch('feature/done');
}
// Check against a specific target
if ($repo->isBranchMerged('feature/done', 'develop')) {
// Merged into develop
}
Značky konfliktov
Keď je súbor upravený v oboch vetvách a zmeny sa prekrývajú, Pitmaster vytvorí štandardné značky konfliktov. merge() označí strany ako HEAD a názvom zlúčenej vetvy:
<<<<<<< HEAD
return $this->value + 1;
=======
return $this->value * 2;
>>>>>>> feature
Trieda ConflictMarker oba varianty uzavrie do značiek:
use Pitmaster\Merge\ConflictMarker;
$marked = ConflictMarker::mark($oursContent, $theirsContent, 'HEAD', 'feature');
// diff3 style, with the base section
$marked = ConflictMarker::mark($oursContent, $theirsContent, 'HEAD', 'feature', $baseContent, 'base');
Trojcestné zlúčenie na úrovni obsahu
Trieda ThreeWayMerge zlúči obsah objektov blob (nie súbory):
use Pitmaster\Merge\ThreeWayMerge;
$result = ThreeWayMerge::merge($baseContent, $oursContent, $theirsContent, 'HEAD', 'feature');
$result['content']; // merged text, with conflict markers if needed
$result['clean']; // true when nothing conflicted
$result['conflicts']; // number of conflict regions
Prijíma tri reťazce (základnú verziu, našu verziu a ich verziu) spolu s voliteľnými označeniami a štýlom konfliktov (predvolene 'merge', prípadne 'diff3', ktorý zahŕňa aj časť so základnou verziou).
Stratégie zlúčenia
Rekurzívne zlúčenie
Predvolená stratégia ($repo->merge($branch)) spracúva histórie zlúčení s kríženiami a viacerými základňami. Trieda CLI pre ňu prijíma aj -s ort a -s recursive.
Zlúčenie našej vetvy
Vytvorí commit zlúčenia, ktorý ponechá strom HEAD nezmenený. Je užitočný na udržiavanie histórie vetvy bez zahrnutia zmien.
$result = $repo->merge('feature/old', 'ours');
Zlúčenie Octopus
Zlúči dve alebo viac vetiev do jedného commitu zlúčenia. Ak v niektorej vetve dôjde ku konfliktu, vráti nečistý výsledok bez zápisu stavu zlúčenia alebo vytvorenia commitu.
$result = $repo->mergeOctopus(['feature/a', 'feature/b', 'feature/c']);
Triedy RecursiveMerge, OursMerge a OctopusMerge v Pitmaster\Merge sú pomocné nástroje na zlúčenie obsahu; Repository implementuje uvedené stratégie samostatne.
Rerere (opätovné použitie zaznamenaného riešenia)
Pitmaster obsahuje samostatnú triedu Rerere, ktorá ukladá riešenia v rozložení rr-cache v git:
use Pitmaster\Merge\Rerere;
$rerere = new Rerere($repo->gitDir());
if ($rerere->isEnabled()) { // rerere.enabled = true in .git/config
$rerere->record($conflicted, $resolved); // remember a resolution
$resolved = $rerere->resolve($conflicted); // replay it, or null
}
merge() ju nevolá: riešenia sa zaznamenávajú a opakovane používajú iba vtedy, keď to urobí váš kód. isEnabled() hľadá doslovný text rerere.enabled = true v .git/config a nerozpoznáva sekciu [rerere], ktorú zapisuje git config rerere.enabled true (#20).
Priebeh zlúčenia na úrovni stromov
Celý proces zlúčenia:
MergeBase::findAll()nájde spoločných predkov- Základný strom, náš strom a ich strom sa porovnávajú cesta po ceste
- Pri súboroch zmenených iba v jednej vetve: použije sa táto zmena
- Pri súboroch zmenených v oboch vetvách:
- Ak sa zmenili rovnako: použije sa ktorákoľvek verzia (sú zhodné)
- Ak sa zmeny líšia: spustí sa
ThreeWayMerge::merge()nad obsahom
- Ak sú všetky zlúčenia čisté: zostaví sa zlúčený strom a vytvorí sa commit zlúčenia
- Ak sa vyskytnú konflikty: vráti sa
MergeResultsclean = falsea cestami ku konfliktom
Používanie CLI
./vendor/bin/pitmaster merge feature/login
Výstup pri úspechu:
Merge successful.
Výstup pri konflikte:
CONFLICT in: src/File.php, src/Other.php