Gå til indhold

Stash

Git stash gemmer ændringer i arbejdsmappe og indeks, så du kan skifte til en anden opgave og gendanne dem senere. Pitmaster implementerer stash med refs/stash og reflog i gits lagerlayout. Der findes ingen stash-metode på Repository; brug klassen Stash nedenfor.

To kendte problemer: når der er mere end én post, lader drop() (og dermed pop()) posten blive stående på listen (#10), og apply() overskriver ikke-committede ændringer i filer, som stashen ikke berører (#11). Anvend stash-poster på et rent working tree.

Sådan fungerer stash

Hver stash-post er en commit med to forældre:

  • Forælder 0: HEAD-committen på stash-tidspunktet (grundtilstanden)
  • Forælder 1: en commit, der indfanger indeksets tilstand (staged ændringer)

Stash-commitens eget træ indfanger working tree-tilstanden (herunder ikke-staged ændringer).

Stash-"stakken" er refloggen for refs/stash. Den nyeste stash er selve refs/stash. Ældre poster er reflogposter: stash@{0}, stash@{1} osv.

Kode
refs/stash -> stash commit (worktree state)
                parent[0] -> HEAD at time of stash
                parent[1] -> index state commit
                               parent[0] -> HEAD at time of stash

Oprettelse af Stash-objektet

PHP
use Pitmaster\Stash\Stash;

$stash = new Stash(
    $repo->objectDatabase(),
    $repo->refDatabase(),
    $repo->gitDir(),
    $repo->workDir(),
);

Push (gem ændringer)

PHP
// public function push(string $message = '', bool $includeUntracked = false): ObjectId
$stashId = $stash->push('Work in progress on login');
echo "Saved: {$stashId->hex}\n";

// Also stash untracked files
$stashId = $stash->push('With new files', includeUntracked: true);

Push gør følgende:

  1. Læser den aktuelle HEAD-commit
  2. Bygger et træ ud fra det aktuelle indeks (staged-tilstand)
  3. Opretter en "index commit" med HEAD som parent
  4. Bygger et træ ud fra den aktuelle arbejdsmappe (inklusive unstaged-ændringer)
  5. Opretter stash-committet (worktree-træ, parents = HEAD + index commit)
  6. Opdaterer refs/stash, så den peger på stash-committet
  7. Tilføjer en reflogpost til stakken
  8. Nulstiller arbejdstræet og indekset til HEAD

Efter push matcher sporede filer HEAD. Usporede filer forbliver, medmindre includeUntracked er angivet; så fjernes de og gemmes i stash-committets eget træ (git gemmer dem i stedet i en tredje parent, så git ser dem som sporede ændringer). push() opretter en post, selv når intet er ændret, hvor git rapporterer "No local changes to save".

Med standardbesked

PHP
$stashId = $stash->push();
// Message: "WIP on <branch>: <short hash> <HEAD subject>"
// With a message: "On <branch>: <message>"

Med en detached HEAD er <branch> lig med HEAD.

Pop (anvend og fjern)

PHP
$stash->pop();

Anvender den øverste stash-post på arbejdsmappen og fjerner den fra stakken. Svarer til apply() efterfulgt af drop().

Pop en bestemt post

PHP
$stash->pop(1);  // Pop stash@{1}

Apply (gendan uden at fjerne)

PHP
$stash->apply();      // Apply stash@{0}
$stash->apply(2);     // Apply stash@{2}

Apply fletter stashen ind i den aktuelle HEAD-fil for fil (stash-base, aktuel HEAD, stash-træ). Stash-posten forbliver i stakken, og indekset berøres ikke. Konflikter efterlader markører mærket Updated upstream og Stashed changes i filerne og kaster Pitmaster\Exceptions\MergeConflictException; pop() beholder derefter posten. Apply kontrollerer ikke først for lokale ændringer (se bemærkningen øverst).

Vis poster

PHP
$entries = $stash->listEntries();

foreach ($entries as $entry) {
    echo "stash@{{$entry['index']}}: {$entry['message']}\n";
    // stash@{0}: On main: Work in progress on login
    // stash@{1}: WIP on main: f707601 Add login form
}

Hver post indeholder:

  • index: stash-positionen (0 = nyeste)
  • message: stash-beskeden
  • hash: stash-commit-hashen

Drop (fjern uden at anvende)

PHP
$stash->drop();       // Drop stash@{0}
$stash->drop(1);      // Drop stash@{1}

Hvis den eneste post fjernes, fjernes refs/stash og dens reflog. Hvis der er flere poster, flytter drop() i øjeblikket kun refs/stash og lader refloggen være uændret, så posten forbliver i listEntries() (#10).

Lagringsdetaljer

Stash-commitstruktur

Kode
commit <hash>
tree <worktree-tree-hash>
parent <HEAD-hash>
parent <index-commit-hash>
author <current identity>
committer <current identity>

<stash message>

Index-commitstruktur

Kode
commit <hash>
tree <index-tree-hash>
parent <HEAD-hash>
author <current identity>
committer <current identity>

index on <branch>: <short hash> <HEAD subject>

Identiteten hentes fra GIT_COMMITTER_NAME, derefter GIT_AUTHOR_NAME, derefter user.name i repository-konfigurationen og til sidst Pitmaster (samme rækkefølge gælder for e-mailen, der slutter med pitmaster@example.invalid). Datoen hentes fra GIT_COMMITTER_DATE, derefter GIT_AUTHOR_DATE og til sidst det aktuelle tidspunkt. I modsætning til commits læser stash ikke konstanterne PITMASTER_*.

Reflog-lagring

Stash-poster gemmes i .git/logs/refs/stash:

Kode
0000000000000000000000000000000000000000 abc123... Name <email> 1700000000 +0000	WIP on main: f707601 Add login form
abc123... def456... Name <email> 1700000001 +0000	On main: Work in progress on login

Refloggen læses i kronologisk rækkefølge. Stash-listen vender rækkefølgen om (nyeste først). Stash-refloggen skrives uanset core.logallrefupdates.

Brug af CLI

Shell
# Save current changes
./vendor/bin/pitmaster stash
./vendor/bin/pitmaster stash push
./vendor/bin/pitmaster stash push "Work in progress on login"

# Apply and remove top entry
./vendor/bin/pitmaster stash pop

# Apply without removing
./vendor/bin/pitmaster stash apply

# List all entries
./vendor/bin/pitmaster stash list

# Remove top entry without applying
./vendor/bin/pitmaster stash drop

Beskeden er argumentet efter push (eller save); -m understøttes ikke. pop, apply og drop fungerer altid på stash@{0}; CLI har ingen --include-untracked.

Begrænsninger

  • Kun stash af hele arbejdsbiblioteket (ingen --keep-index); usporede filer kun via push($message, includeUntracked: true)
  • Ingen tilstand for --patch (interaktivt valg af hunk)
  • Pop/apply gendanner arbejdstræets tilstand; indekset gendannes ikke. Staged-ændringer kommer tilbage som unstaged-ændringer, og nyligt staged filer kommer tilbage som usporede filer
  • stash@{n} accepteres ikke af Repository::resolve()
  • Stash i et tilknyttet worktree bruger forkert konfiguration og reflogplacering (#14)