Sammanfogning
Pitmaster sammanfogar grenar med en tredelad sammanfogning, liksom git merge. Den identifierar möjligheter till snabbspolning, beräknar sammanfogningsbaser, sammanfogar på träd- och innehållsnivå och skapar standardmässiga konfliktmarkörer när filer hamnar i konflikt.
Grundläggande sammanfogning
$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
Klassen MergeResult är ett skrivskyddat värdeobjekt:
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)
När sammanfogningen avbryts på grund av konflikter skrivs konfliktstatusen till indexet och arbetsträdet. Lös filerna, add() dem och anropa $repo->mergeContinue(), eller anropa $repo->mergeAbort() för att återställa tillståndet före sammanfogningen.
Innan du sammanfogar bör du checka in eller stash:a lokala ändringar: sammanfogningen bygger om arbetsträdet utifrån resultatet och skriver över oincheckade ändringar i spårade filer (#2).
Snabbspolade sammanfogningar
När det aktuella HEAD är en anfader till målgrenen utför Pitmaster en snabbspolad sammanfogning: den flyttar helt enkelt HEAD till målincheckningen utan att skapa en sammanfogningsincheckning.
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
Det omvända fallet identifieras ännu inte: när en gren sammanfogas som HEAD redan innehåller skapas en ny sammanfogningsincheckning med ett oförändrat träd i stället för att rapportera "Already up to date" (#6).
Trevägssammanfogning
När grenar har gått isär utför Pitmaster en tredelad sammanfogning:
- Hitta sammanfogningsbasen (den närmaste gemensamma anfadern)
- Jämför basen med vår och deras på trädnivå
- Utför en sammanfogning på innehållsnivå för varje fil som ändrats i båda grenarna
- Skapa en sammanfogningsincheckning med två föräldrar
Before: After merge:
C---D (feature) C---D
/ / \
A---B---E---F (main) A---B---E---F---G (main)
Sammanfogningsbas
Sammanfogningsbasen är den senaste gemensamma anfadern till två incheckningar. Pitmaster hittar den genom att gå igenom incheckningsgrafen. Om det finns flera sammanfogningsbaser (korsande historik) sammanfogar merge() dem först och använder resultatet som bas.
$base = $repo->mergeBase(
$repo->resolve('main'),
$repo->resolve('feature/login'),
);
if ($base !== null) {
echo "Common ancestor: {$base->hex}\n";
}
För direkt åtkomst till algoritmen 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);
Kontroll av grensammanfogning
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
}
Konfliktmarkörer
När en fil har ändrats i båda grenarna och ändringarna överlappar skapar Pitmaster standardmässiga konfliktmarkörer. merge() märker upp sidorna som HEAD och namnet på den sammanfogade grenen:
<<<<<<< HEAD
return $this->value + 1;
=======
return $this->value * 2;
>>>>>>> feature
Klassen ConflictMarker omsluter två versioner med markörer:
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');
Tredelad sammanfogning på innehållsnivå
Klassen ThreeWayMerge sammanfogar blob-innehåll (inte filer):
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
Den tar tre strängar (basversionen, vår version och deras version) plus valfria etiketter och en konfliktstil ('merge' som standard, eller 'diff3' för att ta med basavsnittet).
Sammanfogningsstrategier
Rekursiv sammanfogning
Standardstrategin ($repo->merge($branch)) hanterar korsande sammanfogningshistorik med flera sammanfogningsbaser. CLI accepterar också -s ort och -s recursive för den.
Ours-sammanfogning
Skapar en sammanfogningsincheckning som behåller HEAD:s träd oförändrat. Användbart för att bevara grenens historik utan att införliva ändringar.
$result = $repo->merge('feature/old', 'ours');
Octopus-sammanfogning
Sammanfogar två eller fler grenar till en enda sammanfogningsincheckning. Om någon gren hamnar i konflikt returneras ett orent resultat utan att sammanfogningsstatus skrivs eller en incheckning skapas.
$result = $repo->mergeOctopus(['feature/a', 'feature/b', 'feature/c']);
Klasserna RecursiveMerge, OursMerge och OctopusMerge i Pitmaster\Merge är hjälpfunktioner på innehållsnivå; Repository implementerar själv strategierna ovan.
Rerere (återanvändning av sparad konfliktlösning)
Pitmaster innehåller en fristående Rerere-klass som lagrar konfliktlösningar i gits rr-cache-layout:
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() anropar den inte: konfliktlösningar registreras och återanvänds bara när din kod gör det. isEnabled() letar efter den bokstavliga texten rerere.enabled = true i .git/config och känner inte igen avsnittet [rerere] som git config rerere.enabled true skriver (#20).
Sammanfogningsflöde på trädnivå
Hela sammanfogningsprocessen:
MergeBase::findAll()hittar de gemensamma anfäderna- Basens, vår och deras träd jämförs sökväg för sökväg
- För filer som bara ändrats i en gren: ta den ändringen
- För filer som ändrats i båda grenarna:
- Om båda ändrats på samma sätt: ta endera (de är överens)
- Om ändringarna skiljer sig: kör
ThreeWayMerge::merge()på innehållet
- Om alla sammanfogningar är konfliktfria: bygg det sammanfogade trädet och skapa en sammanfogningsincheckning
- Vid konflikter: returnera
MergeResultmedclean = falseoch konfliktsökvägarna
Användning av CLI
./vendor/bin/pitmaster merge feature/login
Utdata vid lyckat resultat:
Merge successful.
Utdata vid konflikt:
CONFLICT in: src/File.php, src/Other.php