Сливане
Pitmaster слива клонове чрез трипосочно сливане, както git merge. Открива възможности за fast-forward, изчислява основи за сливане, извършва сливания на ниво дърво и съдържание и генерира стандартни маркери за конфликт при конфликти във файловете.
Основно сливане
$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
Класът MergeResult е обект със стойност само за четене:
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)
Когато сливането спре заради конфликти, състоянието на конфликтите се записва в индекса и работното дърво. Разрешете конфликтите във файловете, добавете ги с add() и извикайте $repo->mergeContinue() или извикайте $repo->mergeAbort(), за да възстановите състоянието отпреди сливането.
Преди сливане запишете или скрийте локалните промени: сливането изгражда работното дърво от резултата и презаписва незаписаните промени в проследяваните файлове (#2).
Сливания с fast-forward
Когато текущият HEAD е предшественик на целевия клон, Pitmaster извършва fast-forward сливане: просто премества HEAD към целия commit, без да създава 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
Обратният случай все още не се открива: сливането на клон, който вече е включен в HEAD, създава нов commit за сливане с непроменено дърво, вместо да изведе „Вече е актуално“ (#6).
Тристранно сливане
Когато клоновете са се разделили, Pitmaster извършва трипосочно сливане:
- Намира основата за сливане (най-ниския общ предшественик)
- Сравнява основата с нашия и с техния клон на ниво дърво
- За всеки файл, променен и в двата клона, извършва сливане на ниво съдържание
- Създава commit за сливане с два родителя
Before: After merge:
C---D (feature) C---D
/ / \
A---B---E---F (main) A---B---E---F---G (main)
Основа за сливане
Основата за сливане е най-скорошният общ предшественик на два commit-а. Pitmaster я намира, като обхожда графа на commit-ите. Когато има няколко основи за сливане (история с кръстосани разклонения), merge() първо ги слива и използва резултата като основа.
$base = $repo->mergeBase(
$repo->resolve('main'),
$repo->resolve('feature/login'),
);
if ($base !== null) {
echo "Common ancestor: {$base->hex}\n";
}
За директен достъп до алгоритъма 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);
Проверка за сливане на клонове
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
}
Маркери за конфликти
Когато даден файл е променен и в двата клона и промените се припокриват, Pitmaster генерира стандартни маркери за конфликт. merge() обозначава страните като HEAD и с името на слетия клон:
<<<<<<< HEAD
return $this->value + 1;
=======
return $this->value * 2;
>>>>>>> feature
Класът ConflictMarker обгръща две версии с маркери:
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');
Трипосочно сливане на ниво съдържание
Класът ThreeWayMerge слива съдържанието на blob-ове (не файлове):
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
Приема три низа (базовата версия, нашата версия и тяхната версия), както и незадължителни етикети и стил на конфликтите (по подразбиране 'merge' или 'diff3' за включване на базовия раздел).
Стратегии за сливане
Рекурсивно сливане
Стратегията по подразбиране ($repo->merge($branch)) обработва истории с кръстосани разклонения и няколко основи за сливане. CLI приема също -s ort и -s recursive за нея.
Сливане по стратегията Ours
Записва commit за сливане, който запазва дървото на HEAD без промени. Полезно е за поддържане на историята на клона, без да се включват промени.
$result = $repo->merge('feature/old', 'ours');
Octopus сливане
Слива два или повече клона в един commit за сливане. Ако възникне конфликт в някой клон, връща неуспешен резултат, без да записва състояние на сливане или да създава commit.
$result = $repo->mergeOctopus(['feature/a', 'feature/b', 'feature/c']);
Класовете RecursiveMerge, OursMerge и OctopusMerge в Pitmaster\Merge са помощни средства за сливане на ниво съдържание; Repository реализира сам горните стратегии.
Rerere (повторно използване на записано разрешение)
Pitmaster включва самостоятелен клас Rerere, който съхранява разрешенията в структурата на директориите на git rr-cache:
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() не го извиква: разрешенията се записват и прилагат отново само когато кодът ви го направи. isEnabled() търси буквалния текст rerere.enabled = true в .git/config и не разпознава раздела [rerere], който git config rerere.enabled true записва (#20).
Процес на сливане на ниво дърво
Пълният процес на сливане:
MergeBase::findAll()намира общите предшественици- Дърветата на основата, нашия клон и техния клон се сравняват път по път
- За файловете, променени само в единия клон: прилага се тази промяна
- За файловете, променени и в двата клона:
- Ако са променени по един и същ начин: приема се който и да е вариант (те съвпадат)
- Ако промените са различни: съдържанието се обработва чрез
ThreeWayMerge::merge()
- Ако всички сливания са чисти: изгражда се слятото дърво и се създава commit за сливане
- Ако има конфликти: връща
MergeResultсclean = falseи пътищата до файловете с конфликти
Употреба на CLI
./vendor/bin/pitmaster merge feature/login
Резултат при успешно изпълнение:
Merge successful.
Изход при конфликт:
CONFLICT in: src/File.php, src/Other.php