Μετάβαση στο περιεχόμενο

Συγχώνευση

Το Pitmaster συγχωνεύει κλάδους με συγχώνευση τριών κατευθύνσεων, όπως το git merge. Εντοπίζει περιπτώσεις fast-forward, υπολογίζει βάσεις συγχώνευσης, εκτελεί συγχωνεύσεις σε επίπεδο δέντρου και περιεχομένου και δημιουργεί τυπικούς δείκτες σύγκρουσης όταν υπάρχουν συγκρούσεις σε αρχεία.

Βασική συγχώνευση

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 είναι ένα αντικείμενο τιμής μόνο για ανάγνωση:

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)

Όταν η συγχώνευση σταματήσει λόγω συγκρούσεων, η κατάσταση σύγκρουσης εγγράφεται στο ευρετήριο και στο δέντρο εργασίας. Επιλύστε τις συγκρούσεις στα αρχεία, καταχωρίστε τα με το add() και καλέστε το $repo->mergeContinue() ή καλέστε το $repo->mergeAbort() για να επαναφέρετε την κατάσταση πριν από τη συγχώνευση.

Πριν από τη συγχώνευση, καταχωρίστε ή αποθηκεύστε προσωρινά τις τοπικές αλλαγές: η συγχώνευση αναδημιουργεί το δέντρο εργασίας από το αποτέλεσμα και αντικαθιστά τις μη καταχωρισμένες αλλαγές σε αρχεία υπό παρακολούθηση (#2).

Συγχωνεύσεις fast-forward

Όταν το τρέχον HEAD είναι πρόγονος του κλάδου-στόχου, το Pitmaster εκτελεί συγχώνευση fast-forward: απλώς μετακινεί το HEAD στην καταχώριση-στόχο χωρίς να δημιουργήσει καταχώριση συγχώνευσης.

Κώδικας
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

Η αντίστροφη περίπτωση δεν εντοπίζεται ακόμη: η συγχώνευση κλάδου που περιέχεται ήδη στο HEAD δημιουργεί νέα καταχώριση συγχώνευσης με αμετάβλητο δέντρο αντί να αναφέρει «Ήδη ενημερωμένο» (#6).

Συγχώνευση τριών κατευθύνσεων

Όταν οι κλάδοι έχουν αποκλίνει, το Pitmaster εκτελεί συγχώνευση τριών κατευθύνσεων:

  1. Εντοπισμός της βάσης συγχώνευσης (χαμηλότερος κοινός πρόγονος)
  2. Σύγκριση των δέντρων από τη βάση προς τα δικά μας και προς τα δικά τους σε επίπεδο δέντρου
  3. Για κάθε αρχείο που άλλαξε και στους δύο κλάδους, εκτέλεση συγχώνευσης σε επίπεδο περιεχομένου
  4. Δημιουργία καταχώρισης συγχώνευσης με δύο γονείς
Κώδικας
Before:              After merge:

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

Βάση συγχώνευσης

Η βάση συγχώνευσης είναι ο πιο πρόσφατος κοινός πρόγονος δύο καταχωρίσεων. Το Pitmaster τον εντοπίζει διασχίζοντας το γράφημα καταχωρίσεων. Όταν υπάρχουν πολλές βάσεις συγχώνευσης (ιστορικό criss-cross), το merge() τις συγχωνεύει πρώτα και χρησιμοποιεί το αποτέλεσμα ως βάση.

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

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

Για απευθείας πρόσβαση στον αλγόριθμο MergeBase:

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

Έλεγχος συγχώνευσης κλάδων

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
}

Δείκτες σύγκρουσης

Όταν ένα αρχείο έχει τροποποιηθεί και στους δύο κλάδους και οι αλλαγές αλληλεπικαλύπτονται, το Pitmaster δημιουργεί τυπικούς δείκτες σύγκρουσης. Το merge() επισημαίνει τις πλευρές ως HEAD και με το όνομα του κλάδου που συγχωνεύεται:

Κώδικας
<<<<<<< HEAD
    return $this->value + 1;
=======
    return $this->value * 2;
>>>>>>> feature

Η κλάση ConflictMarker περικλείει δύο εκδόσεις με δείκτες:

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

Συγχώνευση τριών κατευθύνσεων σε επίπεδο περιεχομένου

Η κλάση ThreeWayMerge συγχωνεύει το περιεχόμενο blob (όχι αρχεία):

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

Δέχεται τρεις συμβολοσειρές (την έκδοση βάσης, τη δική μας έκδοση και τη δική τους έκδοση), προαιρετικές ετικέτες και ένα στυλ σύγκρουσης ('merge' από προεπιλογή ή 'diff3' για να συμπεριλάβει την ενότητα βάσης).

Στρατηγικές συγχώνευσης

Αναδρομική συγχώνευση

Η προεπιλεγμένη στρατηγική ($repo->merge($branch)) χειρίζεται ιστορικά criss-cross συγχωνεύσεων με πολλές βάσεις συγχώνευσης. Το CLI δέχεται επίσης τα -s ort και -s recursive για αυτήν.

Συγχώνευση Ours

Δημιουργεί καταχώριση συγχώνευσης που διατηρεί αμετάβλητο το δέντρο του HEAD. Χρήσιμο για τη διατήρηση του ιστορικού κλάδων χωρίς ενσωμάτωση αλλαγών.

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

Συγχώνευση Octopus

Συγχωνεύει δύο ή περισσότερους κλάδους σε μία καταχώριση συγχώνευσης. Αν υπάρχει σύγκρουση σε οποιονδήποτε κλάδο, επιστρέφει μη καθαρό αποτέλεσμα χωρίς να εγγράψει κατάσταση συγχώνευσης ή να δημιουργήσει καταχώριση.

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

Οι κλάσεις RecursiveMerge, OursMerge και OctopusMerge στο Pitmaster\Merge είναι βοηθητικά εργαλεία σε επίπεδο περιεχομένου· το Repository υλοποιεί τις παραπάνω στρατηγικές από μόνο του.

Rerere (επαναχρησιμοποίηση καταγεγραμμένης επίλυσης)

Το Pitmaster περιλαμβάνει μια αυτόνομη κλάση Rerere που αποθηκεύει επιλύσεις στη διάταξη rr-cache του git:

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() δεν το καλεί: οι επιλύσεις καταγράφονται και επαναλαμβάνονται μόνο όταν το κάνει ο κώδικάς σας. Το isEnabled() αναζητά το κυριολεκτικό κείμενο rerere.enabled = true στο .git/config και δεν αναγνωρίζει την ενότητα [rerere] που γράφει το git config rerere.enabled true (#20).

Ροή συγχώνευσης σε επίπεδο δέντρου

Η πλήρης διαδικασία συγχώνευσης:

  1. Το MergeBase::findAll() εντοπίζει τους κοινούς προγόνους
  2. Συγκρίνονται τα δέντρα βάσης, τα δικά μας και τα δικά τους, διαδρομή προς διαδρομή
  3. Για αρχεία που άλλαξαν μόνο σε έναν κλάδο: εφαρμόζεται αυτή η αλλαγή
  4. Για αρχεία που άλλαξαν και στους δύο κλάδους:
    • Αν άλλαξαν με τον ίδιο τρόπο: εφαρμόζεται οποιαδήποτε αλλαγή (συμφωνούν)
    • Αν οι αλλαγές διαφέρουν: εκτελείται το ThreeWayMerge::merge() στο περιεχόμενο
  5. Αν όλες οι συγχωνεύσεις είναι καθαρές: δημιουργείται το συγχωνευμένο δέντρο και μια καταχώριση συγχώνευσης
  6. Αν υπάρχουν συγκρούσεις: επιστρέφεται MergeResult με clean = false και τις διαδρομές των αρχείων που συγκρούονται

Χρήση CLI

Κέλυφος
./vendor/bin/pitmaster merge feature/login

Έξοδος σε περίπτωση επιτυχίας:

Κώδικας
Merge successful.

Έξοδος σε περίπτωση σύγκρουσης:

Κώδικας
CONFLICT in: src/File.php, src/Other.php