Egyesítés
Az Pitmaster ágakat egyesít háromutas módszerrel, az git merge-hez hasonlóan. Észleli a gyors előretekerési lehetőségeket, kiszámítja az egyesítési alapokat, végrehajtja a fa- és tartalomszintű egyesítést, és szabványos konfliktusjelölőket hoz létre, ha a fájlok ütköznek.
Alapvető egyesítés
$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
Az MergeResult osztály csak olvasható értékobjektum:
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)
Ha az egyesítés konfliktusok miatt leáll, a konfliktus állapota az indexbe és a munkafába kerül. Oldja fel a konfliktusokat, add() őket, majd hívja meg az $repo->mergeContinue() metódust, vagy hívja meg az $repo->mergeAbort() metódust az egyesítés előtti állapot visszaállításához.
Egyesítés előtt véglegesítse vagy tegye félre a helyi módosításokat: az egyesítés az eredményből építi újra a munkafát, és felülírja a követett fájlok nem véglegesített módosításait (#2).
Gyors előretekeréses egyesítések
Ha az aktuális HEAD a célág őse, az Pitmaster gyors előretekeréses egyesítést végez: egyszerűen a cél commitra állítja a HEAD-et, és nem hoz létre egyesítési commitot.
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
A fordított esetet még nem észleli: ha olyan ágat egyesít, amely már szerepel a HEAD előzményei között, „Already up to date” jelentése helyett új, változatlan fát tartalmazó egyesítési commitot hoz létre (#6).
Háromutas egyesítés
Ha az ágak eltérő irányban fejlődtek, az Pitmaster háromutas egyesítést végez:
- Megkeresi az egyesítési alapot (a legközelebbi közös őst)
- Fa szinten összehasonlítja az alapot a saját és a másik ág állapotával
- Minden olyan fájlon, amely mindkét ágban módosult, tartalomszintű egyesítést végez
- Létrehoz egy két szülővel rendelkező egyesítési commitot
Before: After merge:
C---D (feature) C---D
/ / \
A---B---E---F (main) A---B---E---F---G (main)
Egyesítési alap
Az összeolvasztási alap két véglegesítés legutóbbi közös őse. A Pitmaster a véglegesítési gráf bejárásával találja meg. Ha több összeolvasztási alap is van (kereszteződő történetben), először a merge() egyesíti őket, majd az eredményt használja alapként.
$base = $repo->mergeBase(
$repo->resolve('main'),
$repo->resolve('feature/login'),
);
if ($base !== null) {
echo "Common ancestor: {$base->hex}\n";
}
Az MergeBase algoritmus közvetlen eléréséhez:
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);
Ágegyesítés ellenőrzése
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
}
Konfliktusjelölők
Ha egy fájl mindkét ágban módosult, és a módosítások átfedik egymást, az Pitmaster szabványos konfliktusjelölőket hoz létre. Az merge() az HEAD névvel, valamint az egyesített ág nevével címkézi az oldalakat:
<<<<<<< HEAD
return $this->value + 1;
=======
return $this->value * 2;
>>>>>>> feature
Az ConflictMarker osztály két verziót fog közre jelölőkkel:
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');
Tartalomszintű háromutas egyesítés
Az ThreeWayMerge osztály blobok tartalmát egyesíti (nem fájlokat):
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
Három sztringet kap (az alapverziót, a saját verziót és a másik verziót), opcionális címkéket, valamint egy konfliktusstílust (alapértelmezés szerint 'merge', vagy 'diff3' az alap szakasz hozzáadásához).
Egyesítési stratégiák
Rekurzív egyesítés
Az alapértelmezett stratégia ($repo->merge($branch)) több egyesítési alappal rendelkező, kereszteződő egyesítési előzményeket is kezel. Az CLI ehhez az -s ort és az -s recursive értéket is elfogadja.
Saját ágat megtartó egyesítés
Olyan egyesítési commitot rögzít, amely változatlanul hagyja a HEAD fáját. Hasznos az ág előzményeinek megőrzéséhez a módosítások beemelése nélkül.
$result = $repo->merge('feature/old', 'ours');
Polipágú egyesítés
Két vagy több ágat egyesít egyetlen egyesítési commitba. Ha bármelyik ágban konfliktus van, nem tiszta eredményt ad vissza, és nem ír egyesítési állapotot vagy hoz létre commitot.
$result = $repo->mergeOctopus(['feature/a', 'feature/b', 'feature/c']);
Az RecursiveMerge, OursMerge és OctopusMerge osztályok az Pitmaster\Merge tartalomszintű segédeszközei; az Repository valósítja meg a fenti stratégiákat.
Rerere (rögzített feloldás újrafelhasználása)
Az Pitmaster tartalmaz egy önálló Rerere osztályt, amely a feloldásokat a git rr-cache elrendezésében tárolja:
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
}
Az merge() nem hívja meg: a feloldások rögzítése és visszajátszása csak akkor történik meg, ha a kód ezt végzi. Az isEnabled() a szó szerinti rerere.enabled = true szöveget keresi az .git/config fájlban, és nem ismeri fel az git config rerere.enabled true által kiírt [rerere] szakaszt (#20).
Fa szintű egyesítési folyamat
A teljes egyesítési folyamat:
- Az
MergeBase::findAll()megkeresi a közös ősöket - Az alap, a saját és a másik ág fáit útvonalanként hasonlítja össze
- A csak az egyik ágban módosult fájloknál átveszi az adott módosítást
- A mindkét ágban módosult fájloknál:
- Ha mindkét ág ugyanúgy módosította: bármelyik változatot átveszi, mert megegyeznek
- Ha a módosítások eltérnek: az
ThreeWayMerge::merge()metódust futtatja a tartalmon
- Ha minden egyesítés konfliktusmentes: felépíti az egyesített fát, és létrehoz egy egyesítési commitot
- Ha van konfliktus:
clean = falseértékkel és az ütköző útvonalakkal rendelkezőMergeResultértéket adja vissza
CLI használata
./vendor/bin/pitmaster merge feature/login
Sikeres művelet kimenete:
Merge successful.
Kimenet konfliktus esetén:
CONFLICT in: src/File.php, src/Other.php