Ha rebase után automatikusan a git push --force-with-lease után nyúlsz, valószínűleg úgy gondolsz rá, mint a force push biztonsági övére. A parancs tényleg megállít egy idegen commit felülírása előtt – egészen addig, amíg az editorod a háttérben nem futtat egy git fetch-et. Onnantól ugyanaz a parancs zöld utat adhat pontosan annak a felülírásnak, amitől védene.

Nem azt védi, amit gondolsz
A --force-with-lease dokumentációja szerint a rövid forma nem azt kérdezi a szervertől, hogy „változott-e a branch, mióta utoljára láttam?”. A Git nem tudja, mikor nézted meg vagy hagytad jóvá a távoli állapotot. Ehelyett azt ellenőrzi, hogy a szerveren lévő ref pillanatnyi értéke megegyezik-e a helyi remote-tracking ref – például az origin/feature – értékével.
Tegyük fel, hogy a távoli feature és a helyi origin/feature egyaránt az A commitra mutat. Egy kolléga feltolja a B commitot, de te még nem fetcheltél. A szerver B-t, a géped továbbra is A-t vár: a lease érvénytelen, a push stale info hibával megáll. Eddig pontosan azt kapjuk, amit vártunk.
A csavar az, hogy a git fetch frissíti a remote-tracking brancheket, miközben sem a working tree-t, sem a lokális feature branchet nem módosítja. Ha ezt egy IDE, Git GUI vagy időzített feladat végzi el, az origin/feature csendben B-re ugrik. A következő lease már B-t vár, a szerver szintén B-n áll, tehát a force push engedélyt kap – akkor is, ha B tartalmát soha nem nézted meg és nem építetted be.
Egy fetch, és eltűnik a riasztás
Ezt két klónnal és egy lokális bare remote-tal próbáltam ki. Az egyik klón feltolt egy új commitot, a másik pedig egy eltérő történetet próbált force pusholni:
# origin/feature még a korábban látott commitra mutat
git push --force-with-lease origin feature
# elutasítva: stale info
# ugyanaz, amit egy háttérben frissítő IDE is megtehet
git fetch origin
git push --force-with-lease origin feature
# sikeres forced update
Az első push 1-es kilépési kóddal leállt. A fetch után ugyanaz a push 0-val lefutott, és a kolléga commitja kikerült a távoli branch történetéből. Ez nem Git-bug. A rövid lease csak a remote-tracking refet tudja heurisztikaként használni arra, hogy melyik távoli állapotot láthattad.
A leginkább „na, ezt nem tudtam” rész a kézikönyvben egy mondattal lejjebb van: az explicit elvárt értéket nem megadó --force-with-lease formákat a Git dokumentációja jelenleg is kísérletinek nevezi. Ez elég erős jelzés ahhoz, hogy ne kezeljük a rövid kapcsolót feltétel nélküli adatvesztés-védelemként.
A kevésbé kényelmes, de valódi lease
A robusztus megoldás az, hogy a művelet elején elmented annak a commitnak az azonosítóját, amelyet ténylegesen ellenőriztél, majd ezt adod át elvárt értékként:
git fetch origin
expected=$(git rev-parse origin/feature)
# Ellenőrzés, majd például az utolsó commit átírása
git log --oneline HEAD..origin/feature
git commit --amend --no-edit
git push \
--force-with-lease=refs/heads/feature:$expected \
origin HEAD:feature
A shell-változó nem mozdul el egy későbbi háttér-fetch hatására. A push csak akkor írja át a feature branchet, ha az még mindig pontosan az elmentett commitra mutat. Ha közben bárki pusholt, a művelet leáll. Hosszabb munkánál ugyanezt egy ideiglenes lokális taggel is rögzítheted; a Git kézikönyve is ezt a mintát mutatja.
Van egy köztes opció is: a --force-if-includes a rövid lease mellé téve ellenőrzi, hogy a frissült remote-tracking csúcs bekerült-e a lokális branch refloggal leírt történetébe. A fenti háttér-fetches tesztben ez már visszautasította a push-t:
git push --force-with-lease --force-if-includes origin feature
Hasznos plusz korlát, de nem ugyanazt fejezi ki, mint egy konkrét, általad jóváhagyott commit azonosítója. Explicit <ref>:<expect> lease mellett ráadásul a kapcsoló nem csinál semmit.
Mit használnék productionben?
Saját, rövid életű feature branchen vállalható a history rewrite, de az explicit commitos lease-et használnám. Ha a csapatnak túl körülményes a hosszú parancs, a --force-if-includes jó második háló, nem tökéletes helyettesítő.
main, release branch vagy több ember által használt branch esetén nem kliensoldali fegyelemre építenék. A szerveren tiltanám a force push-t. A GitHub protected branch szabályai például alapértelmezésben blokkolják ezt, és státuszellenőrzéseket vagy review-t is megkövetelhetnek. A szerveroldali szabályt sem egy IDE, sem egy alias, sem egy rossz pillanatban lefutó fetch nem tudja véletlenül kijátszani.
A --force-with-lease továbbra is lényegesen jobb a vak --force-nál. Csak a neve sugall erősebb garanciát annál, amit a rövid forma ténylegesen ad. A lease akkor valódi lease, ha azt is megmondod, melyik commitra vetted fel.