Siirry sisältöön

Yhdistäminen

Pitmaster yhdistää haaroja kolmen suunnan yhdistämisellä, kuten git merge. Se tunnistaa pikakelausmahdollisuudet, laskee yhdistämiskohdat, yhdistää puita ja sisältöä sekä tuottaa tavanomaiset konfliktimerkit ristiriitatilanteissa.

Yhdistämisen perusteet

PHP
$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-luokka on vain luku -arvo-olio:

PHP
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)

Kun yhdistäminen keskeytyy ristiriitoihin, ristiriitatila kirjataan indeksiin ja työhakemistoon. Ratkaise tiedostot, lisää ne add()-komennolla ja kutsu $repo->mergeContinue()-komentoa tai palauta yhdistämistä edeltävä tila kutsumalla $repo->mergeAbort()-komentoa.

Tee paikallisista muutoksista toimitus tai tallenna ne pinoon ennen yhdistämistä: yhdistäminen rakentaa työhakemiston uudelleen tuloksen pohjalta ja korvaa seurattujen tiedostojen toimittamattomat muutokset (#2).

Pikakelauksella tehtävät yhdistämiset

Kun nykyinen HEAD on kohdehaaran esi-isä, Pitmaster tekee yhdistämisen pikakelauksella: se siirtää HEADin kohdehaaran toimitukseen luomatta yhdistämistoimitusta.

Koodi
Before:         After fast-forward:

A---B (main)    A---B---C---D (main)
     \
      C---D (feature)
PHP
$result = $repo->merge('feature');
// $result->clean === true
// $result->commitId points to D
// No merge commit created

Käänteistä tapausta ei vielä tunnisteta: jos yhdistetään haara, jonka HEAD jo sisältää, syntyy uusi yhdistämistoimitus, jonka puu ei muutu, sen sijaan että ilmoitettaisiin ”Already up to date” (#6).

Kolmen osapuolen yhdistäminen

Kun haarat ovat eriytyneet, Pitmaster tekee kolmen suunnan yhdistämisen:

  1. Etsi yhdistämiskohta (lähin yhteinen esi-isä)
  2. Laske puutasolla erot kantapuusta omiin ja muiden muutoksiin
  3. Yhdistä sisältö kunkin tiedoston osalta, jos sitä on muutettu molemmissa haaroissa
  4. Luo yhdistämistoimitus, jolla on kaksi vanhempaa
Koodi
Before:              After merge:

      C---D (feature)         C---D
     /                       /     \
A---B---E---F (main)   A---B---E---F---G (main)

Yhdistämiskohta

Yhdistämiskohta on kahden toimituksen viimeisin yhteinen esi-isä. Pitmaster etsii sen käymällä läpi toimitusgraafin. Jos yhdistämiskohtia on useita (ristikkäishistoriassa), merge() yhdistää ne ensin ja käyttää tulosta kantana.

PHP
$base = $repo->mergeBase(
    $repo->resolve('main'),
    $repo->resolve('feature/login'),
);

if ($base !== null) {
    echo "Common ancestor: {$base->hex}\n";
}

Yhdistämisalgoritmia MergeBase voi käyttää suoraan seuraavasti:

PHP
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);

Haarojen yhdistämisen tarkistus

PHP
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
}

Konfliktimerkit

Kun tiedostoa on muutettu molemmissa haaroissa ja muutokset ovat päällekkäisiä, Pitmaster tuottaa tavanomaiset konfliktimerkit. merge() merkitsee puolet tunnisteilla HEAD ja yhdistetyn haaran nimellä:

Koodi
<<<<<<< HEAD
    return $this->value + 1;
=======
    return $this->value * 2;
>>>>>>> feature

ConflictMarker-luokka ympäröi kaksi versiota merkeillä:

PHP
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');

Sisällön kolmen suunnan yhdistäminen

ThreeWayMerge-luokka yhdistää blob-sisältöä (ei tiedostoja):

PHP
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

Se ottaa kolme merkkijonoa (kantaversion, oman version ja muiden version) sekä valinnaiset tunnisteet ja konfliktityylin (oletuksena 'merge' tai 'diff3', joka sisällyttää kantaversion osion).

Yhdistämisstrategiat

Rekursiivinen yhdistäminen

Oletusstrategia ($repo->merge($branch)) käsittelee ristikkäisiä yhdistämishistorioita, joissa on useita yhdistämiskohtia. Myös CLI hyväksyy sille arvot -s ort ja -s recursive.

Ours-yhdistäminen

Luo yhdistämistoimituksen, jossa HEADin puu säilyy ennallaan. Hyödyllinen haarahistorian ylläpitoon ilman muutosten sisällyttämistä.

PHP
$result = $repo->merge('feature/old', 'ours');

Octopus-yhdistäminen

Yhdistää kaksi tai useampia haaroja yhteen yhdistämistoimitukseen. Jos haarassa on ristiriitoja, se palauttaa keskeneräisen tuloksen tallentamatta yhdistämistilaa tai luomatta toimitusta.

PHP
$result = $repo->mergeOctopus(['feature/a', 'feature/b', 'feature/c']);

Luokkien RecursiveMerge, OursMerge ja OctopusMerge toiminnot kohteessa Pitmaster\Merge auttavat sisällön yhdistämisessä; Repository toteuttaa yllä olevat strategiat itse.

Rerere (tallennetun ratkaisun uudelleenkäyttö)

Pitmaster sisältää itsenäisen Rerere-luokan, joka tallentaa ratkaisut gitin rr-cache-asetteluun:

PHP
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() ei kutsu sitä: ratkaisut tallennetaan ja toistetaan vain, jos koodisi tekee niin. isEnabled() etsii kirjaimellista tekstiä rerere.enabled = true kohteesta .git/config eikä tunnista osiota [rerere], jonka git config rerere.enabled true kirjoittaa (#20).

Puiden yhdistämisen vaiheet

Yhdistämisen kaikki vaiheet:

  1. MergeBase::findAll() etsii yhteiset esi-isät
  2. Kantapuun, oman puun ja muiden puun polkuja verrataan yksitellen
  3. Jos tiedostoa on muutettu vain yhdessä haarassa, käytetään tätä muutosta
  4. Jos tiedostoa on muutettu molemmissa haaroissa:
    • Jos muutokset ovat samanlaiset, käytetään kumpaa tahansa, sillä ne vastaavat toisiaan
    • Jos muutokset eroavat, suoritetaan sisältöön ThreeWayMerge::merge()
  5. Jos kaikki yhdistämiset onnistuvat, rakennetaan yhdistetty puu ja luodaan yhdistämistoimitus
  6. Jos ristiriitoja esiintyy, palautetaan MergeResult sekä clean = false ja ristiriitapolut

CLI-käyttö

Shell
./vendor/bin/pitmaster merge feature/login

Tuloste onnistuttaessa:

Koodi
Merge successful.

Tuloste ristiriitatilanteessa:

Koodi
CONFLICT in: src/File.php, src/Other.php