Merge
Pitmaster udfører branch-merges med en trevejs-merge, ligesom git merge. Den registrerer muligheder for fast-forward, beregner merge-baser, udfører merges på tree- og indholdsniveau og genererer standardkonfliktmarkører, når filer er i konflikt.
Grundlæggende merge
$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 er et skrivebeskyttet værdiobjekt:
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 merge-processen stopper på grund af konflikter, skrives konfliktstatussen til indekset og arbejdstræet. Løs konflikterne i filerne, add() dem, og kald $repo->mergeContinue(), eller kald $repo->mergeAbort() for at gendanne tilstanden fra før merge-processen.
Før du merger, skal du committe eller stash'e lokale ændringer: merge-processen genopbygger arbejdstræet ud fra resultatet og overskriver ikke-committede ændringer i sporede filer (#2).
Fast-forward-merges
Når den aktuelle HEAD er en forfader til target-branchen, udfører Pitmaster en fast-forward-merge: den flytter blot HEAD til target-committen uden at oprette en merge-commit.
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 omvendte tilfælde registreres endnu ikke: Hvis du merger en branch, som HEAD allerede indeholder, oprettes der en ny merge-commit med et uændret tree i stedet for at rapportere "Already up to date" (#6).
Trevejsfletning
Når brancher er divergeret, udfører Pitmaster en trevejs-merge:
- Find merge-basen (den laveste fælles forfader)
- Sammenlign base-to-ours og base-to-theirs på tree-niveau
- Udfør en merge på indholdsniveau for hver fil, der er ændret i begge brancher
- Opret en merge-commit med to forældre
Before: After merge:
C---D (feature) C---D
/ / \
A---B---E---F (main) A---B---E---F---G (main)
Merge-base
Merge-basen er den seneste fælles forfader til to commits. Pitmaster finder den ved at gennemgå commit-grafen. Hvis der er flere merge-baser (krydsende historik), merger merge() dem først og bruger resultatet som base.
$base = $repo->mergeBase(
$repo->resolve('main'),
$repo->resolve('feature/login'),
);
if ($base !== null) {
echo "Common ancestor: {$base->hex}\n";
}
Sådan får du direkte adgang til 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);
Kontrol af branch-merge
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 er ændret i begge brancher, og ændringerne overlapper, genererer Pitmaster standardkonfliktmarkører. merge() angiver siderne som HEAD og navnet på den mergede branch:
<<<<<<< HEAD
return $this->value + 1;
=======
return $this->value * 2;
>>>>>>> feature
Klassen ConflictMarker indrammer to 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');
Trevejs-merge på indholdsniveau
Klassen ThreeWayMerge merger blob-indhold (ikke 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 tager tre strenge (baseversionen, vores version og deres version) samt valgfrie etiketter og en konfliktstil ('merge' som standard eller 'diff3' for også at inkludere basissektionen).
Merge-strategier
Rekursiv merge
Standardstrategien ($repo->merge($branch)) håndterer krydsende merge-historik med flere merge-baser. CLI accepterer også -s ort og -s recursive til den.
Ours-merge
Registrerer en merge-commit, der bevarer HEAD's tree uændret. Nyttigt til at vedligeholde branchhistorik uden at indarbejde ændringer.
$result = $repo->merge('feature/old', 'ours');
Octopus-merge
Merger to eller flere brancher i én merge-commit. Hvis en branch er i konflikt, returneres et urent resultat uden at skrive mergestatus eller oprette en commit.
$result = $repo->mergeOctopus(['feature/a', 'feature/b', 'feature/c']);
Klasserne RecursiveMerge, OursMerge og OctopusMerge i Pitmaster\Merge er hjælpere til indholdsniveau; Repository implementerer selv strategierne ovenfor.
Rerere (genbrug af registreret konfliktløsning)
Pitmaster indeholder en selvstændig Rerere-klasse, der gemmer konfliktløsninger i git's 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() kalder den ikke: Konfliktløsninger registreres og afspilles kun, når din kode gør det. isEnabled() leder efter den bogstavelige tekst rerere.enabled = true i .git/config og genkender ikke sektionen [rerere], som git config rerere.enabled true skriver (#20).
Merge-forløb på tree-niveau
Hele merge-processen:
MergeBase::findAll()finder de fælles forfædre- Base-, ours- og theirs-trees sammenlignes sti for sti
- For filer, der kun er ændret i én branch: Brug den ændring
- For filer, der er ændret i begge brancher:
- Hvis begge er ændret på samme måde: Brug en af dem (de stemmer overens)
- Hvis ændringerne er forskellige: Kør
ThreeWayMerge::merge()på indholdet
- Hvis alle merges er uden konflikter: Byg det mergede tree, og opret en merge-commit
- Hvis der er konflikter: Returner
MergeResultmedclean = falseog konfliktstierne
Brug af CLI
./vendor/bin/pitmaster merge feature/login
Output ved succes:
Merge successful.
Output ved konflikt:
CONFLICT in: src/File.php, src/Other.php