Slučování
Pitmaster slučuje větve pomocí třícestného sloučení, podobně jako git merge. Rozpozná možnosti rychlého posunu, vypočítá společné předky, provede sloučení na úrovni stromu i obsahu a při konfliktu souborů vytvoří standardní značky konfliktu.
Základní slučování
$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
Třída MergeResult je neměnný objekt hodnoty:
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)
Když se slučování zastaví kvůli konfliktům, stav konfliktu se zapíše do indexu a pracovního stromu. Vyřešte soubory, proveďte add() a zavolejte $repo->mergeContinue(), nebo zavolejte $repo->mergeAbort(), chcete-li obnovit stav před sloučením.
Před sloučením potvrďte nebo uložte místní změny do zásobníku: slučování znovu sestaví pracovní strom podle výsledku a přepíše nepotvrzené změny ve sledovaných souborech (#2).
Sloučení rychlým posunem
Pokud je aktuální HEAD předkem cílové větve, Pitmaster provede sloučení rychlým posunem: pouze přesune HEAD na cílové potvrzení, aniž by vytvořil potvrzení sloučení.
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ý případ zatím není rozpoznán: sloučení větve, kterou HEAD již obsahuje, vytvoří nové potvrzení sloučení s nezměněným stromem místo hlášení „Již aktuální“ (#6).
Třícestné sloučení
Pokud se větve rozešly, Pitmaster provede třícestné sloučení:
- Najde společného předka (nejbližšího společného předka)
- Porovná základní strom se stromem naší větve a stromem jejich větve
- U každého souboru změněného v obou větvích sloučí obsah
- Vytvoří potvrzení sloučení se dvěma rodiči
Before: After merge:
C---D (feature) C---D
/ / \
A---B---E---F (main) A---B---E---F---G (main)
Společný předek
Společný předek je nejnovější společný předek dvou potvrzení. Pitmaster ho najde průchodem grafem potvrzení. Pokud existuje několik společných předků (historie s křížením větví), merge() je nejprve sloučí a výsledek použije jako základ.
$base = $repo->mergeBase(
$repo->resolve('main'),
$repo->resolve('feature/login'),
);
if ($base !== null) {
echo "Common ancestor: {$base->hex}\n";
}
Přímý pří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 sloučení větví
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 konfliktu
Pokud je soubor upraven v obou větvích a změny se překrývají, Pitmaster vytvoří standardní značky konfliktu. merge() označí strany jako HEAD a uvede název sloučené větve:
<<<<<<< HEAD
return $this->value + 1;
=======
return $this->value * 2;
>>>>>>> feature
Třída ConflictMarker uzavírá dvě verze do značek:
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');
Třícestné slučování na úrovni obsahu
Třída ThreeWayMerge slučuje obsah blobů (nikoli soubory):
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
Přijímá tři řetězce (základní verzi, naši verzi a jejich verzi), volitelné popisky a styl konfliktů (ve výchozím nastavení 'merge', nebo 'diff3' pro zahrnutí základní části).
Strategie slučování
Rekurzivní slučování
Výchozí strategie ($repo->merge($branch)) zpracovává historii křížení větví s několika společnými předky. CLI pro ni přijímá také -s ort a -s recursive.
Slučování naší větve
Zaznamená potvrzení sloučení, které ponechá strom HEAD beze změny. Hodí se k zachování historie větví bez začlenění změn.
$result = $repo->merge('feature/old', 'ours');
Sloučení Octopus
Sloučí dvě nebo více větví do jediného potvrzení sloučení. Pokud dojde ke konfliktu v některé větvi, vrátí nečistý výsledek, aniž by zapsalo stav sloučení nebo vytvořilo potvrzení.
$result = $repo->mergeOctopus(['feature/a', 'feature/b', 'feature/c']);
Třídy RecursiveMerge, OursMerge a OctopusMerge v Pitmaster\Merge pomáhají se slučováním obsahu; Repository výše uvedené strategie implementuje samostatně.
Rerere (opakované použití zaznamenaného řešení)
Pitmaster obsahuje samostatnou třídu Rerere, která ukládá řešení v uspořádání rr-cache systému 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() ji nevolá: řešení se zaznamenávají a znovu používají pouze tehdy, když to udělá váš kód. isEnabled() hledá doslovný text rerere.enabled = true v .git/config a nerozpozná sekci [rerere], kterou zapisuje git config rerere.enabled true (#20).
Průběh slučování na úrovni stromu
Celý proces sloučení:
MergeBase::findAll()vyhledá společné předky- Stromy základu, naší větve a jejich větve se porovnají podle jednotlivých cest
- U souborů změněných pouze v jedné větvi se převezme tato změna
- U souborů změněných v obou větvích:
- Pokud se obě změnily stejně, převezme se kterákoli změna (shodují se)
- Pokud se změny liší, spustí se
ThreeWayMerge::merge()nad obsahem
- Pokud všechna sloučení proběhnou čistě, sestaví se sloučený strom a vytvoří potvrzení sloučení
- Pokud dojde ke konfliktům, vrátí se
MergeResultsclean = falsea cestami ke konfliktním souborům
Použití CLI
./vendor/bin/pitmaster merge feature/login
Výstup při úspěchu:
Merge successful.
Výstup při konfliktu:
CONFLICT in: src/File.php, src/Other.php